Send Devin Desktop Windsurf version instead of CLI build id.

This commit is contained in:
Kashyab Ambarani
2026-08-16 21:05:28 -07:00
parent d36c0b3705
commit 4ae4c78d51
3 changed files with 35 additions and 7 deletions
+3 -1
View File
@@ -3,6 +3,7 @@ import type { Api, Model, OAuthCredentials, OAuthLoginCallbacks } from "@earendi
import { authStatus, loginWithCli, readCredentials } from "../src/credentials.js";
import { whichDevin, devinVersion } from "../src/cli.js";
import { FALLBACK_MODELS, loadCliCatalog, modelsFromCatalog } from "../src/models.js";
import { CLIENT_IDE, CLIENT_VERSION } from "../src/metadata.js";
import { streamDevin } from "../src/stream.js";
const PROVIDER_ID = "devin";
@@ -86,7 +87,8 @@ export default async function (pi: ExtensionAPI): Promise<void> {
ctx.ui.notify(
[
bin ? `CLI: ${bin}` : "CLI: not found",
version ? `Version: ${version}` : "Version: unknown",
version ? `CLI version: ${version}` : "CLI version: unknown",
`Client identity: ${CLIENT_IDE} ${CLIENT_VERSION}`,
status.loggedIn ? "Auth: signed in via Devin CLI" : "Auth: not signed in. Run /login devin or `devin auth login`",
].join("\n"),
status.loggedIn && bin ? "info" : "warning",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "pi-devin",
"version": "0.1.0",
"version": "0.1.1",
"description": "Pi provider for Devin Local models. Auth and catalog come from the local Devin CLI; Pi stays the harness.",
"type": "module",
"keywords": [
+31 -5
View File
@@ -1,3 +1,4 @@
import { existsSync, readFileSync } from "node:fs";
import {
encodeMessage,
encodeString,
@@ -5,7 +6,30 @@ import {
encodeVarintField,
} from "./wire.js";
const DEFAULT_VERSION = "3000.4.25";
/**
* Cognition version-gates GetChatMessage against a known Windsurf/Devin Desktop
* release. The Devin CLI build id (`3000.4.25`) is not that string and the
* server answers: "Your Windsurf version is out of date."
* Prefer the installed Devin.app product.json, then a current desktop release.
*/
const FALLBACK_WINDSURF_VERSION = "3.6.27";
const PRODUCT_JSON =
"/Applications/Devin.app/Contents/Resources/app/product.json";
function desktopWindsurfVersion(): string {
try {
if (!existsSync(PRODUCT_JSON)) return FALLBACK_WINDSURF_VERSION;
const product = JSON.parse(readFileSync(PRODUCT_JSON, "utf8")) as {
windsurfVersion?: string;
};
return product.windsurfVersion || FALLBACK_WINDSURF_VERSION;
} catch {
return FALLBACK_WINDSURF_VERSION;
}
}
export const CLIENT_VERSION = desktopWindsurfVersion();
export const CLIENT_IDE = "windsurf";
export interface MetadataInput {
apiKey: string;
@@ -14,10 +38,12 @@ export interface MetadataInput {
requestId: bigint;
triggerId: string;
version?: string;
ide?: string;
}
export function buildMetadata(input: MetadataInput): Buffer {
const version = input.version ?? DEFAULT_VERSION;
const version = input.version ?? CLIENT_VERSION;
const ide = input.ide ?? CLIENT_IDE;
const os =
process.platform === "darwin"
? "darwin"
@@ -25,7 +51,7 @@ export function buildMetadata(input: MetadataInput): Buffer {
? "windows"
: "linux";
const parts: Buffer[] = [
encodeString(1, "devin"),
encodeString(1, ide),
encodeString(2, version),
encodeString(3, input.apiKey),
encodeString(4, "en"),
@@ -33,11 +59,11 @@ export function buildMetadata(input: MetadataInput): Buffer {
encodeString(7, version),
encodeVarintField(9, input.requestId),
encodeString(10, input.sessionId),
encodeString(12, "devin"),
encodeString(12, ide),
encodeMessage(16, encodeTimestampBody()),
encodeString(25, input.triggerId),
encodeString(26, "Unset"),
encodeString(28, "devin"),
encodeString(28, ide),
];
if (input.userJwt) parts.push(encodeString(21, input.userJwt));
return Buffer.concat(parts);