@ai-devkit/agent-manager 0.38.0 → 0.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/capacity/devin.test.js +239 -0
- package/dist/__tests__/capacity/devin.test.js.map +1 -0
- package/dist/__tests__/harnesses/devin/DevinAdapter.test.js +597 -0
- package/dist/__tests__/harnesses/devin/DevinAdapter.test.js.map +1 -0
- package/dist/__tests__/harnesses/devin/DevinAgentMapper.test.js +91 -0
- package/dist/__tests__/harnesses/devin/DevinAgentMapper.test.js.map +1 -0
- package/dist/__tests__/harnesses/devin/DevinSessionLocator.test.js +192 -0
- package/dist/__tests__/harnesses/devin/DevinSessionLocator.test.js.map +1 -0
- package/dist/__tests__/harnesses/devin/DevinSessionParser.test.js +407 -0
- package/dist/__tests__/harnesses/devin/DevinSessionParser.test.js.map +1 -0
- package/dist/__tests__/harnesses/devin/fixtures.js +95 -0
- package/dist/__tests__/harnesses/devin/fixtures.js.map +1 -0
- package/dist/__tests__/harnesses/readiness/AgentReadiness.test.js +64 -1
- package/dist/__tests__/harnesses/readiness/AgentReadiness.test.js.map +1 -1
- package/dist/__tests__/terminal/GhosttyBackend.test.js +334 -0
- package/dist/__tests__/terminal/GhosttyBackend.test.js.map +1 -0
- package/dist/adapters/AgentAdapter.d.ts +1 -1
- package/dist/adapters/AgentAdapter.d.ts.map +1 -1
- package/dist/adapters/AgentAdapter.js +2 -1
- package/dist/adapters/AgentAdapter.js.map +1 -1
- package/dist/capacity/index.d.ts +3 -0
- package/dist/capacity/index.d.ts.map +1 -1
- package/dist/capacity/index.js +8 -0
- package/dist/capacity/index.js.map +1 -1
- package/dist/capacity/providers/devin.d.ts +13 -0
- package/dist/capacity/providers/devin.d.ts.map +1 -0
- package/dist/capacity/providers/devin.js +92 -0
- package/dist/capacity/providers/devin.js.map +1 -0
- package/dist/capacity/sources/devin.d.ts +12 -0
- package/dist/capacity/sources/devin.d.ts.map +1 -0
- package/dist/capacity/sources/devin.js +29 -0
- package/dist/capacity/sources/devin.js.map +1 -0
- package/dist/harnesses/devin/DevinAdapter.d.ts +39 -0
- package/dist/harnesses/devin/DevinAdapter.d.ts.map +1 -0
- package/dist/harnesses/devin/DevinAdapter.js +177 -0
- package/dist/harnesses/devin/DevinAdapter.js.map +1 -0
- package/dist/harnesses/devin/DevinAgentMapper.d.ts +11 -0
- package/dist/harnesses/devin/DevinAgentMapper.d.ts.map +1 -0
- package/dist/harnesses/devin/DevinAgentMapper.js +35 -0
- package/dist/harnesses/devin/DevinAgentMapper.js.map +1 -0
- package/dist/harnesses/devin/DevinSessionLocator.d.ts +46 -0
- package/dist/harnesses/devin/DevinSessionLocator.d.ts.map +1 -0
- package/dist/harnesses/devin/DevinSessionLocator.js +176 -0
- package/dist/harnesses/devin/DevinSessionLocator.js.map +1 -0
- package/dist/harnesses/devin/DevinSessionParser.d.ts +29 -0
- package/dist/harnesses/devin/DevinSessionParser.d.ts.map +1 -0
- package/dist/harnesses/devin/DevinSessionParser.js +166 -0
- package/dist/harnesses/devin/DevinSessionParser.js.map +1 -0
- package/dist/harnesses/devin/credentials.d.ts +10 -0
- package/dist/harnesses/devin/credentials.d.ts.map +1 -0
- package/dist/harnesses/devin/credentials.js +66 -0
- package/dist/harnesses/devin/credentials.js.map +1 -0
- package/dist/harnesses/devin/readiness.d.ts +3 -0
- package/dist/harnesses/devin/readiness.d.ts.map +1 -0
- package/dist/harnesses/devin/readiness.js +39 -0
- package/dist/harnesses/devin/readiness.js.map +1 -0
- package/dist/harnesses/index.d.ts.map +1 -1
- package/dist/harnesses/index.js +3 -1
- package/dist/harnesses/index.js.map +1 -1
- package/dist/harnesses/readiness/AgentReadiness.d.ts.map +1 -1
- package/dist/harnesses/readiness/AgentReadiness.js +3 -1
- package/dist/harnesses/readiness/AgentReadiness.js.map +1 -1
- package/dist/harnesses/readiness/types.d.ts +1 -1
- package/dist/harnesses/readiness/types.d.ts.map +1 -1
- package/dist/harnesses/runtimeProfiles.d.ts.map +1 -1
- package/dist/harnesses/runtimeProfiles.js +2 -1
- package/dist/harnesses/runtimeProfiles.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/terminal/TtyWriter.d.ts.map +1 -1
- package/dist/terminal/TtyWriter.js +4 -3
- package/dist/terminal/TtyWriter.js.map +1 -1
- package/dist/terminal/backends/ghostty.d.ts +3 -0
- package/dist/terminal/backends/ghostty.d.ts.map +1 -0
- package/dist/terminal/backends/ghostty.js +161 -0
- package/dist/terminal/backends/ghostty.js.map +1 -0
- package/dist/terminal/backends/index.d.ts.map +1 -1
- package/dist/terminal/backends/index.js +2 -0
- package/dist/terminal/backends/index.js.map +1 -1
- package/dist/terminal/backends/shared.d.ts +2 -0
- package/dist/terminal/backends/shared.d.ts.map +1 -1
- package/dist/terminal/backends/shared.js +4 -1
- package/dist/terminal/backends/shared.js.map +1 -1
- package/dist/terminal/types.d.ts +1 -0
- package/dist/terminal/types.d.ts.map +1 -1
- package/dist/terminal/types.js +1 -0
- package/dist/terminal/types.js.map +1 -1
- package/dist/utils/process.d.ts +2 -0
- package/dist/utils/process.d.ts.map +1 -1
- package/dist/utils/process.js +3 -0
- package/dist/utils/process.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/capacity/devin.test.ts +210 -0
- package/src/__tests__/capacity/fixtures/devin-config.json +6 -0
- package/src/__tests__/capacity/fixtures/devin-credentials.toml +4 -0
- package/src/__tests__/capacity/fixtures/devin-quota-nonplan.json +5 -0
- package/src/__tests__/capacity/fixtures/devin-quota.json +10 -0
- package/src/__tests__/harnesses/devin/DevinAdapter.test.ts +292 -0
- package/src/__tests__/harnesses/devin/DevinAgentMapper.test.ts +88 -0
- package/src/__tests__/harnesses/devin/DevinSessionLocator.test.ts +139 -0
- package/src/__tests__/harnesses/devin/DevinSessionParser.test.ts +218 -0
- package/src/__tests__/harnesses/devin/fixtures.ts +144 -0
- package/src/__tests__/harnesses/readiness/AgentReadiness.test.ts +41 -0
- package/src/__tests__/terminal/GhosttyBackend.test.ts +271 -0
- package/src/adapters/AgentAdapter.ts +1 -0
- package/src/capacity/index.ts +12 -0
- package/src/capacity/providers/devin.ts +121 -0
- package/src/capacity/sources/devin.ts +49 -0
- package/src/harnesses/devin/DevinAdapter.ts +224 -0
- package/src/harnesses/devin/DevinAgentMapper.ts +43 -0
- package/src/harnesses/devin/DevinSessionLocator.ts +230 -0
- package/src/harnesses/devin/DevinSessionParser.ts +236 -0
- package/src/harnesses/devin/credentials.ts +88 -0
- package/src/harnesses/devin/readiness.ts +36 -0
- package/src/harnesses/index.ts +2 -0
- package/src/harnesses/readiness/AgentReadiness.ts +2 -0
- package/src/harnesses/runtimeProfiles.ts +1 -0
- package/src/index.ts +1 -0
- package/src/terminal/TtyWriter.ts +5 -5
- package/src/terminal/backends/ghostty.ts +180 -0
- package/src/terminal/backends/index.ts +2 -0
- package/src/terminal/backends/shared.ts +6 -1
- package/src/terminal/types.ts +1 -0
- package/src/utils/process.ts +5 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/capacity/index.ts"],"sourcesContent":["import { constants } from \"node:fs\";\nimport { access as fsAccess } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { codexUnavailableReport, probeCodexCapacity } from \"./sources/codex.js\";\nimport { probeClaudeCapacity, type ClaudeCapacityOptions } from \"./sources/claude.js\";\nimport {\n probeOpenAiCapacity,\n probePiAnthropicCapacity,\n probeZaiCapacity,\n type PiAnthropicCapacityOptions,\n type PiOpenAiCapacityOptions,\n type PiZaiCapacityOptions,\n} from \"./sources/pi.js\";\nimport type { CapacityReport } from \"./types.js\";\n\nexport type { CapacityReport, CapacityWindow } from \"./types.js\";\nexport type { ClaudeCapacityOptions } from \"./sources/claude.js\";\nexport type { PiAnthropicCapacityOptions as AnthropicCapacityOptions } from \"./sources/pi.js\";\nexport type {\n PiOpenAiCapacityOptions as OpenAiCapacityOptions,\n PiZaiCapacityOptions as ZaiCapacityOptions,\n} from \"./sources/pi.js\";\n\nexport type CapacityProbeOptions = {\n now?: () => Date;\n path?: string;\n access?: (target: string) => Promise<void>;\n probe?: typeof probeCodexCapacity;\n};\n\nasync function canAccess(target: string, mode: number): Promise<boolean> {\n try {\n await fsAccess(target, mode);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function isCodexInstalled(\n pathValue: string,\n checkAccess?: (target: string) => Promise<void>,\n): Promise<boolean> {\n const directories = pathValue.split(path.delimiter).filter(Boolean);\n for (const directory of directories) {\n const executable = path.join(directory, \"codex\");\n if (checkAccess) {\n try {\n await checkAccess(executable);\n return true;\n } catch {\n continue;\n }\n }\n if (await canAccess(executable, constants.X_OK)) return true;\n }\n return false;\n}\n\nexport async function getCodexCapacityReport(\n options: CapacityProbeOptions = {},\n): Promise<CapacityReport> {\n const generatedAt = (options.now?.() ?? new Date()).toISOString();\n const installed = await isCodexInstalled(options.path ?? process.env.PATH ?? \"\", options.access);\n try {\n return await (options.probe ?? probeCodexCapacity)({\n installed,\n checkedAt: generatedAt,\n });\n } catch {\n return codexUnavailableReport(generatedAt);\n }\n}\n\nexport async function getZaiCapacityReport(\n options: PiZaiCapacityOptions = {},\n): Promise<CapacityReport> {\n const { now, ...probeOptions } = options;\n return probeZaiCapacity({\n ...probeOptions,\n checkedAt: (now?.() ?? new Date()).toISOString(),\n });\n}\n\nexport async function getOpenAiCapacityReport(\n options: PiOpenAiCapacityOptions = {},\n): Promise<CapacityReport> {\n const { now, ...probeOptions } = options;\n return probeOpenAiCapacity({\n ...probeOptions,\n checkedAt: (now?.() ?? new Date()).toISOString(),\n });\n}\n\nexport async function getClaudeCapacityReport(\n options: ClaudeCapacityOptions = {},\n): Promise<CapacityReport> {\n const { now, ...probeOptions } = options;\n return probeClaudeCapacity({\n ...probeOptions,\n checkedAt: (now?.() ?? new Date()).toISOString(),\n now,\n });\n}\n\nexport async function getAnthropicCapacityReport(\n options: PiAnthropicCapacityOptions = {},\n): Promise<CapacityReport> {\n const { now, ...probeOptions } = options;\n return probePiAnthropicCapacity({\n ...probeOptions,\n checkedAt: (now?.() ?? new Date()).toISOString(),\n now,\n });\n}\n"],"names":["constants","access","fsAccess","path","codexUnavailableReport","probeCodexCapacity","probeClaudeCapacity","probeOpenAiCapacity","probePiAnthropicCapacity","probeZaiCapacity","canAccess","target","mode","isCodexInstalled","pathValue","checkAccess","directories","split","delimiter","filter","Boolean","directory","executable","join","X_OK","getCodexCapacityReport","options","generatedAt","now","Date","toISOString","installed","process","env","PATH","probe","checkedAt","getZaiCapacityReport","probeOptions","getOpenAiCapacityReport","getClaudeCapacityReport","getAnthropicCapacityReport"],"mappings":"AAAA,SAASA,SAAS,QAAQ,UAAU;AACpC,SAASC,UAAUC,QAAQ,QAAQ,mBAAmB;AACtD,OAAOC,UAAU,YAAY;AAC7B,SAASC,sBAAsB,EAAEC,kBAAkB,QAAQ,qBAAqB;AAChF,SAASC,mBAAmB,QAAoC,sBAAsB;AACtF,SACEC,mBAAmB,EACnBC,wBAAwB,EACxBC,gBAAgB,QAIX,kBAAkB;
|
|
1
|
+
{"version":3,"sources":["../../src/capacity/index.ts"],"sourcesContent":["import { constants } from \"node:fs\";\nimport { access as fsAccess } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { codexUnavailableReport, probeCodexCapacity } from \"./sources/codex.js\";\nimport { probeClaudeCapacity, type ClaudeCapacityOptions } from \"./sources/claude.js\";\nimport { probeDevinCapacity, type DevinCapacityOptions } from \"./sources/devin.js\";\nimport {\n probeOpenAiCapacity,\n probePiAnthropicCapacity,\n probeZaiCapacity,\n type PiAnthropicCapacityOptions,\n type PiOpenAiCapacityOptions,\n type PiZaiCapacityOptions,\n} from \"./sources/pi.js\";\nimport type { CapacityReport } from \"./types.js\";\n\nexport type { CapacityReport, CapacityWindow } from \"./types.js\";\nexport type { ClaudeCapacityOptions } from \"./sources/claude.js\";\nexport type { DevinCapacityOptions } from \"./sources/devin.js\";\nexport type { PiAnthropicCapacityOptions as AnthropicCapacityOptions } from \"./sources/pi.js\";\nexport type {\n PiOpenAiCapacityOptions as OpenAiCapacityOptions,\n PiZaiCapacityOptions as ZaiCapacityOptions,\n} from \"./sources/pi.js\";\n\nexport type CapacityProbeOptions = {\n now?: () => Date;\n path?: string;\n access?: (target: string) => Promise<void>;\n probe?: typeof probeCodexCapacity;\n};\n\nasync function canAccess(target: string, mode: number): Promise<boolean> {\n try {\n await fsAccess(target, mode);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function isCodexInstalled(\n pathValue: string,\n checkAccess?: (target: string) => Promise<void>,\n): Promise<boolean> {\n const directories = pathValue.split(path.delimiter).filter(Boolean);\n for (const directory of directories) {\n const executable = path.join(directory, \"codex\");\n if (checkAccess) {\n try {\n await checkAccess(executable);\n return true;\n } catch {\n continue;\n }\n }\n if (await canAccess(executable, constants.X_OK)) return true;\n }\n return false;\n}\n\nexport async function getCodexCapacityReport(\n options: CapacityProbeOptions = {},\n): Promise<CapacityReport> {\n const generatedAt = (options.now?.() ?? new Date()).toISOString();\n const installed = await isCodexInstalled(options.path ?? process.env.PATH ?? \"\", options.access);\n try {\n return await (options.probe ?? probeCodexCapacity)({\n installed,\n checkedAt: generatedAt,\n });\n } catch {\n return codexUnavailableReport(generatedAt);\n }\n}\n\nexport async function getZaiCapacityReport(\n options: PiZaiCapacityOptions = {},\n): Promise<CapacityReport> {\n const { now, ...probeOptions } = options;\n return probeZaiCapacity({\n ...probeOptions,\n checkedAt: (now?.() ?? new Date()).toISOString(),\n });\n}\n\nexport async function getOpenAiCapacityReport(\n options: PiOpenAiCapacityOptions = {},\n): Promise<CapacityReport> {\n const { now, ...probeOptions } = options;\n return probeOpenAiCapacity({\n ...probeOptions,\n checkedAt: (now?.() ?? new Date()).toISOString(),\n });\n}\n\nexport async function getClaudeCapacityReport(\n options: ClaudeCapacityOptions = {},\n): Promise<CapacityReport> {\n const { now, ...probeOptions } = options;\n return probeClaudeCapacity({\n ...probeOptions,\n checkedAt: (now?.() ?? new Date()).toISOString(),\n now,\n });\n}\n\nexport async function getDevinCapacityReport(\n options: DevinCapacityOptions = {},\n): Promise<CapacityReport> {\n const { now, ...probeOptions } = options;\n return probeDevinCapacity({\n ...probeOptions,\n checkedAt: (now?.() ?? new Date()).toISOString(),\n });\n}\n\nexport async function getAnthropicCapacityReport(\n options: PiAnthropicCapacityOptions = {},\n): Promise<CapacityReport> {\n const { now, ...probeOptions } = options;\n return probePiAnthropicCapacity({\n ...probeOptions,\n checkedAt: (now?.() ?? new Date()).toISOString(),\n now,\n });\n}\n"],"names":["constants","access","fsAccess","path","codexUnavailableReport","probeCodexCapacity","probeClaudeCapacity","probeDevinCapacity","probeOpenAiCapacity","probePiAnthropicCapacity","probeZaiCapacity","canAccess","target","mode","isCodexInstalled","pathValue","checkAccess","directories","split","delimiter","filter","Boolean","directory","executable","join","X_OK","getCodexCapacityReport","options","generatedAt","now","Date","toISOString","installed","process","env","PATH","probe","checkedAt","getZaiCapacityReport","probeOptions","getOpenAiCapacityReport","getClaudeCapacityReport","getDevinCapacityReport","getAnthropicCapacityReport"],"mappings":"AAAA,SAASA,SAAS,QAAQ,UAAU;AACpC,SAASC,UAAUC,QAAQ,QAAQ,mBAAmB;AACtD,OAAOC,UAAU,YAAY;AAC7B,SAASC,sBAAsB,EAAEC,kBAAkB,QAAQ,qBAAqB;AAChF,SAASC,mBAAmB,QAAoC,sBAAsB;AACtF,SAASC,kBAAkB,QAAmC,qBAAqB;AACnF,SACEC,mBAAmB,EACnBC,wBAAwB,EACxBC,gBAAgB,QAIX,kBAAkB;AAmBzB,eAAeC,UAAUC,MAAc,EAAEC,IAAY;IACnD,IAAI;QACF,MAAMX,SAASU,QAAQC;QACvB,OAAO;IACT,EAAE,OAAM;QACN,OAAO;IACT;AACF;AAEA,eAAeC,iBACbC,SAAiB,EACjBC,WAA+C;IAE/C,MAAMC,cAAcF,UAAUG,KAAK,CAACf,KAAKgB,SAAS,EAAEC,MAAM,CAACC;IAC3D,KAAK,MAAMC,aAAaL,YAAa;QACnC,MAAMM,aAAapB,KAAKqB,IAAI,CAACF,WAAW;QACxC,IAAIN,aAAa;YACf,IAAI;gBACF,MAAMA,YAAYO;gBAClB,OAAO;YACT,EAAE,OAAM;gBACN;YACF;QACF;QACA,IAAI,MAAMZ,UAAUY,YAAYvB,UAAUyB,IAAI,GAAG,OAAO;IAC1D;IACA,OAAO;AACT;AAEA,OAAO,eAAeC,uBACpBC,UAAgC,CAAC,CAAC;IAElC,MAAMC,cAAc,AAACD,CAAAA,QAAQE,GAAG,QAAQ,IAAIC,MAAK,EAAGC,WAAW;IAC/D,MAAMC,YAAY,MAAMlB,iBAAiBa,QAAQxB,IAAI,IAAI8B,QAAQC,GAAG,CAACC,IAAI,IAAI,IAAIR,QAAQ1B,MAAM;IAC/F,IAAI;QACF,OAAO,MAAM,AAAC0B,CAAAA,QAAQS,KAAK,IAAI/B,kBAAiB,EAAG;YACjD2B;YACAK,WAAWT;QACb;IACF,EAAE,OAAM;QACN,OAAOxB,uBAAuBwB;IAChC;AACF;AAEA,OAAO,eAAeU,qBACpBX,UAAgC,CAAC,CAAC;IAElC,MAAM,EAAEE,GAAG,EAAE,GAAGU,cAAc,GAAGZ;IACjC,OAAOjB,iBAAiB;QACtB,GAAG6B,YAAY;QACfF,WAAW,AAACR,CAAAA,WAAW,IAAIC,MAAK,EAAGC,WAAW;IAChD;AACF;AAEA,OAAO,eAAeS,wBACpBb,UAAmC,CAAC,CAAC;IAErC,MAAM,EAAEE,GAAG,EAAE,GAAGU,cAAc,GAAGZ;IACjC,OAAOnB,oBAAoB;QACzB,GAAG+B,YAAY;QACfF,WAAW,AAACR,CAAAA,WAAW,IAAIC,MAAK,EAAGC,WAAW;IAChD;AACF;AAEA,OAAO,eAAeU,wBACpBd,UAAiC,CAAC,CAAC;IAEnC,MAAM,EAAEE,GAAG,EAAE,GAAGU,cAAc,GAAGZ;IACjC,OAAOrB,oBAAoB;QACzB,GAAGiC,YAAY;QACfF,WAAW,AAACR,CAAAA,WAAW,IAAIC,MAAK,EAAGC,WAAW;QAC9CF;IACF;AACF;AAEA,OAAO,eAAea,uBACpBf,UAAgC,CAAC,CAAC;IAElC,MAAM,EAAEE,GAAG,EAAE,GAAGU,cAAc,GAAGZ;IACjC,OAAOpB,mBAAmB;QACxB,GAAGgC,YAAY;QACfF,WAAW,AAACR,CAAAA,WAAW,IAAIC,MAAK,EAAGC,WAAW;IAChD;AACF;AAEA,OAAO,eAAeY,2BACpBhB,UAAsC,CAAC,CAAC;IAExC,MAAM,EAAEE,GAAG,EAAE,GAAGU,cAAc,GAAGZ;IACjC,OAAOlB,yBAAyB;QAC9B,GAAG8B,YAAY;QACfF,WAAW,AAACR,CAAAA,WAAW,IAAIC,MAAK,EAAGC,WAAW;QAC9CF;IACF;AACF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ProviderCapacitySnapshot } from "../types.js";
|
|
2
|
+
import type { DevinCredential } from "../../harnesses/devin/credentials.js";
|
|
3
|
+
export type DevinRequestOptions = {
|
|
4
|
+
fetch?: typeof globalThis.fetch;
|
|
5
|
+
timeoutMs?: number;
|
|
6
|
+
};
|
|
7
|
+
export declare class DevinHttpError extends Error {
|
|
8
|
+
readonly status: number;
|
|
9
|
+
constructor(status: number, message: string);
|
|
10
|
+
}
|
|
11
|
+
export declare function parseDevinCapacity(raw: unknown): ProviderCapacitySnapshot;
|
|
12
|
+
export declare function fetchDevinCapacity(credential: DevinCredential, options?: DevinRequestOptions): Promise<ProviderCapacitySnapshot>;
|
|
13
|
+
//# sourceMappingURL=devin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devin.d.ts","sourceRoot":"","sources":["../../../src/capacity/providers/devin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgC,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAC1F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAQ5E,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,qBAAa,cAAe,SAAQ,KAAK;aAErB,MAAM,EAAE,MAAM;gBAAd,MAAM,EAAE,MAAM,EAC9B,OAAO,EAAE,MAAM;CAKlB;AA8BD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,wBAAwB,CAqCzE;AAED,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,eAAe,EAC3B,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,wBAAwB,CAAC,CA0BnC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
const DEVIN_WEBAPP_HOST = "https://app.devin.ai";
|
|
2
|
+
const DAY_MINUTES = 24 * 60;
|
|
3
|
+
const WEEK_MINUTES = 7 * 24 * 60;
|
|
4
|
+
export class DevinHttpError extends Error {
|
|
5
|
+
status;
|
|
6
|
+
constructor(status, message){
|
|
7
|
+
super(message), this.status = status;
|
|
8
|
+
this.name = "DevinHttpError";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function record(value) {
|
|
12
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
13
|
+
}
|
|
14
|
+
function percent(value) {
|
|
15
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return null;
|
|
16
|
+
return Math.max(0, Math.min(100, value));
|
|
17
|
+
}
|
|
18
|
+
function timestamp(value) {
|
|
19
|
+
if (typeof value !== "string" || !value) return null;
|
|
20
|
+
const ms = Date.parse(value);
|
|
21
|
+
return Number.isNaN(ms) ? null : new Date(ms).toISOString();
|
|
22
|
+
}
|
|
23
|
+
function finiteNumber(value) {
|
|
24
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
25
|
+
}
|
|
26
|
+
function availability(windows) {
|
|
27
|
+
if (windows.length === 0) return "unknown";
|
|
28
|
+
return windows.some((window)=>window.usedPercent !== null && window.usedPercent < 100) ? "yes" : "no";
|
|
29
|
+
}
|
|
30
|
+
export function parseDevinCapacity(raw) {
|
|
31
|
+
const root = record(raw);
|
|
32
|
+
if (!root) throw new Error("Invalid Devin quota response");
|
|
33
|
+
if (root.is_quota_plan !== true || root.has_quota_allocation !== true) {
|
|
34
|
+
return {
|
|
35
|
+
authenticated: true,
|
|
36
|
+
available: "unknown",
|
|
37
|
+
windows: [],
|
|
38
|
+
creditsRemaining: null
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const windows = [];
|
|
42
|
+
if (root.hide_daily_quota !== true) {
|
|
43
|
+
windows.push({
|
|
44
|
+
id: "devin:daily",
|
|
45
|
+
label: "Daily",
|
|
46
|
+
durationMinutes: DAY_MINUTES,
|
|
47
|
+
usedPercent: percent(root.daily_percentage),
|
|
48
|
+
resetsAt: timestamp(root.daily_reset_at)
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
windows.push({
|
|
52
|
+
id: "devin:weekly",
|
|
53
|
+
label: "Weekly",
|
|
54
|
+
durationMinutes: WEEK_MINUTES,
|
|
55
|
+
usedPercent: percent(root.weekly_percentage),
|
|
56
|
+
resetsAt: timestamp(root.weekly_reset_at)
|
|
57
|
+
});
|
|
58
|
+
return {
|
|
59
|
+
authenticated: true,
|
|
60
|
+
available: availability(windows),
|
|
61
|
+
windows,
|
|
62
|
+
creditsRemaining: finiteNumber(root.overage_balance)
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export async function fetchDevinCapacity(credential, options = {}) {
|
|
66
|
+
const url = `${DEVIN_WEBAPP_HOST}/api/${encodeURIComponent(credential.orgId)}/billing/quota/usage`;
|
|
67
|
+
let response;
|
|
68
|
+
try {
|
|
69
|
+
response = await (options.fetch ?? globalThis.fetch)(url, {
|
|
70
|
+
method: "GET",
|
|
71
|
+
headers: {
|
|
72
|
+
Authorization: `Bearer ${credential.key}`
|
|
73
|
+
},
|
|
74
|
+
signal: AbortSignal.timeout(options.timeoutMs ?? 5000)
|
|
75
|
+
});
|
|
76
|
+
} catch {
|
|
77
|
+
throw new Error("Devin quota request failed");
|
|
78
|
+
}
|
|
79
|
+
if (response.status !== 200) {
|
|
80
|
+
throw new DevinHttpError(response.status, `Devin quota request failed: HTTP ${response.status}`);
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
return parseDevinCapacity(await response.json());
|
|
84
|
+
} catch (error) {
|
|
85
|
+
if (error instanceof SyntaxError) {
|
|
86
|
+
throw new Error("Devin quota response is not valid JSON");
|
|
87
|
+
}
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
//# sourceMappingURL=devin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/capacity/providers/devin.ts"],"sourcesContent":["import type { Availability, CapacityWindow, ProviderCapacitySnapshot } from \"../types.js\";\nimport type { DevinCredential } from \"../../harnesses/devin/credentials.js\";\n\nconst DEVIN_WEBAPP_HOST = \"https://app.devin.ai\";\nconst DAY_MINUTES = 24 * 60;\nconst WEEK_MINUTES = 7 * 24 * 60;\n\ntype UnknownRecord = Record<string, unknown>;\n\nexport type DevinRequestOptions = {\n fetch?: typeof globalThis.fetch;\n timeoutMs?: number;\n};\n\nexport class DevinHttpError extends Error {\n constructor(\n public readonly status: number,\n message: string,\n ) {\n super(message);\n this.name = \"DevinHttpError\";\n }\n}\n\nfunction record(value: unknown): UnknownRecord | null {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? (value as UnknownRecord)\n : null;\n}\n\nfunction percent(value: unknown): number | null {\n if (typeof value !== \"number\" || !Number.isFinite(value)) return null;\n return Math.max(0, Math.min(100, value));\n}\n\nfunction timestamp(value: unknown): string | null {\n if (typeof value !== \"string\" || !value) return null;\n const ms = Date.parse(value);\n return Number.isNaN(ms) ? null : new Date(ms).toISOString();\n}\n\nfunction finiteNumber(value: unknown): number | null {\n return typeof value === \"number\" && Number.isFinite(value) ? value : null;\n}\n\nfunction availability(windows: CapacityWindow[]): Availability {\n if (windows.length === 0) return \"unknown\";\n return windows.some((window) => window.usedPercent !== null && window.usedPercent < 100)\n ? \"yes\"\n : \"no\";\n}\n\nexport function parseDevinCapacity(raw: unknown): ProviderCapacitySnapshot {\n const root = record(raw);\n if (!root) throw new Error(\"Invalid Devin quota response\");\n\n if (root.is_quota_plan !== true || root.has_quota_allocation !== true) {\n return {\n authenticated: true,\n available: \"unknown\",\n windows: [],\n creditsRemaining: null,\n };\n }\n\n const windows: CapacityWindow[] = [];\n if (root.hide_daily_quota !== true) {\n windows.push({\n id: \"devin:daily\",\n label: \"Daily\",\n durationMinutes: DAY_MINUTES,\n usedPercent: percent(root.daily_percentage),\n resetsAt: timestamp(root.daily_reset_at),\n });\n }\n windows.push({\n id: \"devin:weekly\",\n label: \"Weekly\",\n durationMinutes: WEEK_MINUTES,\n usedPercent: percent(root.weekly_percentage),\n resetsAt: timestamp(root.weekly_reset_at),\n });\n\n return {\n authenticated: true,\n available: availability(windows),\n windows,\n creditsRemaining: finiteNumber(root.overage_balance),\n };\n}\n\nexport async function fetchDevinCapacity(\n credential: DevinCredential,\n options: DevinRequestOptions = {},\n): Promise<ProviderCapacitySnapshot> {\n const url = `${DEVIN_WEBAPP_HOST}/api/${encodeURIComponent(credential.orgId)}/billing/quota/usage`;\n let response: Response;\n try {\n response = await (options.fetch ?? globalThis.fetch)(url, {\n method: \"GET\",\n headers: { Authorization: `Bearer ${credential.key}` },\n signal: AbortSignal.timeout(options.timeoutMs ?? 5000),\n });\n } catch {\n throw new Error(\"Devin quota request failed\");\n }\n if (response.status !== 200) {\n throw new DevinHttpError(\n response.status,\n `Devin quota request failed: HTTP ${response.status}`,\n );\n }\n try {\n return parseDevinCapacity(await response.json());\n } catch (error) {\n if (error instanceof SyntaxError) {\n throw new Error(\"Devin quota response is not valid JSON\");\n }\n throw error;\n }\n}\n"],"names":["DEVIN_WEBAPP_HOST","DAY_MINUTES","WEEK_MINUTES","DevinHttpError","Error","status","message","name","record","value","Array","isArray","percent","Number","isFinite","Math","max","min","timestamp","ms","Date","parse","isNaN","toISOString","finiteNumber","availability","windows","length","some","window","usedPercent","parseDevinCapacity","raw","root","is_quota_plan","has_quota_allocation","authenticated","available","creditsRemaining","hide_daily_quota","push","id","label","durationMinutes","daily_percentage","resetsAt","daily_reset_at","weekly_percentage","weekly_reset_at","overage_balance","fetchDevinCapacity","credential","options","url","encodeURIComponent","orgId","response","fetch","globalThis","method","headers","Authorization","key","signal","AbortSignal","timeout","timeoutMs","json","error","SyntaxError"],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CapacityReport } from "../types.js";
|
|
2
|
+
import { type DevinCredentialOptions } from "../../harnesses/devin/credentials.js";
|
|
3
|
+
export type DevinCapacityOptions = DevinCredentialOptions & {
|
|
4
|
+
now?: () => Date;
|
|
5
|
+
fetch?: typeof globalThis.fetch;
|
|
6
|
+
timeoutMs?: number;
|
|
7
|
+
};
|
|
8
|
+
export type DevinProbeOptions = DevinCapacityOptions & {
|
|
9
|
+
checkedAt: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function probeDevinCapacity(options: DevinProbeOptions): Promise<CapacityReport>;
|
|
12
|
+
//# sourceMappingURL=devin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devin.d.ts","sourceRoot":"","sources":["../../../src/capacity/sources/devin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAEL,KAAK,sBAAsB,EAC5B,MAAM,sCAAsC,CAAC;AAE9C,MAAM,MAAM,oBAAoB,GAAG,sBAAsB,GAAG;IAC1D,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,oBAAoB,GAAG;IACrD,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAcF,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC,CAmB5F"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { DevinHttpError, fetchDevinCapacity } from "../providers/devin.js";
|
|
2
|
+
import { resolveDevinCredential } from "../../harnesses/devin/credentials.js";
|
|
3
|
+
function devinReport(snapshot, checkedAt) {
|
|
4
|
+
return {
|
|
5
|
+
harness: "devin",
|
|
6
|
+
provider: "devin",
|
|
7
|
+
generatedAt: checkedAt,
|
|
8
|
+
...snapshot
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export async function probeDevinCapacity(options) {
|
|
12
|
+
const credential = await resolveDevinCredential(options);
|
|
13
|
+
try {
|
|
14
|
+
const snapshot = await fetchDevinCapacity(credential, options);
|
|
15
|
+
return devinReport(snapshot, options.checkedAt);
|
|
16
|
+
} catch (error) {
|
|
17
|
+
if (error instanceof DevinHttpError && (error.status === 401 || error.status === 403)) {
|
|
18
|
+
return devinReport({
|
|
19
|
+
authenticated: false,
|
|
20
|
+
available: "unknown",
|
|
21
|
+
windows: [],
|
|
22
|
+
creditsRemaining: null
|
|
23
|
+
}, options.checkedAt);
|
|
24
|
+
}
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
//# sourceMappingURL=devin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/capacity/sources/devin.ts"],"sourcesContent":["import { DevinHttpError, fetchDevinCapacity } from \"../providers/devin.js\";\nimport type { CapacityReport } from \"../types.js\";\nimport {\n resolveDevinCredential,\n type DevinCredentialOptions,\n} from \"../../harnesses/devin/credentials.js\";\n\nexport type DevinCapacityOptions = DevinCredentialOptions & {\n now?: () => Date;\n fetch?: typeof globalThis.fetch;\n timeoutMs?: number;\n};\n\nexport type DevinProbeOptions = DevinCapacityOptions & {\n checkedAt: string;\n};\n\nfunction devinReport(\n snapshot: Omit<CapacityReport, \"harness\" | \"provider\" | \"generatedAt\">,\n checkedAt: string,\n): CapacityReport {\n return {\n harness: \"devin\",\n provider: \"devin\",\n generatedAt: checkedAt,\n ...snapshot,\n };\n}\n\nexport async function probeDevinCapacity(options: DevinProbeOptions): Promise<CapacityReport> {\n const credential = await resolveDevinCredential(options);\n try {\n const snapshot = await fetchDevinCapacity(credential, options);\n return devinReport(snapshot, options.checkedAt);\n } catch (error) {\n if (error instanceof DevinHttpError && (error.status === 401 || error.status === 403)) {\n return devinReport(\n {\n authenticated: false,\n available: \"unknown\",\n windows: [],\n creditsRemaining: null,\n },\n options.checkedAt,\n );\n }\n throw error;\n }\n}\n"],"names":["DevinHttpError","fetchDevinCapacity","resolveDevinCredential","devinReport","snapshot","checkedAt","harness","provider","generatedAt","probeDevinCapacity","options","credential","error","status","authenticated","available","windows","creditsRemaining"],"mappings":"AAAA,SAASA,cAAc,EAAEC,kBAAkB,QAAQ,wBAAwB;AAE3E,SACEC,sBAAsB,QAEjB,uCAAuC;AAY9C,SAASC,YACPC,QAAsE,EACtEC,SAAiB;IAEjB,OAAO;QACLC,SAAS;QACTC,UAAU;QACVC,aAAaH;QACb,GAAGD,QAAQ;IACb;AACF;AAEA,OAAO,eAAeK,mBAAmBC,OAA0B;IACjE,MAAMC,aAAa,MAAMT,uBAAuBQ;IAChD,IAAI;QACF,MAAMN,WAAW,MAAMH,mBAAmBU,YAAYD;QACtD,OAAOP,YAAYC,UAAUM,QAAQL,SAAS;IAChD,EAAE,OAAOO,OAAO;QACd,IAAIA,iBAAiBZ,kBAAmBY,CAAAA,MAAMC,MAAM,KAAK,OAAOD,MAAMC,MAAM,KAAK,GAAE,GAAI;YACrF,OAAOV,YACL;gBACEW,eAAe;gBACfC,WAAW;gBACXC,SAAS,EAAE;gBACXC,kBAAkB;YACpB,GACAP,QAAQL,SAAS;QAErB;QACA,MAAMO;IACR;AACF"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Devin Adapter
|
|
3
|
+
*
|
|
4
|
+
* Detects running Devin CLI agents by:
|
|
5
|
+
* 1. Filtering `devin` processes from a shared process snapshot, rejecting
|
|
6
|
+
* utility subcommands (list, doctor, ssh, ...) and `devin acp` children
|
|
7
|
+
* spawned by a Devin TUI
|
|
8
|
+
* 2. Matching processes to sessions via `session_locks/<slug>.lock` files,
|
|
9
|
+
* which contain the PID of the session's `devin acp` backend (lock-first;
|
|
10
|
+
* a TUI is matched through its backend child's ppid)
|
|
11
|
+
* 3. Falling back to `sessions.working_directory` matching in
|
|
12
|
+
* ~/.local/share/devin/cli/sessions.db (WAL SQLite, opened read-only)
|
|
13
|
+
*
|
|
14
|
+
* sessionFilePath encodes "<dbPath>::<sessionSlug>" so getConversation() can
|
|
15
|
+
* address a session row without extending the AgentAdapter interface.
|
|
16
|
+
*/
|
|
17
|
+
import type { AgentAdapter, AgentInfo, ProcessInfo, ConversationMessage, ConversationOptions, SessionSummary, ListSessionsOptions, AgentDetectionContext } from "../../adapters/AgentAdapter.js";
|
|
18
|
+
export declare class DevinAdapter implements AgentAdapter {
|
|
19
|
+
readonly type: "devin";
|
|
20
|
+
readonly processNames: readonly ["devin"];
|
|
21
|
+
private readonly parser;
|
|
22
|
+
private readonly locator;
|
|
23
|
+
private readonly mapper;
|
|
24
|
+
private readonly cleanup;
|
|
25
|
+
constructor(dbPath?: string, locksDir?: string);
|
|
26
|
+
close(): void;
|
|
27
|
+
canHandle(processInfo: ProcessInfo): boolean;
|
|
28
|
+
detectAgents(context?: AgentDetectionContext): Promise<AgentInfo[]>;
|
|
29
|
+
getConversation(sessionFilePath: string, options?: ConversationOptions): ConversationMessage[];
|
|
30
|
+
listSessions(opts?: ListSessionsOptions): Promise<SessionSummary[]>;
|
|
31
|
+
findSessionsById(sessionId: string): Promise<SessionSummary[]>;
|
|
32
|
+
/**
|
|
33
|
+
* A `devin acp` process spawned by another devin process is that session's
|
|
34
|
+
* backend, not a separate agent. Standalone `acp` processes (external ACP
|
|
35
|
+
* clients) keep their own agent entry.
|
|
36
|
+
*/
|
|
37
|
+
private isAcpBackendChild;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=DevinAdapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DevinAdapter.d.ts","sourceRoot":"","sources":["../../../src/harnesses/devin/DevinAdapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,SAAS,EACT,WAAW,EACX,mBAAmB,EACnB,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,gCAAgC,CAAC;AAyFxC,qBAAa,YAAa,YAAW,YAAY;IAC/C,QAAQ,CAAC,IAAI,EAAG,OAAO,CAAU;IACjC,QAAQ,CAAC,YAAY,qBAAsB;IAE3C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmB;IAC1C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;gBAExC,MAAM,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;IAU9C,KAAK,IAAI,IAAI;IAOb,SAAS,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO;IAMtC,YAAY,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IA6CzE,eAAe,CAAC,eAAe,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,mBAAmB,EAAE;IAUxF,YAAY,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAInE,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAIpE;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;CAQ1B"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Devin Adapter
|
|
3
|
+
*
|
|
4
|
+
* Detects running Devin CLI agents by:
|
|
5
|
+
* 1. Filtering `devin` processes from a shared process snapshot, rejecting
|
|
6
|
+
* utility subcommands (list, doctor, ssh, ...) and `devin acp` children
|
|
7
|
+
* spawned by a Devin TUI
|
|
8
|
+
* 2. Matching processes to sessions via `session_locks/<slug>.lock` files,
|
|
9
|
+
* which contain the PID of the session's `devin acp` backend (lock-first;
|
|
10
|
+
* a TUI is matched through its backend child's ppid)
|
|
11
|
+
* 3. Falling back to `sessions.working_directory` matching in
|
|
12
|
+
* ~/.local/share/devin/cli/sessions.db (WAL SQLite, opened read-only)
|
|
13
|
+
*
|
|
14
|
+
* sessionFilePath encodes "<dbPath>::<sessionSlug>" so getConversation() can
|
|
15
|
+
* address a session row without extending the AgentAdapter interface.
|
|
16
|
+
*/ import { DevinAgentMapper } from "./DevinAgentMapper.js";
|
|
17
|
+
import { DevinSessionLocator, decodeDevinSessionRef } from "./DevinSessionLocator.js";
|
|
18
|
+
import { DevinSessionParser } from "./DevinSessionParser.js";
|
|
19
|
+
import { findHarnessProcesses, matchesExecutable } from "../shared.js";
|
|
20
|
+
import { executablePath } from "../../utils/process.js";
|
|
21
|
+
/**
|
|
22
|
+
* `devin` invocations that are utilities, not agent sessions. `acp` is
|
|
23
|
+
* deliberately absent: a standalone `devin acp` serves an external client
|
|
24
|
+
* and is itself an agent.
|
|
25
|
+
*/ const UTILITY_SUBCOMMANDS = new Set([
|
|
26
|
+
"auth",
|
|
27
|
+
"cloud",
|
|
28
|
+
"desktop",
|
|
29
|
+
"doctor",
|
|
30
|
+
"forward",
|
|
31
|
+
"help",
|
|
32
|
+
"list",
|
|
33
|
+
"ls",
|
|
34
|
+
"mcp",
|
|
35
|
+
"migrate",
|
|
36
|
+
"models",
|
|
37
|
+
"plugins",
|
|
38
|
+
"rm",
|
|
39
|
+
"rules",
|
|
40
|
+
"sandbox",
|
|
41
|
+
"setup",
|
|
42
|
+
"skills",
|
|
43
|
+
"ssh",
|
|
44
|
+
"uninstall",
|
|
45
|
+
"update",
|
|
46
|
+
"version"
|
|
47
|
+
]);
|
|
48
|
+
/**
|
|
49
|
+
* Flags that always consume the next argv token as their value.
|
|
50
|
+
* (`devin --help`, main-command options.)
|
|
51
|
+
*/ const REQUIRED_VALUE_FLAGS = new Set([
|
|
52
|
+
"--prompt-file",
|
|
53
|
+
"--config",
|
|
54
|
+
"--permission-mode",
|
|
55
|
+
"--model"
|
|
56
|
+
]);
|
|
57
|
+
/**
|
|
58
|
+
* Flags whose value is optional: the next token is consumed only when it
|
|
59
|
+
* isn't itself a flag (commander `[<VALUE>]` semantics).
|
|
60
|
+
*/ const OPTIONAL_VALUE_FLAGS = new Set([
|
|
61
|
+
"-p",
|
|
62
|
+
"--print",
|
|
63
|
+
"-r",
|
|
64
|
+
"--resume",
|
|
65
|
+
"--export",
|
|
66
|
+
"--respect-workspace-trust"
|
|
67
|
+
]);
|
|
68
|
+
/**
|
|
69
|
+
* First positional argv token after argv[0] — the subcommand candidate.
|
|
70
|
+
* Flag values are skipped (so `devin -p "update deps"` isn't mistaken for
|
|
71
|
+
* the `update` subcommand); `--` ends the search.
|
|
72
|
+
*/ function firstPositionalToken(command) {
|
|
73
|
+
const argv0 = executablePath(command);
|
|
74
|
+
const tokens = command.trim().slice(argv0.length).trim().split(/\s+/);
|
|
75
|
+
for(let i = 0; i < tokens.length; i++){
|
|
76
|
+
const token = tokens[i];
|
|
77
|
+
if (!token) continue;
|
|
78
|
+
if (token === "--") return null;
|
|
79
|
+
if (!token.startsWith("-")) return token;
|
|
80
|
+
const flag = token.split("=", 1)[0];
|
|
81
|
+
if (flag === token) {
|
|
82
|
+
if (REQUIRED_VALUE_FLAGS.has(flag)) {
|
|
83
|
+
i++;
|
|
84
|
+
} else if (OPTIONAL_VALUE_FLAGS.has(flag) && !tokens[i + 1]?.startsWith("-")) {
|
|
85
|
+
i++;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
export class DevinAdapter {
|
|
92
|
+
type = "devin";
|
|
93
|
+
processNames = [
|
|
94
|
+
"devin"
|
|
95
|
+
];
|
|
96
|
+
parser;
|
|
97
|
+
locator;
|
|
98
|
+
mapper;
|
|
99
|
+
cleanup = ()=>this.close();
|
|
100
|
+
constructor(dbPath, locksDir){
|
|
101
|
+
this.parser = new DevinSessionParser();
|
|
102
|
+
// Locator defaults resolve dbPath itself and derive locksDir from it.
|
|
103
|
+
this.locator = new DevinSessionLocator(dbPath, locksDir, this.parser);
|
|
104
|
+
this.mapper = new DevinAgentMapper(this.locator.dbFilePath);
|
|
105
|
+
process.once("exit", this.cleanup);
|
|
106
|
+
process.once("SIGINT", this.cleanup);
|
|
107
|
+
process.once("SIGTERM", this.cleanup);
|
|
108
|
+
}
|
|
109
|
+
close() {
|
|
110
|
+
process.off("exit", this.cleanup);
|
|
111
|
+
process.off("SIGINT", this.cleanup);
|
|
112
|
+
process.off("SIGTERM", this.cleanup);
|
|
113
|
+
this.locator.close();
|
|
114
|
+
}
|
|
115
|
+
canHandle(processInfo) {
|
|
116
|
+
if (!matchesExecutable(processInfo.command, "devin")) return false;
|
|
117
|
+
const subcommand = firstPositionalToken(processInfo.command);
|
|
118
|
+
return subcommand === null || !UTILITY_SUBCOMMANDS.has(subcommand.toLowerCase());
|
|
119
|
+
}
|
|
120
|
+
async detectAgents(context) {
|
|
121
|
+
const { processes } = await findHarnessProcesses(this, context);
|
|
122
|
+
if (processes.length === 0) return [];
|
|
123
|
+
const db = this.locator.openDb();
|
|
124
|
+
const processByPid = new Map(processes.map((proc)=>[
|
|
125
|
+
proc.pid,
|
|
126
|
+
proc
|
|
127
|
+
]));
|
|
128
|
+
// Locks map a session to its holder PID (the `devin acp` backend); a TUI
|
|
129
|
+
// is matched through its backend child's ppid. Only trust locks whose
|
|
130
|
+
// holder is a live devin process in this snapshot (stale files and PID
|
|
131
|
+
// reuse are filtered out).
|
|
132
|
+
const sessionByPid = new Map();
|
|
133
|
+
for (const lock of this.locator.listActiveLocks()){
|
|
134
|
+
const holder = processByPid.get(lock.pid);
|
|
135
|
+
if (!holder || !this.canHandle(holder)) continue;
|
|
136
|
+
sessionByPid.set(lock.pid, lock.sessionId);
|
|
137
|
+
if (holder.ppid !== undefined) sessionByPid.set(holder.ppid, lock.sessionId);
|
|
138
|
+
}
|
|
139
|
+
const agents = [];
|
|
140
|
+
for (const proc of processes){
|
|
141
|
+
if (this.isAcpBackendChild(proc, processByPid)) continue;
|
|
142
|
+
const slug = sessionByPid.get(proc.pid);
|
|
143
|
+
const session = !db ? null : slug ? this.locator.findSessionById(db, slug) : proc.cwd ? this.locator.findSessionForDirectory(db, proc.cwd) : null;
|
|
144
|
+
if (!session || !db) {
|
|
145
|
+
agents.push(this.mapper.mapProcessOnlyAgent(proc));
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
const stats = this.parser.getSessionStats(db, session.sessionId);
|
|
149
|
+
agents.push(this.mapper.mapSessionToAgent(session, stats, proc));
|
|
150
|
+
}
|
|
151
|
+
return agents;
|
|
152
|
+
}
|
|
153
|
+
getConversation(sessionFilePath, options) {
|
|
154
|
+
const sessionId = decodeDevinSessionRef(sessionFilePath);
|
|
155
|
+
if (!sessionId) return [];
|
|
156
|
+
const db = this.locator.openDb();
|
|
157
|
+
if (!db) return [];
|
|
158
|
+
return this.parser.getConversation(db, sessionId, options);
|
|
159
|
+
}
|
|
160
|
+
async listSessions(opts) {
|
|
161
|
+
return this.locator.listSessions(opts);
|
|
162
|
+
}
|
|
163
|
+
async findSessionsById(sessionId) {
|
|
164
|
+
return this.locator.findSessionsById(sessionId);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* A `devin acp` process spawned by another devin process is that session's
|
|
168
|
+
* backend, not a separate agent. Standalone `acp` processes (external ACP
|
|
169
|
+
* clients) keep their own agent entry.
|
|
170
|
+
*/ isAcpBackendChild(proc, processByPid) {
|
|
171
|
+
if (firstPositionalToken(proc.command)?.toLowerCase() !== "acp") return false;
|
|
172
|
+
if (proc.ppid === undefined) return false;
|
|
173
|
+
return processByPid.has(proc.ppid);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
//# sourceMappingURL=DevinAdapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/harnesses/devin/DevinAdapter.ts"],"sourcesContent":["/**\n * Devin Adapter\n *\n * Detects running Devin CLI agents by:\n * 1. Filtering `devin` processes from a shared process snapshot, rejecting\n * utility subcommands (list, doctor, ssh, ...) and `devin acp` children\n * spawned by a Devin TUI\n * 2. Matching processes to sessions via `session_locks/<slug>.lock` files,\n * which contain the PID of the session's `devin acp` backend (lock-first;\n * a TUI is matched through its backend child's ppid)\n * 3. Falling back to `sessions.working_directory` matching in\n * ~/.local/share/devin/cli/sessions.db (WAL SQLite, opened read-only)\n *\n * sessionFilePath encodes \"<dbPath>::<sessionSlug>\" so getConversation() can\n * address a session row without extending the AgentAdapter interface.\n */\n\nimport type {\n AgentAdapter,\n AgentInfo,\n ProcessInfo,\n ConversationMessage,\n ConversationOptions,\n SessionSummary,\n ListSessionsOptions,\n AgentDetectionContext,\n} from \"../../adapters/AgentAdapter.js\";\nimport { DevinAgentMapper } from \"./DevinAgentMapper.js\";\nimport { DevinSessionLocator, decodeDevinSessionRef } from \"./DevinSessionLocator.js\";\nimport { DevinSessionParser } from \"./DevinSessionParser.js\";\nimport { findHarnessProcesses, matchesExecutable } from \"../shared.js\";\nimport { executablePath } from \"../../utils/process.js\";\n\n/**\n * `devin` invocations that are utilities, not agent sessions. `acp` is\n * deliberately absent: a standalone `devin acp` serves an external client\n * and is itself an agent.\n */\nconst UTILITY_SUBCOMMANDS = new Set([\n \"auth\",\n \"cloud\",\n \"desktop\",\n \"doctor\",\n \"forward\",\n \"help\",\n \"list\",\n \"ls\",\n \"mcp\",\n \"migrate\",\n \"models\",\n \"plugins\",\n \"rm\",\n \"rules\",\n \"sandbox\",\n \"setup\",\n \"skills\",\n \"ssh\",\n \"uninstall\",\n \"update\",\n \"version\",\n]);\n\n/**\n * Flags that always consume the next argv token as their value.\n * (`devin --help`, main-command options.)\n */\nconst REQUIRED_VALUE_FLAGS = new Set([\n \"--prompt-file\",\n \"--config\",\n \"--permission-mode\",\n \"--model\",\n]);\n\n/**\n * Flags whose value is optional: the next token is consumed only when it\n * isn't itself a flag (commander `[<VALUE>]` semantics).\n */\nconst OPTIONAL_VALUE_FLAGS = new Set([\n \"-p\",\n \"--print\",\n \"-r\",\n \"--resume\",\n \"--export\",\n \"--respect-workspace-trust\",\n]);\n\n/**\n * First positional argv token after argv[0] — the subcommand candidate.\n * Flag values are skipped (so `devin -p \"update deps\"` isn't mistaken for\n * the `update` subcommand); `--` ends the search.\n */\nfunction firstPositionalToken(command: string): string | null {\n const argv0 = executablePath(command);\n const tokens = command\n .trim()\n .slice(argv0.length)\n .trim()\n .split(/\\s+/);\n for (let i = 0; i < tokens.length; i++) {\n const token = tokens[i];\n if (!token) continue;\n if (token === \"--\") return null;\n if (!token.startsWith(\"-\")) return token;\n const flag = token.split(\"=\", 1)[0];\n if (flag === token) {\n if (REQUIRED_VALUE_FLAGS.has(flag)) {\n i++;\n } else if (OPTIONAL_VALUE_FLAGS.has(flag) && !tokens[i + 1]?.startsWith(\"-\")) {\n i++;\n }\n }\n }\n return null;\n}\n\nexport class DevinAdapter implements AgentAdapter {\n readonly type = \"devin\" as const;\n readonly processNames = [\"devin\"] as const;\n\n private readonly parser: DevinSessionParser;\n private readonly locator: DevinSessionLocator;\n private readonly mapper: DevinAgentMapper;\n private readonly cleanup = (): void => this.close();\n\n constructor(dbPath?: string, locksDir?: string) {\n this.parser = new DevinSessionParser();\n // Locator defaults resolve dbPath itself and derive locksDir from it.\n this.locator = new DevinSessionLocator(dbPath, locksDir, this.parser);\n this.mapper = new DevinAgentMapper(this.locator.dbFilePath);\n process.once(\"exit\", this.cleanup);\n process.once(\"SIGINT\", this.cleanup);\n process.once(\"SIGTERM\", this.cleanup);\n }\n\n close(): void {\n process.off(\"exit\", this.cleanup);\n process.off(\"SIGINT\", this.cleanup);\n process.off(\"SIGTERM\", this.cleanup);\n this.locator.close();\n }\n\n canHandle(processInfo: ProcessInfo): boolean {\n if (!matchesExecutable(processInfo.command, \"devin\")) return false;\n const subcommand = firstPositionalToken(processInfo.command);\n return subcommand === null || !UTILITY_SUBCOMMANDS.has(subcommand.toLowerCase());\n }\n\n async detectAgents(context?: AgentDetectionContext): Promise<AgentInfo[]> {\n const { processes } = await findHarnessProcesses(this, context);\n if (processes.length === 0) return [];\n\n const db = this.locator.openDb();\n const processByPid = new Map(processes.map((proc) => [proc.pid, proc]));\n\n // Locks map a session to its holder PID (the `devin acp` backend); a TUI\n // is matched through its backend child's ppid. Only trust locks whose\n // holder is a live devin process in this snapshot (stale files and PID\n // reuse are filtered out).\n const sessionByPid = new Map<number, string>();\n for (const lock of this.locator.listActiveLocks()) {\n const holder = processByPid.get(lock.pid);\n if (!holder || !this.canHandle(holder)) continue;\n sessionByPid.set(lock.pid, lock.sessionId);\n if (holder.ppid !== undefined) sessionByPid.set(holder.ppid, lock.sessionId);\n }\n\n const agents: AgentInfo[] = [];\n\n for (const proc of processes) {\n if (this.isAcpBackendChild(proc, processByPid)) continue;\n\n const slug = sessionByPid.get(proc.pid);\n const session = !db\n ? null\n : slug\n ? this.locator.findSessionById(db, slug)\n : proc.cwd\n ? this.locator.findSessionForDirectory(db, proc.cwd)\n : null;\n\n if (!session || !db) {\n agents.push(this.mapper.mapProcessOnlyAgent(proc));\n continue;\n }\n\n const stats = this.parser.getSessionStats(db, session.sessionId);\n agents.push(this.mapper.mapSessionToAgent(session, stats, proc));\n }\n\n return agents;\n }\n\n getConversation(sessionFilePath: string, options?: ConversationOptions): ConversationMessage[] {\n const sessionId = decodeDevinSessionRef(sessionFilePath);\n if (!sessionId) return [];\n\n const db = this.locator.openDb();\n if (!db) return [];\n\n return this.parser.getConversation(db, sessionId, options);\n }\n\n async listSessions(opts?: ListSessionsOptions): Promise<SessionSummary[]> {\n return this.locator.listSessions(opts);\n }\n\n async findSessionsById(sessionId: string): Promise<SessionSummary[]> {\n return this.locator.findSessionsById(sessionId);\n }\n\n /**\n * A `devin acp` process spawned by another devin process is that session's\n * backend, not a separate agent. Standalone `acp` processes (external ACP\n * clients) keep their own agent entry.\n */\n private isAcpBackendChild(\n proc: ProcessInfo,\n processByPid: Map<number, ProcessInfo>,\n ): boolean {\n if (firstPositionalToken(proc.command)?.toLowerCase() !== \"acp\") return false;\n if (proc.ppid === undefined) return false;\n return processByPid.has(proc.ppid);\n }\n}\n"],"names":["DevinAgentMapper","DevinSessionLocator","decodeDevinSessionRef","DevinSessionParser","findHarnessProcesses","matchesExecutable","executablePath","UTILITY_SUBCOMMANDS","Set","REQUIRED_VALUE_FLAGS","OPTIONAL_VALUE_FLAGS","firstPositionalToken","command","argv0","tokens","trim","slice","length","split","i","token","startsWith","flag","has","DevinAdapter","type","processNames","parser","locator","mapper","cleanup","close","dbPath","locksDir","dbFilePath","process","once","off","canHandle","processInfo","subcommand","toLowerCase","detectAgents","context","processes","db","openDb","processByPid","Map","map","proc","pid","sessionByPid","lock","listActiveLocks","holder","get","set","sessionId","ppid","undefined","agents","isAcpBackendChild","slug","session","findSessionById","cwd","findSessionForDirectory","push","mapProcessOnlyAgent","stats","getSessionStats","mapSessionToAgent","getConversation","sessionFilePath","options","listSessions","opts","findSessionsById"],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AgentInfo, ProcessInfo } from "../../adapters/AgentAdapter.js";
|
|
2
|
+
import type { DevinSession } from "./DevinSessionLocator.js";
|
|
3
|
+
import type { DevinSessionStats } from "./DevinSessionParser.js";
|
|
4
|
+
export declare class DevinAgentMapper {
|
|
5
|
+
private readonly dbPath;
|
|
6
|
+
constructor(dbPath: string);
|
|
7
|
+
mapSessionToAgent(session: DevinSession, stats: DevinSessionStats, proc: ProcessInfo): AgentInfo;
|
|
8
|
+
mapProcessOnlyAgent(proc: ProcessInfo): AgentInfo;
|
|
9
|
+
private determineStatus;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=DevinAgentMapper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DevinAgentMapper.d.ts","sourceRoot":"","sources":["../../../src/harnesses/devin/DevinAgentMapper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAG7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAE7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAGjE,qBAAa,gBAAgB;IACf,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,MAAM;IAE3C,iBAAiB,CACf,OAAO,EAAE,YAAY,EACrB,KAAK,EAAE,iBAAiB,EACxB,IAAI,EAAE,WAAW,GAChB,SAAS;IAmBZ,mBAAmB,CAAC,IAAI,EAAE,WAAW,GAAG,SAAS;IAIjD,OAAO,CAAC,eAAe;CAIxB"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { AgentStatus } from "../../adapters/AgentAdapter.js";
|
|
2
|
+
import { generateAgentName } from "../../utils/matching.js";
|
|
3
|
+
import { encodeDevinSessionRef } from "./DevinSessionLocator.js";
|
|
4
|
+
import { isIdle, processOnlyAgent } from "../shared.js";
|
|
5
|
+
export class DevinAgentMapper {
|
|
6
|
+
dbPath;
|
|
7
|
+
constructor(dbPath){
|
|
8
|
+
this.dbPath = dbPath;
|
|
9
|
+
}
|
|
10
|
+
mapSessionToAgent(session, stats, proc) {
|
|
11
|
+
const lastActive = stats.lastTimeUpdated > 0 ? new Date(stats.lastTimeUpdated) : new Date(session.lastActivityAt || session.timeCreated);
|
|
12
|
+
return {
|
|
13
|
+
name: generateAgentName(session.directory || proc.cwd || "", proc.pid),
|
|
14
|
+
type: "devin",
|
|
15
|
+
status: this.determineStatus(stats, lastActive),
|
|
16
|
+
summary: stats.summary || session.title || "Devin session active",
|
|
17
|
+
pid: proc.pid,
|
|
18
|
+
projectPath: session.directory || proc.cwd || "",
|
|
19
|
+
sessionId: session.sessionId,
|
|
20
|
+
lastActive,
|
|
21
|
+
sessionFilePath: encodeDevinSessionRef(this.dbPath, session.sessionId)
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
mapProcessOnlyAgent(proc) {
|
|
25
|
+
return processOnlyAgent("devin", proc, {
|
|
26
|
+
summary: "Devin process running"
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
determineStatus(stats, lastActive) {
|
|
30
|
+
if (isIdle(lastActive)) return AgentStatus.IDLE;
|
|
31
|
+
return stats.lastRole === "assistant" ? AgentStatus.WAITING : AgentStatus.RUNNING;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
//# sourceMappingURL=DevinAgentMapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/harnesses/devin/DevinAgentMapper.ts"],"sourcesContent":["import type { AgentInfo, ProcessInfo } from \"../../adapters/AgentAdapter.js\";\nimport { AgentStatus } from \"../../adapters/AgentAdapter.js\";\nimport { generateAgentName } from \"../../utils/matching.js\";\nimport type { DevinSession } from \"./DevinSessionLocator.js\";\nimport { encodeDevinSessionRef } from \"./DevinSessionLocator.js\";\nimport type { DevinSessionStats } from \"./DevinSessionParser.js\";\nimport { isIdle, processOnlyAgent } from \"../shared.js\";\n\nexport class DevinAgentMapper {\n constructor(private readonly dbPath: string) {}\n\n mapSessionToAgent(\n session: DevinSession,\n stats: DevinSessionStats,\n proc: ProcessInfo,\n ): AgentInfo {\n const lastActive =\n stats.lastTimeUpdated > 0\n ? new Date(stats.lastTimeUpdated)\n : new Date(session.lastActivityAt || session.timeCreated);\n\n return {\n name: generateAgentName(session.directory || proc.cwd || \"\", proc.pid),\n type: \"devin\",\n status: this.determineStatus(stats, lastActive),\n summary: stats.summary || session.title || \"Devin session active\",\n pid: proc.pid,\n projectPath: session.directory || proc.cwd || \"\",\n sessionId: session.sessionId,\n lastActive,\n sessionFilePath: encodeDevinSessionRef(this.dbPath, session.sessionId),\n };\n }\n\n mapProcessOnlyAgent(proc: ProcessInfo): AgentInfo {\n return processOnlyAgent(\"devin\", proc, { summary: \"Devin process running\" });\n }\n\n private determineStatus(stats: DevinSessionStats, lastActive: Date): AgentStatus {\n if (isIdle(lastActive)) return AgentStatus.IDLE;\n return stats.lastRole === \"assistant\" ? AgentStatus.WAITING : AgentStatus.RUNNING;\n }\n}\n"],"names":["AgentStatus","generateAgentName","encodeDevinSessionRef","isIdle","processOnlyAgent","DevinAgentMapper","dbPath","mapSessionToAgent","session","stats","proc","lastActive","lastTimeUpdated","Date","lastActivityAt","timeCreated","name","directory","cwd","pid","type","status","determineStatus","summary","title","projectPath","sessionId","sessionFilePath","mapProcessOnlyAgent","IDLE","lastRole","WAITING","RUNNING"],"mappings":"AACA,SAASA,WAAW,QAAQ,iCAAiC;AAC7D,SAASC,iBAAiB,QAAQ,0BAA0B;AAE5D,SAASC,qBAAqB,QAAQ,2BAA2B;AAEjE,SAASC,MAAM,EAAEC,gBAAgB,QAAQ,eAAe;AAExD,OAAO,MAAMC;;IACX,YAAY,AAAiBC,MAAc,CAAE;aAAhBA,SAAAA;IAAiB;IAE9CC,kBACEC,OAAqB,EACrBC,KAAwB,EACxBC,IAAiB,EACN;QACX,MAAMC,aACJF,MAAMG,eAAe,GAAG,IACpB,IAAIC,KAAKJ,MAAMG,eAAe,IAC9B,IAAIC,KAAKL,QAAQM,cAAc,IAAIN,QAAQO,WAAW;QAE5D,OAAO;YACLC,MAAMf,kBAAkBO,QAAQS,SAAS,IAAIP,KAAKQ,GAAG,IAAI,IAAIR,KAAKS,GAAG;YACrEC,MAAM;YACNC,QAAQ,IAAI,CAACC,eAAe,CAACb,OAAOE;YACpCY,SAASd,MAAMc,OAAO,IAAIf,QAAQgB,KAAK,IAAI;YAC3CL,KAAKT,KAAKS,GAAG;YACbM,aAAajB,QAAQS,SAAS,IAAIP,KAAKQ,GAAG,IAAI;YAC9CQ,WAAWlB,QAAQkB,SAAS;YAC5Bf;YACAgB,iBAAiBzB,sBAAsB,IAAI,CAACI,MAAM,EAAEE,QAAQkB,SAAS;QACvE;IACF;IAEAE,oBAAoBlB,IAAiB,EAAa;QAChD,OAAON,iBAAiB,SAASM,MAAM;YAAEa,SAAS;QAAwB;IAC5E;IAEQD,gBAAgBb,KAAwB,EAAEE,UAAgB,EAAe;QAC/E,IAAIR,OAAOQ,aAAa,OAAOX,YAAY6B,IAAI;QAC/C,OAAOpB,MAAMqB,QAAQ,KAAK,cAAc9B,YAAY+B,OAAO,GAAG/B,YAAYgC,OAAO;IACnF;AACF"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
import type { ListSessionsOptions, SessionSummary } from "../../adapters/AgentAdapter.js";
|
|
3
|
+
import { DevinSessionParser } from "./DevinSessionParser.js";
|
|
4
|
+
export declare function encodeDevinSessionRef(dbPath: string, sessionId: string): string;
|
|
5
|
+
/** Inverse of {@link encodeDevinSessionRef}; null when the ref has no separator. */
|
|
6
|
+
export declare function decodeDevinSessionRef(ref: string): string | null;
|
|
7
|
+
export interface DevinSession {
|
|
8
|
+
sessionId: string;
|
|
9
|
+
directory: string;
|
|
10
|
+
title: string | null;
|
|
11
|
+
/** Epoch milliseconds. */
|
|
12
|
+
timeCreated: number;
|
|
13
|
+
/** Epoch milliseconds. */
|
|
14
|
+
lastActivityAt: number;
|
|
15
|
+
}
|
|
16
|
+
/** A live `session_locks/<slug>.lock` entry: the session and its holder PID. */
|
|
17
|
+
export interface DevinSessionLock {
|
|
18
|
+
sessionId: string;
|
|
19
|
+
pid: number;
|
|
20
|
+
}
|
|
21
|
+
export declare class DevinSessionLocator {
|
|
22
|
+
private readonly parser;
|
|
23
|
+
private readonly dbPath;
|
|
24
|
+
private readonly locksDir;
|
|
25
|
+
private db;
|
|
26
|
+
constructor(dbPath?: string, locksDir?: string, parser?: DevinSessionParser);
|
|
27
|
+
static resolveDbPath(): string;
|
|
28
|
+
static defaultLocksDir(dbPath: string): string;
|
|
29
|
+
openDb(): Database.Database | null;
|
|
30
|
+
close(): void;
|
|
31
|
+
/**
|
|
32
|
+
* Read `session_locks/*.lock` entries. Each file name is a session slug and
|
|
33
|
+
* its body is the PID of the `devin acp` backend holding the session.
|
|
34
|
+
* Malformed files are skipped; callers validate liveness against the
|
|
35
|
+
* process snapshot.
|
|
36
|
+
*/
|
|
37
|
+
listActiveLocks(): DevinSessionLock[];
|
|
38
|
+
findSessionById(db: Database.Database, sessionId: string): DevinSession | null;
|
|
39
|
+
findSessionForDirectory(db: Database.Database, directory: string): DevinSession | null;
|
|
40
|
+
listSessions(opts?: ListSessionsOptions): SessionSummary[];
|
|
41
|
+
findSessionsById(sessionId: string): SessionSummary[];
|
|
42
|
+
private toSessionSummary;
|
|
43
|
+
get dbFilePath(): string;
|
|
44
|
+
get sessionLocksDir(): string;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=DevinSessionLocator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DevinSessionLocator.d.ts","sourceRoot":"","sources":["../../../src/harnesses/devin/DevinSessionLocator.ts"],"names":[],"mappings":"AAEA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAC1F,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAM7D,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAE/E;AAED,oFAAoF;AACpF,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGhE;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,0BAA0B;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,0BAA0B;IAC1B,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,gFAAgF;AAChF,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACb;AAoBD,qBAAa,mBAAmB;IAQ5B,OAAO,CAAC,QAAQ,CAAC,MAAM;IAPzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,EAAE,CAAkC;gBAG1C,MAAM,SAAsC,EAC5C,QAAQ,SAA8C,EACrC,MAAM,GAAE,kBAA6C;IAMxE,MAAM,CAAC,aAAa,IAAI,MAAM;IAM9B,MAAM,CAAC,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAI9C,MAAM,IAAI,QAAQ,CAAC,QAAQ,GAAG,IAAI;IAYlC,KAAK,IAAI,IAAI;IAWb;;;;;OAKG;IACH,eAAe,IAAI,gBAAgB,EAAE;IAyBrC,eAAe,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAe9E,uBAAuB,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAiBtF,YAAY,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,cAAc,EAAE;IA0B1D,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,EAAE;IAmBrD,OAAO,CAAC,gBAAgB;IAaxB,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED,IAAI,eAAe,IAAI,MAAM,CAE5B;CACF"}
|