@orgloop/agentctl 1.2.1 → 1.4.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/adapters/claude-code.d.ts +3 -1
- package/dist/adapters/claude-code.js +51 -0
- package/dist/adapters/codex.d.ts +3 -1
- package/dist/adapters/codex.js +35 -0
- package/dist/adapters/openclaw.d.ts +3 -1
- package/dist/adapters/openclaw.js +61 -4
- package/dist/adapters/opencode.d.ts +3 -1
- package/dist/adapters/opencode.js +57 -0
- package/dist/adapters/pi-rust.d.ts +3 -1
- package/dist/adapters/pi-rust.js +74 -0
- package/dist/adapters/pi.d.ts +3 -1
- package/dist/adapters/pi.js +38 -0
- package/dist/cli.js +55 -96
- package/dist/core/types.d.ts +26 -2
- package/dist/daemon/fuse-engine.d.ts +13 -10
- package/dist/daemon/fuse-engine.js +69 -46
- package/dist/daemon/metrics.d.ts +8 -6
- package/dist/daemon/metrics.js +15 -11
- package/dist/daemon/server.js +159 -43
- package/dist/daemon/session-tracker.d.ts +42 -43
- package/dist/daemon/session-tracker.js +141 -255
- package/dist/daemon/state.d.ts +12 -2
- package/dist/hooks.d.ts +1 -1
- package/package.json +1 -1
- package/dist/merge.d.ts +0 -24
- package/dist/merge.js +0 -65
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentAdapter, AgentSession, LaunchOpts, LifecycleEvent, ListOpts, PeekOpts, StopOpts } from "../core/types.js";
|
|
1
|
+
import type { AgentAdapter, AgentSession, DiscoveredSession, LaunchOpts, LifecycleEvent, ListOpts, PeekOpts, StopOpts } from "../core/types.js";
|
|
2
2
|
export interface PidInfo {
|
|
3
3
|
pid: number;
|
|
4
4
|
cwd: string;
|
|
@@ -38,6 +38,8 @@ export declare class ClaudeCodeAdapter implements AgentAdapter {
|
|
|
38
38
|
private readonly getPids;
|
|
39
39
|
private readonly isProcessAlive;
|
|
40
40
|
constructor(opts?: ClaudeCodeAdapterOpts);
|
|
41
|
+
discover(): Promise<DiscoveredSession[]>;
|
|
42
|
+
isAlive(sessionId: string): Promise<boolean>;
|
|
41
43
|
list(opts?: ListOpts): Promise<AgentSession[]>;
|
|
42
44
|
peek(sessionId: string, opts?: PeekOpts): Promise<string>;
|
|
43
45
|
status(sessionId: string): Promise<AgentSession>;
|
|
@@ -32,6 +32,57 @@ export class ClaudeCodeAdapter {
|
|
|
32
32
|
this.getPids = opts?.getPids || getClaudePids;
|
|
33
33
|
this.isProcessAlive = opts?.isProcessAlive || defaultIsProcessAlive;
|
|
34
34
|
}
|
|
35
|
+
async discover() {
|
|
36
|
+
const runningPids = await this.getPids();
|
|
37
|
+
const results = [];
|
|
38
|
+
let projectDirs;
|
|
39
|
+
try {
|
|
40
|
+
projectDirs = await fs.readdir(this.projectsDir);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
for (const projDir of projectDirs) {
|
|
46
|
+
const projPath = path.join(this.projectsDir, projDir);
|
|
47
|
+
const stat = await fs.stat(projPath).catch(() => null);
|
|
48
|
+
if (!stat?.isDirectory())
|
|
49
|
+
continue;
|
|
50
|
+
const entries = await this.getEntriesForProject(projPath, projDir);
|
|
51
|
+
for (const { entry, index } of entries) {
|
|
52
|
+
if (entry.isSidechain)
|
|
53
|
+
continue;
|
|
54
|
+
const isRunning = await this.isSessionRunning(entry, index, runningPids);
|
|
55
|
+
const { model, tokens } = await this.parseSessionTail(entry.fullPath);
|
|
56
|
+
results.push({
|
|
57
|
+
id: entry.sessionId,
|
|
58
|
+
status: isRunning ? "running" : "stopped",
|
|
59
|
+
adapter: this.id,
|
|
60
|
+
cwd: index.originalPath || entry.projectPath,
|
|
61
|
+
model,
|
|
62
|
+
startedAt: new Date(entry.created),
|
|
63
|
+
stoppedAt: isRunning ? undefined : new Date(entry.modified),
|
|
64
|
+
pid: isRunning
|
|
65
|
+
? await this.findMatchingPid(entry, index, runningPids)
|
|
66
|
+
: undefined,
|
|
67
|
+
prompt: entry.firstPrompt?.slice(0, 200),
|
|
68
|
+
tokens,
|
|
69
|
+
nativeMetadata: {
|
|
70
|
+
projectDir: index.originalPath || entry.projectPath,
|
|
71
|
+
gitBranch: entry.gitBranch,
|
|
72
|
+
messageCount: entry.messageCount,
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return results;
|
|
78
|
+
}
|
|
79
|
+
async isAlive(sessionId) {
|
|
80
|
+
const runningPids = await this.getPids();
|
|
81
|
+
const entry = await this.findIndexEntry(sessionId);
|
|
82
|
+
if (!entry)
|
|
83
|
+
return false;
|
|
84
|
+
return this.isSessionRunning(entry.entry, entry.index, runningPids);
|
|
85
|
+
}
|
|
35
86
|
async list(opts) {
|
|
36
87
|
const runningPids = await this.getPids();
|
|
37
88
|
const sessions = [];
|
package/dist/adapters/codex.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentAdapter, AgentSession, LaunchOpts, LifecycleEvent, ListOpts, PeekOpts, StopOpts } from "../core/types.js";
|
|
1
|
+
import type { AgentAdapter, AgentSession, DiscoveredSession, LaunchOpts, LifecycleEvent, ListOpts, PeekOpts, StopOpts } from "../core/types.js";
|
|
2
2
|
export interface CodexPidInfo {
|
|
3
3
|
pid: number;
|
|
4
4
|
cwd: string;
|
|
@@ -34,6 +34,8 @@ export declare class CodexAdapter implements AgentAdapter {
|
|
|
34
34
|
private readonly getPids;
|
|
35
35
|
private readonly isProcessAlive;
|
|
36
36
|
constructor(opts?: CodexAdapterOpts);
|
|
37
|
+
discover(): Promise<DiscoveredSession[]>;
|
|
38
|
+
isAlive(sessionId: string): Promise<boolean>;
|
|
37
39
|
list(opts?: ListOpts): Promise<AgentSession[]>;
|
|
38
40
|
peek(sessionId: string, opts?: PeekOpts): Promise<string>;
|
|
39
41
|
status(sessionId: string): Promise<AgentSession>;
|
package/dist/adapters/codex.js
CHANGED
|
@@ -30,6 +30,41 @@ export class CodexAdapter {
|
|
|
30
30
|
this.getPids = opts?.getPids || getCodexPids;
|
|
31
31
|
this.isProcessAlive = opts?.isProcessAlive || defaultIsProcessAlive;
|
|
32
32
|
}
|
|
33
|
+
async discover() {
|
|
34
|
+
const runningPids = await this.getPids();
|
|
35
|
+
const sessionInfos = await this.discoverSessions();
|
|
36
|
+
const results = [];
|
|
37
|
+
for (const info of sessionInfos) {
|
|
38
|
+
const isRunning = this.isSessionRunning(info, runningPids);
|
|
39
|
+
const pid = isRunning
|
|
40
|
+
? this.findMatchingPid(info, runningPids)
|
|
41
|
+
: undefined;
|
|
42
|
+
results.push({
|
|
43
|
+
id: info.id,
|
|
44
|
+
status: isRunning ? "running" : "stopped",
|
|
45
|
+
adapter: this.id,
|
|
46
|
+
cwd: info.cwd,
|
|
47
|
+
model: info.model,
|
|
48
|
+
startedAt: info.created,
|
|
49
|
+
stoppedAt: isRunning ? undefined : info.modified,
|
|
50
|
+
pid,
|
|
51
|
+
prompt: info.firstPrompt,
|
|
52
|
+
tokens: info.tokens,
|
|
53
|
+
nativeMetadata: {
|
|
54
|
+
cliVersion: info.cliVersion,
|
|
55
|
+
lastMessage: info.lastMessage,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return results;
|
|
60
|
+
}
|
|
61
|
+
async isAlive(sessionId) {
|
|
62
|
+
const runningPids = await this.getPids();
|
|
63
|
+
const info = await this.findSession(sessionId);
|
|
64
|
+
if (!info)
|
|
65
|
+
return false;
|
|
66
|
+
return this.isSessionRunning(info, runningPids);
|
|
67
|
+
}
|
|
33
68
|
async list(opts) {
|
|
34
69
|
const runningPids = await this.getPids();
|
|
35
70
|
const sessionInfos = await this.discoverSessions();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentAdapter, AgentSession, LaunchOpts, LifecycleEvent, ListOpts, PeekOpts, StopOpts } from "../core/types.js";
|
|
1
|
+
import type { AgentAdapter, AgentSession, DiscoveredSession, LaunchOpts, LifecycleEvent, ListOpts, PeekOpts, StopOpts } from "../core/types.js";
|
|
2
2
|
export interface DeviceIdentity {
|
|
3
3
|
deviceId: string;
|
|
4
4
|
publicKeyPem: string;
|
|
@@ -122,6 +122,8 @@ export declare class OpenClawAdapter implements AgentAdapter {
|
|
|
122
122
|
private readonly rpcCall;
|
|
123
123
|
constructor(opts?: OpenClawAdapterOpts);
|
|
124
124
|
private tryLoadDeviceIdentity;
|
|
125
|
+
discover(): Promise<DiscoveredSession[]>;
|
|
126
|
+
isAlive(sessionId: string): Promise<boolean>;
|
|
125
127
|
list(opts?: ListOpts): Promise<AgentSession[]>;
|
|
126
128
|
peek(sessionId: string, opts?: PeekOpts): Promise<string>;
|
|
127
129
|
status(sessionId: string): Promise<AgentSession>;
|
|
@@ -197,6 +197,62 @@ export class OpenClawAdapter {
|
|
|
197
197
|
return null;
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
|
+
async discover() {
|
|
201
|
+
if (!this.authToken)
|
|
202
|
+
return [];
|
|
203
|
+
let result;
|
|
204
|
+
try {
|
|
205
|
+
result = (await this.rpcCall("sessions.list", {
|
|
206
|
+
includeDerivedTitles: true,
|
|
207
|
+
includeLastMessage: true,
|
|
208
|
+
}));
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
return [];
|
|
212
|
+
}
|
|
213
|
+
return result.sessions.map((row) => {
|
|
214
|
+
const updatedAt = row.updatedAt ?? 0;
|
|
215
|
+
const model = row.model || result.defaults.model || undefined;
|
|
216
|
+
const input = row.inputTokens ?? 0;
|
|
217
|
+
const output = row.outputTokens ?? 0;
|
|
218
|
+
// Gateway is the source of truth: if a session is in the list, it's alive.
|
|
219
|
+
// Don't guess from timestamps — sessions can be idle for hours between messages.
|
|
220
|
+
return {
|
|
221
|
+
id: row.sessionId || row.key,
|
|
222
|
+
status: "running",
|
|
223
|
+
adapter: this.id,
|
|
224
|
+
model,
|
|
225
|
+
startedAt: updatedAt > 0 ? new Date(updatedAt) : new Date(),
|
|
226
|
+
prompt: row.derivedTitle || row.displayName || row.label,
|
|
227
|
+
tokens: input || output ? { in: input, out: output } : undefined,
|
|
228
|
+
nativeMetadata: {
|
|
229
|
+
key: row.key,
|
|
230
|
+
kind: row.kind,
|
|
231
|
+
channel: row.channel,
|
|
232
|
+
displayName: row.displayName,
|
|
233
|
+
modelProvider: row.modelProvider || result.defaults.modelProvider,
|
|
234
|
+
lastMessagePreview: row.lastMessagePreview,
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
async isAlive(sessionId) {
|
|
240
|
+
if (!this.authToken)
|
|
241
|
+
return false;
|
|
242
|
+
try {
|
|
243
|
+
const result = (await this.rpcCall("sessions.list", {
|
|
244
|
+
search: sessionId,
|
|
245
|
+
}));
|
|
246
|
+
// Gateway is the source of truth: if a session is in the list, it's alive.
|
|
247
|
+
return result.sessions.some((s) => s.sessionId === sessionId ||
|
|
248
|
+
s.key === sessionId ||
|
|
249
|
+
s.sessionId?.startsWith(sessionId) ||
|
|
250
|
+
s.key.startsWith(sessionId));
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
200
256
|
async list(opts) {
|
|
201
257
|
if (!this.authToken) {
|
|
202
258
|
console.warn("Warning: OPENCLAW_GATEWAY_TOKEN is not set — OpenClaw adapter cannot authenticate. " +
|
|
@@ -342,17 +398,18 @@ export class OpenClawAdapter {
|
|
|
342
398
|
}
|
|
343
399
|
// --- Private helpers ---
|
|
344
400
|
mapRowToSession(row, defaults) {
|
|
345
|
-
const now = Date.now();
|
|
346
401
|
const updatedAt = row.updatedAt ?? 0;
|
|
347
|
-
const ageMs = now - updatedAt;
|
|
348
|
-
|
|
402
|
+
const ageMs = Date.now() - updatedAt;
|
|
403
|
+
// Gateway is the source of truth: if a session is in the list, it's alive.
|
|
404
|
+
// Use "running" for recently active sessions, "idle" for quiet ones.
|
|
405
|
+
const status = updatedAt > 0 && ageMs < 5 * 60 * 1000 ? "running" : "idle";
|
|
349
406
|
const model = row.model || defaults.model || undefined;
|
|
350
407
|
const input = row.inputTokens ?? 0;
|
|
351
408
|
const output = row.outputTokens ?? 0;
|
|
352
409
|
return {
|
|
353
410
|
id: row.sessionId || row.key,
|
|
354
411
|
adapter: this.id,
|
|
355
|
-
status
|
|
412
|
+
status,
|
|
356
413
|
startedAt: updatedAt > 0 ? new Date(updatedAt) : new Date(),
|
|
357
414
|
cwd: undefined,
|
|
358
415
|
model,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentAdapter, AgentSession, LaunchOpts, LifecycleEvent, ListOpts, PeekOpts, StopOpts } from "../core/types.js";
|
|
1
|
+
import type { AgentAdapter, AgentSession, DiscoveredSession, LaunchOpts, LifecycleEvent, ListOpts, PeekOpts, StopOpts } from "../core/types.js";
|
|
2
2
|
export interface PidInfo {
|
|
3
3
|
pid: number;
|
|
4
4
|
cwd: string;
|
|
@@ -95,6 +95,8 @@ export declare class OpenCodeAdapter implements AgentAdapter {
|
|
|
95
95
|
private readonly getPids;
|
|
96
96
|
private readonly isProcessAlive;
|
|
97
97
|
constructor(opts?: OpenCodeAdapterOpts);
|
|
98
|
+
discover(): Promise<DiscoveredSession[]>;
|
|
99
|
+
isAlive(sessionId: string): Promise<boolean>;
|
|
98
100
|
list(opts?: ListOpts): Promise<AgentSession[]>;
|
|
99
101
|
peek(sessionId: string, opts?: PeekOpts): Promise<string>;
|
|
100
102
|
status(sessionId: string): Promise<AgentSession>;
|
|
@@ -39,6 +39,63 @@ export class OpenCodeAdapter {
|
|
|
39
39
|
this.getPids = opts?.getPids || getOpenCodePids;
|
|
40
40
|
this.isProcessAlive = opts?.isProcessAlive || defaultIsProcessAlive;
|
|
41
41
|
}
|
|
42
|
+
async discover() {
|
|
43
|
+
const runningPids = await this.getPids();
|
|
44
|
+
const results = [];
|
|
45
|
+
let projectDirs;
|
|
46
|
+
try {
|
|
47
|
+
projectDirs = await fs.readdir(this.sessionDir);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
for (const projHash of projectDirs) {
|
|
53
|
+
const projPath = path.join(this.sessionDir, projHash);
|
|
54
|
+
const stat = await fs.stat(projPath).catch(() => null);
|
|
55
|
+
if (!stat?.isDirectory())
|
|
56
|
+
continue;
|
|
57
|
+
const sessionFiles = await this.getSessionFilesForProject(projPath);
|
|
58
|
+
for (const sessionData of sessionFiles) {
|
|
59
|
+
const isRunning = await this.isSessionRunning(sessionData, runningPids);
|
|
60
|
+
const { model, tokens, cost } = await this.aggregateMessageStats(sessionData.id);
|
|
61
|
+
const createdAt = sessionData.time?.created
|
|
62
|
+
? new Date(sessionData.time.created)
|
|
63
|
+
: new Date();
|
|
64
|
+
const updatedAt = sessionData.time?.updated
|
|
65
|
+
? new Date(sessionData.time.updated)
|
|
66
|
+
: undefined;
|
|
67
|
+
results.push({
|
|
68
|
+
id: sessionData.id,
|
|
69
|
+
status: isRunning ? "running" : "stopped",
|
|
70
|
+
adapter: this.id,
|
|
71
|
+
cwd: sessionData.directory,
|
|
72
|
+
model,
|
|
73
|
+
startedAt: createdAt,
|
|
74
|
+
stoppedAt: isRunning ? undefined : updatedAt,
|
|
75
|
+
pid: isRunning
|
|
76
|
+
? await this.findMatchingPid(sessionData, runningPids)
|
|
77
|
+
: undefined,
|
|
78
|
+
prompt: sessionData.title?.slice(0, 200),
|
|
79
|
+
tokens,
|
|
80
|
+
cost,
|
|
81
|
+
nativeMetadata: {
|
|
82
|
+
projectID: sessionData.projectID,
|
|
83
|
+
slug: sessionData.slug,
|
|
84
|
+
summary: sessionData.summary,
|
|
85
|
+
version: sessionData.version,
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return results;
|
|
91
|
+
}
|
|
92
|
+
async isAlive(sessionId) {
|
|
93
|
+
const runningPids = await this.getPids();
|
|
94
|
+
const resolved = await this.resolveSessionId(sessionId);
|
|
95
|
+
if (!resolved)
|
|
96
|
+
return false;
|
|
97
|
+
return this.isSessionRunning(resolved, runningPids);
|
|
98
|
+
}
|
|
42
99
|
async list(opts) {
|
|
43
100
|
const runningPids = await this.getPids();
|
|
44
101
|
const sessions = [];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentAdapter, AgentSession, LaunchOpts, LifecycleEvent, ListOpts, PeekOpts, StopOpts } from "../core/types.js";
|
|
1
|
+
import type { AgentAdapter, AgentSession, DiscoveredSession, LaunchOpts, LifecycleEvent, ListOpts, PeekOpts, StopOpts } from "../core/types.js";
|
|
2
2
|
export interface PidInfo {
|
|
3
3
|
pid: number;
|
|
4
4
|
cwd: string;
|
|
@@ -41,6 +41,8 @@ export declare class PiRustAdapter implements AgentAdapter {
|
|
|
41
41
|
private readonly getPids;
|
|
42
42
|
private readonly isProcessAlive;
|
|
43
43
|
constructor(opts?: PiRustAdapterOpts);
|
|
44
|
+
discover(): Promise<DiscoveredSession[]>;
|
|
45
|
+
isAlive(sessionId: string): Promise<boolean>;
|
|
44
46
|
list(opts?: ListOpts): Promise<AgentSession[]>;
|
|
45
47
|
peek(sessionId: string, opts?: PeekOpts): Promise<string>;
|
|
46
48
|
status(sessionId: string): Promise<AgentSession>;
|
package/dist/adapters/pi-rust.js
CHANGED
|
@@ -33,6 +33,80 @@ export class PiRustAdapter {
|
|
|
33
33
|
this.getPids = opts?.getPids || getPiRustPids;
|
|
34
34
|
this.isProcessAlive = opts?.isProcessAlive || defaultIsProcessAlive;
|
|
35
35
|
}
|
|
36
|
+
async discover() {
|
|
37
|
+
const runningPids = await this.getPids();
|
|
38
|
+
const results = [];
|
|
39
|
+
let projectDirs;
|
|
40
|
+
try {
|
|
41
|
+
const entries = await fs.readdir(this.sessionDir);
|
|
42
|
+
projectDirs = entries.filter((e) => e.startsWith("--"));
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
for (const projDir of projectDirs) {
|
|
48
|
+
const projPath = path.join(this.sessionDir, projDir);
|
|
49
|
+
const stat = await fs.stat(projPath).catch(() => null);
|
|
50
|
+
if (!stat?.isDirectory())
|
|
51
|
+
continue;
|
|
52
|
+
const projectCwd = decodeProjDir(projDir);
|
|
53
|
+
const sessionFiles = await this.getSessionFiles(projPath);
|
|
54
|
+
for (const file of sessionFiles) {
|
|
55
|
+
const filePath = path.join(projPath, file);
|
|
56
|
+
const header = await this.readSessionHeader(filePath);
|
|
57
|
+
if (!header)
|
|
58
|
+
continue;
|
|
59
|
+
const isRunning = await this.isSessionRunning(header, projectCwd, runningPids);
|
|
60
|
+
const { model, tokens, cost } = await this.parseSessionTail(filePath);
|
|
61
|
+
const firstPrompt = await this.readFirstPrompt(filePath);
|
|
62
|
+
let fileStat;
|
|
63
|
+
try {
|
|
64
|
+
fileStat = await fs.stat(filePath);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// ignore
|
|
68
|
+
}
|
|
69
|
+
results.push({
|
|
70
|
+
id: header.id,
|
|
71
|
+
status: isRunning ? "running" : "stopped",
|
|
72
|
+
adapter: this.id,
|
|
73
|
+
cwd: header.cwd || projectCwd,
|
|
74
|
+
model: model || header.modelId,
|
|
75
|
+
startedAt: new Date(header.timestamp),
|
|
76
|
+
stoppedAt: isRunning
|
|
77
|
+
? undefined
|
|
78
|
+
: fileStat
|
|
79
|
+
? new Date(Number(fileStat.mtimeMs))
|
|
80
|
+
: undefined,
|
|
81
|
+
pid: isRunning
|
|
82
|
+
? await this.findMatchingPid(header, projectCwd, runningPids)
|
|
83
|
+
: undefined,
|
|
84
|
+
prompt: firstPrompt?.slice(0, 200),
|
|
85
|
+
tokens,
|
|
86
|
+
cost: cost ?? undefined,
|
|
87
|
+
nativeMetadata: {
|
|
88
|
+
provider: header.provider,
|
|
89
|
+
thinkingLevel: header.thinkingLevel,
|
|
90
|
+
projectDir: projectCwd,
|
|
91
|
+
sessionFile: filePath,
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return results;
|
|
97
|
+
}
|
|
98
|
+
async isAlive(sessionId) {
|
|
99
|
+
const runningPids = await this.getPids();
|
|
100
|
+
const filePath = await this.findSessionFile(sessionId);
|
|
101
|
+
if (!filePath)
|
|
102
|
+
return false;
|
|
103
|
+
const header = await this.readSessionHeader(filePath);
|
|
104
|
+
if (!header)
|
|
105
|
+
return false;
|
|
106
|
+
const projDir = path.basename(path.dirname(filePath));
|
|
107
|
+
const projectCwd = decodeProjDir(projDir);
|
|
108
|
+
return this.isSessionRunning(header, projectCwd, runningPids);
|
|
109
|
+
}
|
|
36
110
|
async list(opts) {
|
|
37
111
|
const runningPids = await this.getPids();
|
|
38
112
|
const sessions = [];
|
package/dist/adapters/pi.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentAdapter, AgentSession, LaunchOpts, LifecycleEvent, ListOpts, PeekOpts, StopOpts } from "../core/types.js";
|
|
1
|
+
import type { AgentAdapter, AgentSession, DiscoveredSession, LaunchOpts, LifecycleEvent, ListOpts, PeekOpts, StopOpts } from "../core/types.js";
|
|
2
2
|
export interface PidInfo {
|
|
3
3
|
pid: number;
|
|
4
4
|
cwd: string;
|
|
@@ -41,6 +41,8 @@ export declare class PiAdapter implements AgentAdapter {
|
|
|
41
41
|
private readonly getPids;
|
|
42
42
|
private readonly isProcessAlive;
|
|
43
43
|
constructor(opts?: PiAdapterOpts);
|
|
44
|
+
discover(): Promise<DiscoveredSession[]>;
|
|
45
|
+
isAlive(sessionId: string): Promise<boolean>;
|
|
44
46
|
list(opts?: ListOpts): Promise<AgentSession[]>;
|
|
45
47
|
peek(sessionId: string, opts?: PeekOpts): Promise<string>;
|
|
46
48
|
/** Extract assistant messages from a JSONL session file */
|
package/dist/adapters/pi.js
CHANGED
|
@@ -33,6 +33,44 @@ export class PiAdapter {
|
|
|
33
33
|
this.getPids = opts?.getPids || getPiPids;
|
|
34
34
|
this.isProcessAlive = opts?.isProcessAlive || defaultIsProcessAlive;
|
|
35
35
|
}
|
|
36
|
+
async discover() {
|
|
37
|
+
const runningPids = await this.getPids();
|
|
38
|
+
const discovered = await this.discoverSessions();
|
|
39
|
+
const results = [];
|
|
40
|
+
for (const disc of discovered) {
|
|
41
|
+
const isRunning = await this.isSessionRunning(disc, runningPids);
|
|
42
|
+
const { model, tokens, cost } = await this.parseSessionTail(disc.filePath, disc.header);
|
|
43
|
+
results.push({
|
|
44
|
+
id: disc.sessionId,
|
|
45
|
+
status: isRunning ? "running" : "stopped",
|
|
46
|
+
adapter: this.id,
|
|
47
|
+
cwd: disc.header.cwd,
|
|
48
|
+
model,
|
|
49
|
+
startedAt: disc.created,
|
|
50
|
+
stoppedAt: isRunning ? undefined : disc.modified,
|
|
51
|
+
pid: isRunning
|
|
52
|
+
? await this.findMatchingPid(disc, runningPids)
|
|
53
|
+
: undefined,
|
|
54
|
+
prompt: await this.getFirstPrompt(disc.filePath),
|
|
55
|
+
tokens,
|
|
56
|
+
cost,
|
|
57
|
+
nativeMetadata: {
|
|
58
|
+
provider: disc.header.provider,
|
|
59
|
+
thinkingLevel: disc.header.thinkingLevel,
|
|
60
|
+
version: disc.header.version,
|
|
61
|
+
cwdSlug: disc.cwdSlug,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return results;
|
|
66
|
+
}
|
|
67
|
+
async isAlive(sessionId) {
|
|
68
|
+
const runningPids = await this.getPids();
|
|
69
|
+
const disc = await this.findSession(sessionId);
|
|
70
|
+
if (!disc)
|
|
71
|
+
return false;
|
|
72
|
+
return this.isSessionRunning(disc, runningPids);
|
|
73
|
+
}
|
|
36
74
|
async list(opts) {
|
|
37
75
|
const runningPids = await this.getPids();
|
|
38
76
|
const discovered = await this.discoverSessions();
|