@ai-devkit/agent-manager 0.26.4 → 0.28.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/README.md +12 -4
- package/dist/AgentManager.d.ts +5 -0
- package/dist/AgentManager.d.ts.map +1 -1
- package/dist/AgentManager.js +21 -1
- package/dist/AgentManager.js.map +1 -1
- package/dist/__tests__/AgentManager.test.js +112 -0
- package/dist/__tests__/AgentManager.test.js.map +1 -1
- package/dist/__tests__/adapters/CodexAdapter.test.js +199 -0
- package/dist/__tests__/adapters/CodexAdapter.test.js.map +1 -1
- package/dist/__tests__/database/DurableAgentsDatabase.test.js +100 -0
- package/dist/__tests__/database/DurableAgentsDatabase.test.js.map +1 -0
- package/dist/__tests__/print/ClaudePrintAgent.integration.test.js +7 -7
- package/dist/__tests__/print/ClaudePrintAgent.integration.test.js.map +1 -1
- package/dist/__tests__/print/ClaudePrintAgentService.test.js +7 -7
- package/dist/__tests__/print/ClaudePrintAgentService.test.js.map +1 -1
- package/dist/__tests__/print/ClaudePrintRunner.test.js +1 -1
- package/dist/__tests__/print/ClaudePrintRunner.test.js.map +1 -1
- package/dist/__tests__/print/DurableAgent.test.js +17 -0
- package/dist/__tests__/print/DurableAgent.test.js.map +1 -0
- package/dist/__tests__/print/DurableAgentRepository.sqlite.test.js +186 -0
- package/dist/__tests__/print/DurableAgentRepository.sqlite.test.js.map +1 -0
- package/dist/__tests__/print/{PrintAgentStore.test.js → DurableAgentRepository.test.js} +63 -110
- package/dist/__tests__/print/DurableAgentRepository.test.js.map +1 -0
- package/dist/__tests__/utils/AgentRegistry.test.js +67 -0
- package/dist/__tests__/utils/AgentRegistry.test.js.map +1 -1
- package/dist/adapters/AgentAdapter.d.ts +2 -0
- package/dist/adapters/AgentAdapter.d.ts.map +1 -1
- package/dist/adapters/AgentAdapter.js.map +1 -1
- package/dist/adapters/CodexAdapter.d.ts +1 -0
- package/dist/adapters/CodexAdapter.d.ts.map +1 -1
- package/dist/adapters/CodexAdapter.js +16 -6
- package/dist/adapters/CodexAdapter.js.map +1 -1
- package/dist/database/connection.d.ts +1 -0
- package/dist/database/connection.d.ts.map +1 -1
- package/dist/database/connection.js +20 -5
- package/dist/database/connection.js.map +1 -1
- package/dist/database/migrations/002_pins.sql +1 -0
- package/dist/database/migrations/003_durable_agents.sql +43 -0
- package/dist/durable/ClaudeCliProbe.d.ts.map +1 -0
- package/dist/{print → durable}/ClaudeCliProbe.js +1 -1
- package/dist/durable/ClaudeCliProbe.js.map +1 -0
- package/dist/{print → durable}/ClaudePrintAgentService.d.ts +11 -11
- package/dist/durable/ClaudePrintAgentService.d.ts.map +1 -0
- package/dist/{print → durable}/ClaudePrintAgentService.js +12 -12
- package/dist/durable/ClaudePrintAgentService.js.map +1 -0
- package/dist/{print → durable}/ClaudePrintRunner.d.ts +3 -3
- package/dist/durable/ClaudePrintRunner.d.ts.map +1 -0
- package/dist/{print → durable}/ClaudePrintRunner.js +2 -2
- package/dist/durable/ClaudePrintRunner.js.map +1 -0
- package/dist/durable/DurableAgent.d.ts +61 -0
- package/dist/durable/DurableAgent.d.ts.map +1 -0
- package/dist/durable/DurableAgent.js +46 -0
- package/dist/durable/DurableAgent.js.map +1 -0
- package/dist/durable/DurableAgentRepository.d.ts +60 -0
- package/dist/durable/DurableAgentRepository.d.ts.map +1 -0
- package/dist/durable/DurableAgentRepository.js +325 -0
- package/dist/durable/DurableAgentRepository.js.map +1 -0
- package/dist/index.d.ts +12 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -7
- package/dist/index.js.map +1 -1
- package/dist/utils/AgentRegistry.d.ts +5 -0
- package/dist/utils/AgentRegistry.d.ts.map +1 -1
- package/dist/utils/AgentRegistry.js +19 -2
- package/dist/utils/AgentRegistry.js.map +1 -1
- package/package.json +1 -1
- package/src/AgentManager.ts +21 -0
- package/src/__tests__/AgentManager.test.ts +117 -0
- package/src/__tests__/adapters/CodexAdapter.test.ts +132 -0
- package/src/__tests__/database/DurableAgentsDatabase.test.ts +77 -0
- package/src/__tests__/print/ClaudePrintAgent.integration.test.ts +6 -6
- package/src/__tests__/print/ClaudePrintAgentService.test.ts +7 -7
- package/src/__tests__/print/ClaudePrintRunner.test.ts +3 -3
- package/src/__tests__/print/{PrintAgent.test.ts → DurableAgent.test.ts} +6 -6
- package/src/__tests__/print/DurableAgentRepository.sqlite.test.ts +105 -0
- package/src/__tests__/print/DurableAgentRepository.test.ts +163 -0
- package/src/__tests__/utils/AgentRegistry.test.ts +68 -0
- package/src/adapters/AgentAdapter.ts +3 -0
- package/src/adapters/CodexAdapter.ts +14 -11
- package/src/database/connection.ts +18 -5
- package/src/database/migrations/002_pins.sql +1 -0
- package/src/database/migrations/003_durable_agents.sql +43 -0
- package/src/{print → durable}/ClaudeCliProbe.ts +1 -1
- package/src/{print → durable}/ClaudePrintAgentService.ts +21 -21
- package/src/{print → durable}/ClaudePrintRunner.ts +4 -4
- package/src/durable/DurableAgent.ts +91 -0
- package/src/durable/DurableAgentRepository.ts +307 -0
- package/src/index.ts +27 -26
- package/src/utils/AgentRegistry.ts +21 -0
- package/dist/__tests__/print/PrintAgent.test.js +0 -17
- package/dist/__tests__/print/PrintAgent.test.js.map +0 -1
- package/dist/__tests__/print/PrintAgentStore.test.js.map +0 -1
- package/dist/print/ClaudeCliProbe.d.ts.map +0 -1
- package/dist/print/ClaudeCliProbe.js.map +0 -1
- package/dist/print/ClaudePrintAgentService.d.ts.map +0 -1
- package/dist/print/ClaudePrintAgentService.js.map +0 -1
- package/dist/print/ClaudePrintRunner.d.ts.map +0 -1
- package/dist/print/ClaudePrintRunner.js.map +0 -1
- package/dist/print/PrintAgent.d.ts +0 -57
- package/dist/print/PrintAgent.d.ts.map +0 -1
- package/dist/print/PrintAgent.js +0 -42
- package/dist/print/PrintAgent.js.map +0 -1
- package/dist/print/PrintAgentStore.d.ts +0 -69
- package/dist/print/PrintAgentStore.d.ts.map +0 -1
- package/dist/print/PrintAgentStore.js +0 -484
- package/dist/print/PrintAgentStore.js.map +0 -1
- package/src/__tests__/print/PrintAgentStore.test.ts +0 -192
- package/src/print/PrintAgent.ts +0 -86
- package/src/print/PrintAgentStore.ts +0 -503
- /package/dist/{print → durable}/ClaudeCliProbe.d.ts +0 -0
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
export type PrintAgentState = 'ready' | 'running' | 'degraded';
|
|
2
|
-
export type PrintSessionHealth = 'uninitialized' | 'healthy' | 'unknown' | 'mismatch';
|
|
3
|
-
export type PrintRunStatus = 'succeeded' | 'failed' | 'interrupted';
|
|
4
|
-
export interface ProcessIdentity {
|
|
5
|
-
pid: number;
|
|
6
|
-
startedAt: string;
|
|
7
|
-
}
|
|
8
|
-
export interface PrintActiveRun {
|
|
9
|
-
token: string;
|
|
10
|
-
owner: ProcessIdentity;
|
|
11
|
-
provider: ProcessIdentity | null;
|
|
12
|
-
startedAt: string;
|
|
13
|
-
}
|
|
14
|
-
export interface PrintLastResult {
|
|
15
|
-
status: PrintRunStatus;
|
|
16
|
-
completedAt: string;
|
|
17
|
-
exitCode: number | null;
|
|
18
|
-
summary: string;
|
|
19
|
-
}
|
|
20
|
-
export interface PrintAgent {
|
|
21
|
-
id: string;
|
|
22
|
-
name: string;
|
|
23
|
-
provider: 'claude';
|
|
24
|
-
mode: 'print';
|
|
25
|
-
cwd: string;
|
|
26
|
-
providerSessionId: string;
|
|
27
|
-
state: PrintAgentState;
|
|
28
|
-
sessionHealth: PrintSessionHealth;
|
|
29
|
-
createdAt: string;
|
|
30
|
-
updatedAt: string;
|
|
31
|
-
lastActiveAt: string | null;
|
|
32
|
-
lastResult: PrintLastResult | null;
|
|
33
|
-
activeRun: PrintActiveRun | null;
|
|
34
|
-
}
|
|
35
|
-
export declare class PrintAgentError extends Error {
|
|
36
|
-
readonly code: string;
|
|
37
|
-
constructor(message: string, code: string);
|
|
38
|
-
}
|
|
39
|
-
export declare class PrintAgentBusyError extends PrintAgentError {
|
|
40
|
-
readonly agentId: string;
|
|
41
|
-
constructor(agentId: string, agentName: string);
|
|
42
|
-
}
|
|
43
|
-
export declare class PrintAgentNotFoundError extends PrintAgentError {
|
|
44
|
-
readonly reference: string;
|
|
45
|
-
constructor(reference: string);
|
|
46
|
-
}
|
|
47
|
-
export declare class PrintAgentStoreError extends PrintAgentError {
|
|
48
|
-
constructor(message: string);
|
|
49
|
-
}
|
|
50
|
-
export declare class PrintAgentNameConflictError extends PrintAgentError {
|
|
51
|
-
readonly agentName: string;
|
|
52
|
-
constructor(agentName: string);
|
|
53
|
-
}
|
|
54
|
-
export declare class ClaudePrintError extends PrintAgentError {
|
|
55
|
-
constructor(message: string, code?: string);
|
|
56
|
-
}
|
|
57
|
-
//# sourceMappingURL=PrintAgent.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PrintAgent.d.ts","sourceRoot":"","sources":["../../src/print/PrintAgent.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,SAAS,GAAG,UAAU,CAAC;AAC/D,MAAM,MAAM,kBAAkB,GAAG,eAAe,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;AACtF,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,QAAQ,GAAG,aAAa,CAAC;AAEpE,MAAM,WAAW,eAAe;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,eAAe,CAAC;IACvB,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC5B,MAAM,EAAE,cAAc,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,OAAO,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,iBAAiB,EAAE,MAAM,CAAC;IAC1B,KAAK,EAAE,eAAe,CAAC;IACvB,aAAa,EAAE,kBAAkB,CAAC;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,eAAe,GAAG,IAAI,CAAC;IACnC,SAAS,EAAE,cAAc,GAAG,IAAI,CAAC;CACpC;AAED,qBAAa,eAAgB,SAAQ,KAAK;aAGlB,IAAI,EAAE,MAAM;gBAD5B,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,MAAM;CAKnC;AAED,qBAAa,mBAAoB,SAAQ,eAAe;aAEhC,OAAO,EAAE,MAAM;gBAAf,OAAO,EAAE,MAAM,EAC/B,SAAS,EAAE,MAAM;CAKxB;AAED,qBAAa,uBAAwB,SAAQ,eAAe;aAC5B,SAAS,EAAE,MAAM;gBAAjB,SAAS,EAAE,MAAM;CAIhD;AAED,qBAAa,oBAAqB,SAAQ,eAAe;gBACzC,OAAO,EAAE,MAAM;CAI9B;AAED,qBAAa,2BAA4B,SAAQ,eAAe;aAChC,SAAS,EAAE,MAAM;gBAAjB,SAAS,EAAE,MAAM;CAIhD;AAED,qBAAa,gBAAiB,SAAQ,eAAe;gBACrC,OAAO,EAAE,MAAM,EAAE,IAAI,SAAwB;CAI5D"}
|
package/dist/print/PrintAgent.js
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
export class PrintAgentError extends Error {
|
|
2
|
-
code;
|
|
3
|
-
constructor(message, code){
|
|
4
|
-
super(message), this.code = code;
|
|
5
|
-
this.name = 'PrintAgentError';
|
|
6
|
-
}
|
|
7
|
-
}
|
|
8
|
-
export class PrintAgentBusyError extends PrintAgentError {
|
|
9
|
-
agentId;
|
|
10
|
-
constructor(agentId, agentName){
|
|
11
|
-
super(`Print agent "${agentName}" is busy.`, 'PRINT_AGENT_BUSY'), this.agentId = agentId;
|
|
12
|
-
this.name = 'PrintAgentBusyError';
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
export class PrintAgentNotFoundError extends PrintAgentError {
|
|
16
|
-
reference;
|
|
17
|
-
constructor(reference){
|
|
18
|
-
super(`Print agent "${reference}" was not found.`, 'PRINT_AGENT_NOT_FOUND'), this.reference = reference;
|
|
19
|
-
this.name = 'PrintAgentNotFoundError';
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
export class PrintAgentStoreError extends PrintAgentError {
|
|
23
|
-
constructor(message){
|
|
24
|
-
super(message, 'PRINT_AGENT_STORE');
|
|
25
|
-
this.name = 'PrintAgentStoreError';
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
export class PrintAgentNameConflictError extends PrintAgentError {
|
|
29
|
-
agentName;
|
|
30
|
-
constructor(agentName){
|
|
31
|
-
super(`Print agent name "${agentName}" is already in use.`, 'PRINT_AGENT_NAME_CONFLICT'), this.agentName = agentName;
|
|
32
|
-
this.name = 'PrintAgentNameConflictError';
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
export class ClaudePrintError extends PrintAgentError {
|
|
36
|
-
constructor(message, code = 'CLAUDE_PRINT_FAILED'){
|
|
37
|
-
super(message, code);
|
|
38
|
-
this.name = 'ClaudePrintError';
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
//# sourceMappingURL=PrintAgent.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/print/PrintAgent.ts"],"sourcesContent":["export type PrintAgentState = 'ready' | 'running' | 'degraded';\nexport type PrintSessionHealth = 'uninitialized' | 'healthy' | 'unknown' | 'mismatch';\nexport type PrintRunStatus = 'succeeded' | 'failed' | 'interrupted';\n\nexport interface ProcessIdentity {\n pid: number;\n startedAt: string;\n}\n\nexport interface PrintActiveRun {\n token: string;\n owner: ProcessIdentity;\n provider: ProcessIdentity | null;\n startedAt: string;\n}\n\nexport interface PrintLastResult {\n status: PrintRunStatus;\n completedAt: string;\n exitCode: number | null;\n summary: string;\n}\n\nexport interface PrintAgent {\n id: string;\n name: string;\n provider: 'claude';\n mode: 'print';\n cwd: string;\n providerSessionId: string;\n state: PrintAgentState;\n sessionHealth: PrintSessionHealth;\n createdAt: string;\n updatedAt: string;\n lastActiveAt: string | null;\n lastResult: PrintLastResult | null;\n activeRun: PrintActiveRun | null;\n}\n\nexport class PrintAgentError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n ) {\n super(message);\n this.name = 'PrintAgentError';\n }\n}\n\nexport class PrintAgentBusyError extends PrintAgentError {\n constructor(\n public readonly agentId: string,\n agentName: string,\n ) {\n super(`Print agent \"${agentName}\" is busy.`, 'PRINT_AGENT_BUSY');\n this.name = 'PrintAgentBusyError';\n }\n}\n\nexport class PrintAgentNotFoundError extends PrintAgentError {\n constructor(public readonly reference: string) {\n super(`Print agent \"${reference}\" was not found.`, 'PRINT_AGENT_NOT_FOUND');\n this.name = 'PrintAgentNotFoundError';\n }\n}\n\nexport class PrintAgentStoreError extends PrintAgentError {\n constructor(message: string) {\n super(message, 'PRINT_AGENT_STORE');\n this.name = 'PrintAgentStoreError';\n }\n}\n\nexport class PrintAgentNameConflictError extends PrintAgentError {\n constructor(public readonly agentName: string) {\n super(`Print agent name \"${agentName}\" is already in use.`, 'PRINT_AGENT_NAME_CONFLICT');\n this.name = 'PrintAgentNameConflictError';\n }\n}\n\nexport class ClaudePrintError extends PrintAgentError {\n constructor(message: string, code = 'CLAUDE_PRINT_FAILED') {\n super(message, code);\n this.name = 'ClaudePrintError';\n }\n}\n"],"names":["PrintAgentError","Error","message","code","name","PrintAgentBusyError","agentId","agentName","PrintAgentNotFoundError","reference","PrintAgentStoreError","PrintAgentNameConflictError","ClaudePrintError"],"mappings":"AAuCA,OAAO,MAAMA,wBAAwBC;;IACjC,YACIC,OAAe,EACf,AAAgBC,IAAY,CAC9B;QACE,KAAK,CAACD,eAFUC,OAAAA;QAGhB,IAAI,CAACC,IAAI,GAAG;IAChB;AACJ;AAEA,OAAO,MAAMC,4BAA4BL;;IACrC,YACI,AAAgBM,OAAe,EAC/BC,SAAiB,CACnB;QACE,KAAK,CAAC,CAAC,aAAa,EAAEA,UAAU,UAAU,CAAC,EAAE,0BAH7BD,UAAAA;QAIhB,IAAI,CAACF,IAAI,GAAG;IAChB;AACJ;AAEA,OAAO,MAAMI,gCAAgCR;;IACzC,YAAY,AAAgBS,SAAiB,CAAE;QAC3C,KAAK,CAAC,CAAC,aAAa,EAAEA,UAAU,gBAAgB,CAAC,EAAE,+BAD3BA,YAAAA;QAExB,IAAI,CAACL,IAAI,GAAG;IAChB;AACJ;AAEA,OAAO,MAAMM,6BAA6BV;IACtC,YAAYE,OAAe,CAAE;QACzB,KAAK,CAACA,SAAS;QACf,IAAI,CAACE,IAAI,GAAG;IAChB;AACJ;AAEA,OAAO,MAAMO,oCAAoCX;;IAC7C,YAAY,AAAgBO,SAAiB,CAAE;QAC3C,KAAK,CAAC,CAAC,kBAAkB,EAAEA,UAAU,oBAAoB,CAAC,EAAE,mCADpCA,YAAAA;QAExB,IAAI,CAACH,IAAI,GAAG;IAChB;AACJ;AAEA,OAAO,MAAMQ,yBAAyBZ;IAClC,YAAYE,OAAe,EAAEC,OAAO,qBAAqB,CAAE;QACvD,KAAK,CAACD,SAASC;QACf,IAAI,CAACC,IAAI,GAAG;IAChB;AACJ"}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import type { PrintAgent, ProcessIdentity, PrintRunStatus, PrintSessionHealth } from './PrintAgent.js';
|
|
2
|
-
export interface CreatePrintAgentInput {
|
|
3
|
-
name: string;
|
|
4
|
-
cwd: string;
|
|
5
|
-
}
|
|
6
|
-
export interface PrintAgentStoreOptions {
|
|
7
|
-
filePath?: string;
|
|
8
|
-
lockTimeoutMs?: number;
|
|
9
|
-
now?: () => Date;
|
|
10
|
-
processInspector?: ProcessInspector;
|
|
11
|
-
incompleteLockGraceMs?: number;
|
|
12
|
-
mutationLockStaleMs?: number;
|
|
13
|
-
}
|
|
14
|
-
export interface ProcessInspector {
|
|
15
|
-
getIdentity(pid: number): ProcessIdentity | null;
|
|
16
|
-
}
|
|
17
|
-
export interface PrintRunCompletion {
|
|
18
|
-
status: PrintRunStatus;
|
|
19
|
-
exitCode: number | null;
|
|
20
|
-
summary: string;
|
|
21
|
-
sessionHealth: PrintSessionHealth;
|
|
22
|
-
}
|
|
23
|
-
export declare class PrintAgentStore {
|
|
24
|
-
readonly filePath: string;
|
|
25
|
-
private readonly lockPath;
|
|
26
|
-
private readonly lockTimeoutMs;
|
|
27
|
-
private readonly now;
|
|
28
|
-
private readonly processInspector;
|
|
29
|
-
private readonly runLocksRoot;
|
|
30
|
-
private readonly incompleteLockGraceMs;
|
|
31
|
-
private readonly mutationLockStaleMs;
|
|
32
|
-
constructor(options?: PrintAgentStoreOptions);
|
|
33
|
-
create(input: CreatePrintAgentInput): Promise<PrintAgent>;
|
|
34
|
-
list(): Promise<PrintAgent[]>;
|
|
35
|
-
getById(id: string): Promise<PrintAgent | null>;
|
|
36
|
-
reconcile(): Promise<void>;
|
|
37
|
-
resolve(reference: string): Promise<PrintAgent | PrintAgent[] | null>;
|
|
38
|
-
acquireRun(id: string): Promise<{
|
|
39
|
-
agent: PrintAgent;
|
|
40
|
-
token: string;
|
|
41
|
-
}>;
|
|
42
|
-
recordProviderProcess(id: string, token: string, identity: ProcessIdentity): Promise<void>;
|
|
43
|
-
completeRun(id: string, token: string, result: PrintRunCompletion): Promise<PrintAgent>;
|
|
44
|
-
private canonicalDirectory;
|
|
45
|
-
private validateBoundCwd;
|
|
46
|
-
private ensureSafeParent;
|
|
47
|
-
private ensureRunLocksRoot;
|
|
48
|
-
private assertNotSymlink;
|
|
49
|
-
private readFile;
|
|
50
|
-
private listRaw;
|
|
51
|
-
private isStoreFile;
|
|
52
|
-
private writeFile;
|
|
53
|
-
private withMutationLock;
|
|
54
|
-
private isYoungMutationLock;
|
|
55
|
-
private updateAgent;
|
|
56
|
-
private runLockPath;
|
|
57
|
-
private readRunLock;
|
|
58
|
-
private writeRunLock;
|
|
59
|
-
private requireOwnedRun;
|
|
60
|
-
private isActive;
|
|
61
|
-
private sameProcess;
|
|
62
|
-
private isYoungLock;
|
|
63
|
-
private removeOwnedRunLock;
|
|
64
|
-
private removeLockDirectory;
|
|
65
|
-
}
|
|
66
|
-
export declare class LocalProcessInspector implements ProcessInspector {
|
|
67
|
-
getIdentity(pid: number): ProcessIdentity | null;
|
|
68
|
-
}
|
|
69
|
-
//# sourceMappingURL=PrintAgentStore.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PrintAgentStore.d.ts","sourceRoot":"","sources":["../../src/print/PrintAgentStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,484 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import os from 'os';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import { randomUUID } from 'crypto';
|
|
5
|
-
import { execFileSync } from 'child_process';
|
|
6
|
-
import { PrintAgentBusyError, PrintAgentNameConflictError, PrintAgentNotFoundError, PrintAgentStoreError } from './PrintAgent.js';
|
|
7
|
-
const DEFAULT_FILE = path.join(os.homedir(), '.ai-devkit', 'print-agents.json');
|
|
8
|
-
export class PrintAgentStore {
|
|
9
|
-
filePath;
|
|
10
|
-
lockPath;
|
|
11
|
-
lockTimeoutMs;
|
|
12
|
-
now;
|
|
13
|
-
processInspector;
|
|
14
|
-
runLocksRoot;
|
|
15
|
-
incompleteLockGraceMs;
|
|
16
|
-
mutationLockStaleMs;
|
|
17
|
-
constructor(options = {}){
|
|
18
|
-
this.filePath = options.filePath ?? DEFAULT_FILE;
|
|
19
|
-
this.lockPath = `${this.filePath}.lock`;
|
|
20
|
-
this.lockTimeoutMs = options.lockTimeoutMs ?? 2000;
|
|
21
|
-
this.now = options.now ?? (()=>new Date());
|
|
22
|
-
this.processInspector = options.processInspector ?? new LocalProcessInspector();
|
|
23
|
-
this.runLocksRoot = path.join(path.dirname(this.filePath), 'print-agent-locks');
|
|
24
|
-
this.incompleteLockGraceMs = options.incompleteLockGraceMs ?? 30_000;
|
|
25
|
-
this.mutationLockStaleMs = options.mutationLockStaleMs ?? 30_000;
|
|
26
|
-
}
|
|
27
|
-
async create(input) {
|
|
28
|
-
const cwd = this.canonicalDirectory(input.cwd);
|
|
29
|
-
return this.withMutationLock(async ()=>{
|
|
30
|
-
const data = this.readFile();
|
|
31
|
-
if (data.agents.some((agent)=>agent.name.toLowerCase() === input.name.toLowerCase())) {
|
|
32
|
-
throw new PrintAgentNameConflictError(input.name);
|
|
33
|
-
}
|
|
34
|
-
const timestamp = this.now().toISOString();
|
|
35
|
-
let id = randomUUID();
|
|
36
|
-
let providerSessionId = randomUUID();
|
|
37
|
-
while(providerSessionId === id)providerSessionId = randomUUID();
|
|
38
|
-
while(data.agents.some((agent)=>agent.id === id))id = randomUUID();
|
|
39
|
-
const agent = {
|
|
40
|
-
id,
|
|
41
|
-
name: input.name,
|
|
42
|
-
provider: 'claude',
|
|
43
|
-
mode: 'print',
|
|
44
|
-
cwd,
|
|
45
|
-
providerSessionId,
|
|
46
|
-
state: 'ready',
|
|
47
|
-
sessionHealth: 'uninitialized',
|
|
48
|
-
createdAt: timestamp,
|
|
49
|
-
updatedAt: timestamp,
|
|
50
|
-
lastActiveAt: null,
|
|
51
|
-
lastResult: null,
|
|
52
|
-
activeRun: null
|
|
53
|
-
};
|
|
54
|
-
data.agents.push(agent);
|
|
55
|
-
this.writeFile(data);
|
|
56
|
-
return structuredClone(agent);
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
async list() {
|
|
60
|
-
await this.reconcile();
|
|
61
|
-
return this.listRaw();
|
|
62
|
-
}
|
|
63
|
-
async getById(id) {
|
|
64
|
-
return (await this.list()).find((agent)=>agent.id === id) ?? null;
|
|
65
|
-
}
|
|
66
|
-
async reconcile() {
|
|
67
|
-
const running = this.listRaw().filter((agent)=>agent.state === 'running' && agent.activeRun);
|
|
68
|
-
for (const snapshot of running){
|
|
69
|
-
const lockPath = this.runLockPath(snapshot.id);
|
|
70
|
-
const metadata = this.readRunLock(snapshot.id);
|
|
71
|
-
if (metadata && this.isActive(metadata)) continue;
|
|
72
|
-
if (!metadata && this.isYoungLock(lockPath)) continue;
|
|
73
|
-
if (fs.existsSync(lockPath)) {
|
|
74
|
-
const quarantine = `${lockPath}.stale-${randomUUID()}`;
|
|
75
|
-
try {
|
|
76
|
-
fs.renameSync(lockPath, quarantine);
|
|
77
|
-
this.removeLockDirectory(quarantine);
|
|
78
|
-
} catch {
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
const completedAt = this.now().toISOString();
|
|
83
|
-
await this.updateAgent(snapshot.id, (current)=>{
|
|
84
|
-
if (current.state !== 'running' || current.activeRun?.token !== snapshot.activeRun?.token) return current;
|
|
85
|
-
return {
|
|
86
|
-
...current,
|
|
87
|
-
state: 'degraded',
|
|
88
|
-
sessionHealth: 'unknown',
|
|
89
|
-
activeRun: null,
|
|
90
|
-
updatedAt: completedAt,
|
|
91
|
-
lastActiveAt: completedAt,
|
|
92
|
-
lastResult: {
|
|
93
|
-
status: 'interrupted',
|
|
94
|
-
completedAt,
|
|
95
|
-
exitCode: null,
|
|
96
|
-
summary: 'Previous print run was interrupted.'
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
async resolve(reference) {
|
|
103
|
-
const agents = await this.list();
|
|
104
|
-
const byId = agents.find((agent)=>agent.id === reference);
|
|
105
|
-
if (byId) return byId;
|
|
106
|
-
const matches = agents.filter((agent)=>agent.name.toLowerCase() === reference.toLowerCase());
|
|
107
|
-
if (matches.length === 0) return null;
|
|
108
|
-
return matches.length === 1 ? matches[0] : matches;
|
|
109
|
-
}
|
|
110
|
-
async acquireRun(id) {
|
|
111
|
-
const existing = await this.getById(id);
|
|
112
|
-
if (!existing) throw new PrintAgentNotFoundError(id);
|
|
113
|
-
this.validateBoundCwd(existing.cwd);
|
|
114
|
-
const runLock = this.runLockPath(id);
|
|
115
|
-
let recoveredStale = false;
|
|
116
|
-
for(;;){
|
|
117
|
-
this.ensureRunLocksRoot();
|
|
118
|
-
this.assertNotSymlink(runLock);
|
|
119
|
-
try {
|
|
120
|
-
fs.mkdirSync(runLock, {
|
|
121
|
-
mode: 0o700
|
|
122
|
-
});
|
|
123
|
-
break;
|
|
124
|
-
} catch (error) {
|
|
125
|
-
if (error.code !== 'EEXIST') {
|
|
126
|
-
throw new PrintAgentStoreError(`Cannot acquire print-agent run lock: ${error.message}`);
|
|
127
|
-
}
|
|
128
|
-
const metadata = this.readRunLock(id);
|
|
129
|
-
if (!metadata || this.isActive(metadata)) {
|
|
130
|
-
throw new PrintAgentBusyError(id, existing.name);
|
|
131
|
-
}
|
|
132
|
-
const quarantine = `${runLock}.stale-${randomUUID()}`;
|
|
133
|
-
try {
|
|
134
|
-
fs.renameSync(runLock, quarantine);
|
|
135
|
-
this.removeLockDirectory(quarantine);
|
|
136
|
-
recoveredStale = true;
|
|
137
|
-
} catch {
|
|
138
|
-
// Another contender changed the lock. Retry and inspect the winner.
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
const owner = this.processInspector.getIdentity(process.pid);
|
|
143
|
-
if (!owner) {
|
|
144
|
-
this.removeLockDirectory(runLock);
|
|
145
|
-
throw new PrintAgentStoreError('Cannot determine the current process identity.');
|
|
146
|
-
}
|
|
147
|
-
const token = randomUUID();
|
|
148
|
-
const startedAt = this.now().toISOString();
|
|
149
|
-
const activeRun = {
|
|
150
|
-
token,
|
|
151
|
-
owner,
|
|
152
|
-
provider: null,
|
|
153
|
-
startedAt
|
|
154
|
-
};
|
|
155
|
-
this.writeRunLock(id, activeRun);
|
|
156
|
-
try {
|
|
157
|
-
const agent = await this.updateAgent(id, (current)=>({
|
|
158
|
-
...current,
|
|
159
|
-
state: 'running',
|
|
160
|
-
activeRun,
|
|
161
|
-
updatedAt: startedAt,
|
|
162
|
-
...recoveredStale ? {
|
|
163
|
-
sessionHealth: 'unknown',
|
|
164
|
-
lastResult: {
|
|
165
|
-
status: 'interrupted',
|
|
166
|
-
completedAt: startedAt,
|
|
167
|
-
exitCode: null,
|
|
168
|
-
summary: 'Previous print run was interrupted.'
|
|
169
|
-
}
|
|
170
|
-
} : {}
|
|
171
|
-
}));
|
|
172
|
-
return {
|
|
173
|
-
agent,
|
|
174
|
-
token
|
|
175
|
-
};
|
|
176
|
-
} catch (error) {
|
|
177
|
-
this.removeOwnedRunLock(id, token);
|
|
178
|
-
throw error;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
async recordProviderProcess(id, token, identity) {
|
|
182
|
-
const metadata = this.requireOwnedRun(id, token);
|
|
183
|
-
const next = {
|
|
184
|
-
...metadata,
|
|
185
|
-
provider: identity
|
|
186
|
-
};
|
|
187
|
-
this.writeRunLock(id, next);
|
|
188
|
-
await this.updateAgent(id, (agent)=>{
|
|
189
|
-
if (agent.activeRun?.token !== token) throw new PrintAgentStoreError('Print run ownership changed.');
|
|
190
|
-
return {
|
|
191
|
-
...agent,
|
|
192
|
-
activeRun: next,
|
|
193
|
-
updatedAt: this.now().toISOString()
|
|
194
|
-
};
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
async completeRun(id, token, result) {
|
|
198
|
-
this.requireOwnedRun(id, token);
|
|
199
|
-
const completedAt = this.now().toISOString();
|
|
200
|
-
const agent = await this.updateAgent(id, (current)=>{
|
|
201
|
-
if (current.activeRun?.token !== token) throw new PrintAgentStoreError('Print run ownership changed.');
|
|
202
|
-
return {
|
|
203
|
-
...current,
|
|
204
|
-
state: result.status === 'succeeded' ? 'ready' : 'degraded',
|
|
205
|
-
sessionHealth: result.sessionHealth,
|
|
206
|
-
activeRun: null,
|
|
207
|
-
lastActiveAt: completedAt,
|
|
208
|
-
updatedAt: completedAt,
|
|
209
|
-
lastResult: {
|
|
210
|
-
status: result.status,
|
|
211
|
-
completedAt,
|
|
212
|
-
exitCode: result.exitCode,
|
|
213
|
-
summary: result.summary.slice(0, 4096)
|
|
214
|
-
}
|
|
215
|
-
};
|
|
216
|
-
});
|
|
217
|
-
this.removeOwnedRunLock(id, token);
|
|
218
|
-
return agent;
|
|
219
|
-
}
|
|
220
|
-
canonicalDirectory(input) {
|
|
221
|
-
try {
|
|
222
|
-
const resolved = fs.realpathSync(input);
|
|
223
|
-
if (!fs.statSync(resolved).isDirectory()) throw new Error('not a directory');
|
|
224
|
-
return resolved;
|
|
225
|
-
} catch {
|
|
226
|
-
throw new PrintAgentStoreError(`Print agent cwd is not an existing directory: ${input}`);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
validateBoundCwd(bound) {
|
|
230
|
-
try {
|
|
231
|
-
const stat = fs.lstatSync(bound);
|
|
232
|
-
if (!stat.isDirectory() || stat.isSymbolicLink() || fs.realpathSync(bound) !== bound) {
|
|
233
|
-
throw new Error('binding changed');
|
|
234
|
-
}
|
|
235
|
-
} catch {
|
|
236
|
-
throw new PrintAgentStoreError(`Print agent cwd binding is no longer safe: ${bound}`);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
ensureSafeParent() {
|
|
240
|
-
const parent = path.dirname(this.filePath);
|
|
241
|
-
fs.mkdirSync(parent, {
|
|
242
|
-
recursive: true,
|
|
243
|
-
mode: 0o700
|
|
244
|
-
});
|
|
245
|
-
const stat = fs.lstatSync(parent);
|
|
246
|
-
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
247
|
-
throw new PrintAgentStoreError(`Unsafe print-agent store directory: ${parent}`);
|
|
248
|
-
}
|
|
249
|
-
return parent;
|
|
250
|
-
}
|
|
251
|
-
ensureRunLocksRoot() {
|
|
252
|
-
this.ensureSafeParent();
|
|
253
|
-
fs.mkdirSync(this.runLocksRoot, {
|
|
254
|
-
recursive: true,
|
|
255
|
-
mode: 0o700
|
|
256
|
-
});
|
|
257
|
-
const stat = fs.lstatSync(this.runLocksRoot);
|
|
258
|
-
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
259
|
-
throw new PrintAgentStoreError(`Unsafe print-agent lock directory: ${this.runLocksRoot}`);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
assertNotSymlink(target) {
|
|
263
|
-
try {
|
|
264
|
-
if (fs.lstatSync(target).isSymbolicLink()) {
|
|
265
|
-
throw new PrintAgentStoreError(`Unsafe symbolic link in print-agent storage: ${target}`);
|
|
266
|
-
}
|
|
267
|
-
} catch (error) {
|
|
268
|
-
if (error instanceof PrintAgentStoreError) throw error;
|
|
269
|
-
if (error.code !== 'ENOENT') {
|
|
270
|
-
throw new PrintAgentStoreError(`Cannot inspect print-agent storage: ${target}`);
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
readFile() {
|
|
275
|
-
this.ensureSafeParent();
|
|
276
|
-
this.assertNotSymlink(this.filePath);
|
|
277
|
-
if (!fs.existsSync(this.filePath)) return {
|
|
278
|
-
version: 1,
|
|
279
|
-
agents: []
|
|
280
|
-
};
|
|
281
|
-
try {
|
|
282
|
-
const parsed = JSON.parse(fs.readFileSync(this.filePath, 'utf8'));
|
|
283
|
-
if (!this.isStoreFile(parsed)) throw new Error('invalid schema');
|
|
284
|
-
return parsed;
|
|
285
|
-
} catch {
|
|
286
|
-
throw new PrintAgentStoreError(`Invalid print-agent store: ${this.filePath}`);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
listRaw() {
|
|
290
|
-
return this.readFile().agents.map((agent)=>structuredClone(agent));
|
|
291
|
-
}
|
|
292
|
-
isStoreFile(value) {
|
|
293
|
-
if (!value || typeof value !== 'object') return false;
|
|
294
|
-
const record = value;
|
|
295
|
-
return record.version === 1 && Array.isArray(record.agents);
|
|
296
|
-
}
|
|
297
|
-
writeFile(data) {
|
|
298
|
-
const parent = this.ensureSafeParent();
|
|
299
|
-
this.assertNotSymlink(this.filePath);
|
|
300
|
-
const temp = path.join(parent, `.print-agents-${process.pid}-${randomUUID()}.tmp`);
|
|
301
|
-
this.assertNotSymlink(temp);
|
|
302
|
-
let fd;
|
|
303
|
-
try {
|
|
304
|
-
fd = fs.openSync(temp, 'wx', 0o600);
|
|
305
|
-
fs.writeFileSync(fd, JSON.stringify(data, null, 2), 'utf8');
|
|
306
|
-
fs.fsyncSync(fd);
|
|
307
|
-
fs.closeSync(fd);
|
|
308
|
-
fd = undefined;
|
|
309
|
-
fs.renameSync(temp, this.filePath);
|
|
310
|
-
fs.chmodSync(this.filePath, 0o600);
|
|
311
|
-
} catch (error) {
|
|
312
|
-
if (fd !== undefined) fs.closeSync(fd);
|
|
313
|
-
try {
|
|
314
|
-
fs.unlinkSync(temp);
|
|
315
|
-
} catch {}
|
|
316
|
-
if (error instanceof PrintAgentStoreError) throw error;
|
|
317
|
-
throw new PrintAgentStoreError(`Failed to update print-agent store: ${error.message}`);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
async withMutationLock(operation) {
|
|
321
|
-
this.ensureSafeParent();
|
|
322
|
-
const started = Date.now();
|
|
323
|
-
for(;;){
|
|
324
|
-
this.assertNotSymlink(this.lockPath);
|
|
325
|
-
try {
|
|
326
|
-
fs.mkdirSync(this.lockPath, {
|
|
327
|
-
mode: 0o700
|
|
328
|
-
});
|
|
329
|
-
break;
|
|
330
|
-
} catch (error) {
|
|
331
|
-
if (error.code !== 'EEXIST') {
|
|
332
|
-
throw new PrintAgentStoreError(`Cannot acquire print-agent store lock: ${error.message}`);
|
|
333
|
-
}
|
|
334
|
-
if (!this.isYoungMutationLock()) {
|
|
335
|
-
const quarantine = `${this.lockPath}.stale-${randomUUID()}`;
|
|
336
|
-
try {
|
|
337
|
-
fs.renameSync(this.lockPath, quarantine);
|
|
338
|
-
fs.rmdirSync(quarantine);
|
|
339
|
-
continue;
|
|
340
|
-
} catch {
|
|
341
|
-
// Another contender changed the lock. Retry until the bounded timeout.
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
if (Date.now() - started >= this.lockTimeoutMs) {
|
|
345
|
-
throw new PrintAgentStoreError('Timed out acquiring print-agent store lock.');
|
|
346
|
-
}
|
|
347
|
-
await new Promise((resolve)=>setTimeout(resolve, 10));
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
try {
|
|
351
|
-
return await operation();
|
|
352
|
-
} finally{
|
|
353
|
-
try {
|
|
354
|
-
fs.rmdirSync(this.lockPath);
|
|
355
|
-
} catch {}
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
isYoungMutationLock() {
|
|
359
|
-
try {
|
|
360
|
-
this.assertNotSymlink(this.lockPath);
|
|
361
|
-
const stat = fs.statSync(this.lockPath);
|
|
362
|
-
return stat.isDirectory() && Date.now() - stat.mtimeMs < this.mutationLockStaleMs;
|
|
363
|
-
} catch {
|
|
364
|
-
return false;
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
async updateAgent(id, update) {
|
|
368
|
-
return this.withMutationLock(async ()=>{
|
|
369
|
-
const data = this.readFile();
|
|
370
|
-
const index = data.agents.findIndex((agent)=>agent.id === id);
|
|
371
|
-
if (index < 0) throw new PrintAgentNotFoundError(id);
|
|
372
|
-
const next = update(data.agents[index]);
|
|
373
|
-
data.agents[index] = next;
|
|
374
|
-
this.writeFile(data);
|
|
375
|
-
return structuredClone(next);
|
|
376
|
-
});
|
|
377
|
-
}
|
|
378
|
-
runLockPath(id) {
|
|
379
|
-
if (!/^[0-9a-f-]{36}$/i.test(id)) throw new PrintAgentStoreError('Invalid print-agent id.');
|
|
380
|
-
return path.join(this.runLocksRoot, `${id}.lock`);
|
|
381
|
-
}
|
|
382
|
-
readRunLock(id) {
|
|
383
|
-
const ownerPath = path.join(this.runLockPath(id), 'owner.json');
|
|
384
|
-
try {
|
|
385
|
-
this.assertNotSymlink(ownerPath);
|
|
386
|
-
const value = JSON.parse(fs.readFileSync(ownerPath, 'utf8'));
|
|
387
|
-
if (!value || typeof value.token !== 'string' || !value.owner || typeof value.owner.pid !== 'number') return null;
|
|
388
|
-
return value;
|
|
389
|
-
} catch {
|
|
390
|
-
return null;
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
writeRunLock(id, metadata) {
|
|
394
|
-
const lockPath = this.runLockPath(id);
|
|
395
|
-
const ownerPath = path.join(lockPath, 'owner.json');
|
|
396
|
-
const tempPath = path.join(lockPath, `.owner-${randomUUID()}.tmp`);
|
|
397
|
-
this.assertNotSymlink(lockPath);
|
|
398
|
-
this.assertNotSymlink(ownerPath);
|
|
399
|
-
fs.writeFileSync(tempPath, JSON.stringify(metadata), {
|
|
400
|
-
encoding: 'utf8',
|
|
401
|
-
mode: 0o600,
|
|
402
|
-
flag: 'wx'
|
|
403
|
-
});
|
|
404
|
-
fs.renameSync(tempPath, ownerPath);
|
|
405
|
-
}
|
|
406
|
-
requireOwnedRun(id, token) {
|
|
407
|
-
const metadata = this.readRunLock(id);
|
|
408
|
-
if (!metadata || metadata.token !== token) throw new PrintAgentStoreError('Print run ownership changed.');
|
|
409
|
-
return metadata;
|
|
410
|
-
}
|
|
411
|
-
isActive(metadata) {
|
|
412
|
-
return this.sameProcess(metadata.owner) || metadata.provider !== null && this.sameProcess(metadata.provider);
|
|
413
|
-
}
|
|
414
|
-
sameProcess(expected) {
|
|
415
|
-
const actual = this.processInspector.getIdentity(expected.pid);
|
|
416
|
-
return actual !== null && actual.startedAt === expected.startedAt;
|
|
417
|
-
}
|
|
418
|
-
isYoungLock(lockPath) {
|
|
419
|
-
try {
|
|
420
|
-
this.assertNotSymlink(lockPath);
|
|
421
|
-
return Date.now() - fs.statSync(lockPath).mtimeMs < this.incompleteLockGraceMs;
|
|
422
|
-
} catch {
|
|
423
|
-
return false;
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
removeOwnedRunLock(id, token) {
|
|
427
|
-
const metadata = this.readRunLock(id);
|
|
428
|
-
if (!metadata || metadata.token !== token) return;
|
|
429
|
-
this.removeLockDirectory(this.runLockPath(id));
|
|
430
|
-
}
|
|
431
|
-
removeLockDirectory(lockPath) {
|
|
432
|
-
this.assertNotSymlink(lockPath);
|
|
433
|
-
try {
|
|
434
|
-
for (const name of fs.readdirSync(lockPath)){
|
|
435
|
-
const entry = path.join(lockPath, name);
|
|
436
|
-
this.assertNotSymlink(entry);
|
|
437
|
-
if (!fs.lstatSync(entry).isFile()) throw new PrintAgentStoreError(`Unsafe entry in print-agent lock: ${entry}`);
|
|
438
|
-
fs.unlinkSync(entry);
|
|
439
|
-
}
|
|
440
|
-
fs.rmdirSync(lockPath);
|
|
441
|
-
} catch (error) {
|
|
442
|
-
if (error.code !== 'ENOENT') throw error;
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
export class LocalProcessInspector {
|
|
447
|
-
getIdentity(pid) {
|
|
448
|
-
if (!Number.isInteger(pid) || pid <= 0) return null;
|
|
449
|
-
try {
|
|
450
|
-
if (process.platform === 'linux') {
|
|
451
|
-
const stat = fs.readFileSync(`/proc/${pid}/stat`, 'utf8');
|
|
452
|
-
const close = stat.lastIndexOf(')');
|
|
453
|
-
const fields = stat.slice(close + 2).split(' ');
|
|
454
|
-
const startTicks = fields[19];
|
|
455
|
-
if (!startTicks) return null;
|
|
456
|
-
return {
|
|
457
|
-
pid,
|
|
458
|
-
startedAt: `linux:${startTicks}`
|
|
459
|
-
};
|
|
460
|
-
}
|
|
461
|
-
const startedAt = execFileSync('ps', [
|
|
462
|
-
'-o',
|
|
463
|
-
'lstart=',
|
|
464
|
-
'-p',
|
|
465
|
-
String(pid)
|
|
466
|
-
], {
|
|
467
|
-
encoding: 'utf8',
|
|
468
|
-
stdio: [
|
|
469
|
-
'ignore',
|
|
470
|
-
'pipe',
|
|
471
|
-
'ignore'
|
|
472
|
-
]
|
|
473
|
-
}).trim();
|
|
474
|
-
return startedAt ? {
|
|
475
|
-
pid,
|
|
476
|
-
startedAt
|
|
477
|
-
} : null;
|
|
478
|
-
} catch {
|
|
479
|
-
return null;
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
//# sourceMappingURL=PrintAgentStore.js.map
|