@ai-devkit/agent-manager 0.34.0 → 0.35.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/AgentManager.d.ts +2 -0
- package/dist/AgentManager.d.ts.map +1 -1
- package/dist/AgentManager.js +25 -0
- package/dist/AgentManager.js.map +1 -1
- package/dist/__tests__/AgentManager.test.js +74 -0
- package/dist/__tests__/AgentManager.test.js.map +1 -1
- package/dist/__tests__/adapters/ClaudeCodeAdapter.test.js +28 -0
- package/dist/__tests__/adapters/ClaudeCodeAdapter.test.js.map +1 -1
- package/dist/__tests__/adapters/CodexAdapter.test.js +7 -0
- package/dist/__tests__/adapters/CodexAdapter.test.js.map +1 -1
- package/dist/__tests__/providers/copilot/CopilotAdapter.test.js +18 -0
- package/dist/__tests__/providers/copilot/CopilotAdapter.test.js.map +1 -1
- package/dist/__tests__/providers/gemini/GeminiCliAdapter.test.js +36 -0
- package/dist/__tests__/providers/gemini/GeminiCliAdapter.test.js.map +1 -1
- package/dist/__tests__/providers/grok/GrokAgentMapper.test.js +56 -0
- package/dist/__tests__/providers/grok/GrokAgentMapper.test.js.map +1 -0
- package/dist/__tests__/{adapters → providers/grok}/GrokCliAdapter.test.js +19 -6
- package/dist/__tests__/providers/grok/GrokCliAdapter.test.js.map +1 -0
- package/dist/__tests__/providers/grok/GrokSessionLocator.test.js +133 -0
- package/dist/__tests__/providers/grok/GrokSessionLocator.test.js.map +1 -0
- package/dist/__tests__/providers/opencode/OpenCodeAdapter.test.js +24 -0
- package/dist/__tests__/providers/opencode/OpenCodeAdapter.test.js.map +1 -1
- package/dist/__tests__/providers/pi/PiAdapter.test.js +30 -0
- package/dist/__tests__/providers/pi/PiAdapter.test.js.map +1 -1
- package/dist/__tests__/utils/session.test.js +18 -1
- package/dist/__tests__/utils/session.test.js.map +1 -1
- package/dist/adapters/AgentAdapter.d.ts +8 -0
- package/dist/adapters/AgentAdapter.d.ts.map +1 -1
- package/dist/adapters/AgentAdapter.js.map +1 -1
- package/dist/adapters/index.d.ts +1 -1
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +1 -1
- package/dist/adapters/index.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/providers/claude/ClaudeCodeAdapter.d.ts +2 -0
- package/dist/providers/claude/ClaudeCodeAdapter.d.ts.map +1 -1
- package/dist/providers/claude/ClaudeCodeAdapter.js +22 -19
- package/dist/providers/claude/ClaudeCodeAdapter.js.map +1 -1
- package/dist/providers/claude/ClaudeSessionLocator.d.ts +4 -0
- package/dist/providers/claude/ClaudeSessionLocator.d.ts.map +1 -1
- package/dist/providers/claude/ClaudeSessionLocator.js +18 -1
- package/dist/providers/claude/ClaudeSessionLocator.js.map +1 -1
- package/dist/providers/codex/CodexAdapter.d.ts +1 -0
- package/dist/providers/codex/CodexAdapter.d.ts.map +1 -1
- package/dist/providers/codex/CodexAdapter.js +8 -0
- package/dist/providers/codex/CodexAdapter.js.map +1 -1
- package/dist/providers/copilot/CopilotAdapter.d.ts +2 -0
- package/dist/providers/copilot/CopilotAdapter.d.ts.map +1 -1
- package/dist/providers/copilot/CopilotAdapter.js +24 -9
- package/dist/providers/copilot/CopilotAdapter.js.map +1 -1
- package/dist/providers/copilot/CopilotSessionLocator.d.ts +1 -0
- package/dist/providers/copilot/CopilotSessionLocator.d.ts.map +1 -1
- package/dist/providers/copilot/CopilotSessionLocator.js +9 -1
- package/dist/providers/copilot/CopilotSessionLocator.js.map +1 -1
- package/dist/providers/gemini/GeminiCliAdapter.d.ts +1 -0
- package/dist/providers/gemini/GeminiCliAdapter.d.ts.map +1 -1
- package/dist/providers/gemini/GeminiCliAdapter.js +9 -0
- package/dist/providers/gemini/GeminiCliAdapter.js.map +1 -1
- package/dist/providers/grok/GrokAgentMapper.d.ts +14 -0
- package/dist/providers/grok/GrokAgentMapper.d.ts.map +1 -0
- package/dist/providers/grok/GrokAgentMapper.js +36 -0
- package/dist/providers/grok/GrokAgentMapper.js.map +1 -0
- package/dist/providers/grok/GrokCliAdapter.d.ts +36 -0
- package/dist/providers/grok/GrokCliAdapter.d.ts.map +1 -0
- package/dist/providers/grok/GrokCliAdapter.js +98 -0
- package/dist/providers/grok/GrokCliAdapter.js.map +1 -0
- package/dist/providers/grok/GrokSessionLocator.d.ts +62 -0
- package/dist/providers/grok/GrokSessionLocator.d.ts.map +1 -0
- package/dist/providers/grok/GrokSessionLocator.js +140 -0
- package/dist/providers/grok/GrokSessionLocator.js.map +1 -0
- package/dist/providers/grok/GrokSessionParser.d.ts +54 -0
- package/dist/providers/grok/GrokSessionParser.d.ts.map +1 -0
- package/dist/providers/grok/GrokSessionParser.js +122 -0
- package/dist/providers/grok/GrokSessionParser.js.map +1 -0
- package/dist/providers/opencode/OpenCodeAdapter.d.ts +1 -0
- package/dist/providers/opencode/OpenCodeAdapter.d.ts.map +1 -1
- package/dist/providers/opencode/OpenCodeAdapter.js +3 -0
- package/dist/providers/opencode/OpenCodeAdapter.js.map +1 -1
- package/dist/providers/opencode/OpenCodeSessionLocator.d.ts +2 -0
- package/dist/providers/opencode/OpenCodeSessionLocator.d.ts.map +1 -1
- package/dist/providers/opencode/OpenCodeSessionLocator.js +31 -12
- package/dist/providers/opencode/OpenCodeSessionLocator.js.map +1 -1
- package/dist/providers/pi/PiAdapter.d.ts +1 -0
- package/dist/providers/pi/PiAdapter.d.ts.map +1 -1
- package/dist/providers/pi/PiAdapter.js +10 -0
- package/dist/providers/pi/PiAdapter.js.map +1 -1
- package/dist/providers/pi/PiSessionLocator.d.ts +1 -0
- package/dist/providers/pi/PiSessionLocator.d.ts.map +1 -1
- package/dist/providers/pi/PiSessionLocator.js +8 -1
- package/dist/providers/pi/PiSessionLocator.js.map +1 -1
- package/dist/utils/session.d.ts +2 -0
- package/dist/utils/session.d.ts.map +1 -1
- package/dist/utils/session.js +3 -0
- package/dist/utils/session.js.map +1 -1
- package/package.json +1 -1
- package/src/AgentManager.ts +34 -0
- package/src/__tests__/AgentManager.test.ts +60 -0
- package/src/__tests__/adapters/ClaudeCodeAdapter.test.ts +23 -0
- package/src/__tests__/adapters/CodexAdapter.test.ts +7 -0
- package/src/__tests__/providers/copilot/CopilotAdapter.test.ts +12 -0
- package/src/__tests__/providers/gemini/GeminiCliAdapter.test.ts +21 -0
- package/src/__tests__/providers/grok/GrokAgentMapper.test.ts +57 -0
- package/src/__tests__/{adapters → providers/grok}/GrokCliAdapter.test.ts +18 -9
- package/src/__tests__/providers/grok/GrokSessionLocator.test.ts +106 -0
- package/src/__tests__/providers/opencode/OpenCodeAdapter.test.ts +18 -0
- package/src/__tests__/providers/pi/PiAdapter.test.ts +15 -0
- package/src/__tests__/utils/session.test.ts +11 -1
- package/src/adapters/AgentAdapter.ts +9 -0
- package/src/adapters/index.ts +1 -1
- package/src/index.ts +1 -1
- package/src/providers/claude/ClaudeCodeAdapter.ts +28 -23
- package/src/providers/claude/ClaudeSessionLocator.ts +20 -0
- package/src/providers/codex/CodexAdapter.ts +8 -0
- package/src/providers/copilot/CopilotAdapter.ts +26 -10
- package/src/providers/copilot/CopilotSessionLocator.ts +7 -1
- package/src/providers/gemini/GeminiCliAdapter.ts +8 -0
- package/src/providers/grok/GrokAgentMapper.ts +41 -0
- package/src/providers/grok/GrokCliAdapter.ts +125 -0
- package/src/providers/grok/GrokSessionLocator.ts +179 -0
- package/src/providers/grok/GrokSessionParser.ts +166 -0
- package/src/providers/opencode/OpenCodeAdapter.ts +4 -0
- package/src/providers/opencode/OpenCodeSessionLocator.ts +43 -15
- package/src/providers/pi/PiAdapter.ts +12 -0
- package/src/providers/pi/PiSessionLocator.ts +16 -1
- package/src/utils/session.ts +11 -0
- package/dist/__tests__/adapters/GrokCliAdapter.test.js.map +0 -1
- package/dist/adapters/GrokCliAdapter.d.ts +0 -80
- package/dist/adapters/GrokCliAdapter.d.ts.map +0 -1
- package/dist/adapters/GrokCliAdapter.js +0 -310
- package/dist/adapters/GrokCliAdapter.js.map +0 -1
- package/src/adapters/GrokCliAdapter.ts +0 -408
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/providers/copilot/CopilotSessionLocator.ts"],"sourcesContent":["import * as path from \"path\";\nimport { isDirectory, safeReaddir } from \"../../utils/session.js\";\n\nexport interface CopilotLock {\n sessionDir: string;\n sessionId: string;\n pid: number;\n}\n\nexport interface CopilotSessionDir {\n sessionDir: string;\n sessionId: string;\n}\n\nexport interface CopilotSessionLocatorOptions {\n sessionStateDir?: string;\n}\n\nexport class CopilotSessionLocator {\n private readonly sessionStateDir: string;\n\n constructor(options: CopilotSessionLocatorOptions = {}) {\n const homeDir = process.env.HOME || process.env.USERPROFILE || \"\";\n this.sessionStateDir =\n options.sessionStateDir ?? path.join(homeDir, \".copilot\", \"session-state\");\n }\n\n listSessionDirs(): CopilotSessionDir[] {\n if (!isDirectory(this.sessionStateDir)) return [];\n\n const sessionDirs: CopilotSessionDir[] = [];\n for (const sessionId of safeReaddir(this.sessionStateDir)) {\n const sessionDir = path.join(this.sessionStateDir, sessionId);\n if (!isDirectory(sessionDir)) continue;\n\n sessionDirs.push({ sessionDir, sessionId });\n }\n\n return sessionDirs;\n }\n\n discoverActiveLocks(): CopilotLock[] {\n const locks: CopilotLock[] = [];\n for (const { sessionDir, sessionId } of this.listSessionDirs()) {\n for (const entry of safeReaddir(sessionDir)) {\n const match = entry.match(/^inuse\\.(\\d+)\\.lock$/);\n if (!match) continue;\n\n const pid = Number.parseInt(match[1], 10);\n if (!Number.isFinite(pid)) continue;\n locks.push({ sessionDir, sessionId, pid });\n }\n }\n\n return locks;\n }\n}\n"],"names":["path","isDirectory","safeReaddir","CopilotSessionLocator","sessionStateDir","options","homeDir","process","env","HOME","USERPROFILE","join","listSessionDirs","sessionDirs","sessionId","sessionDir","push","discoverActiveLocks","locks","entry","match","pid","Number","parseInt","isFinite"],"mappings":"AAAA,YAAYA,UAAU,OAAO;AAC7B,SAASC,WAAW,EAAEC,WAAW,QAAQ,yBAAyB;
|
|
1
|
+
{"version":3,"sources":["../../../src/providers/copilot/CopilotSessionLocator.ts"],"sourcesContent":["import * as path from \"path\";\nimport { isDirectory, isSafePathSegment, safeReaddir } from \"../../utils/session.js\";\n\nexport interface CopilotLock {\n sessionDir: string;\n sessionId: string;\n pid: number;\n}\n\nexport interface CopilotSessionDir {\n sessionDir: string;\n sessionId: string;\n}\n\nexport interface CopilotSessionLocatorOptions {\n sessionStateDir?: string;\n}\n\nexport class CopilotSessionLocator {\n private readonly sessionStateDir: string;\n\n constructor(options: CopilotSessionLocatorOptions = {}) {\n const homeDir = process.env.HOME || process.env.USERPROFILE || \"\";\n this.sessionStateDir =\n options.sessionStateDir ?? path.join(homeDir, \".copilot\", \"session-state\");\n }\n\n listSessionDirs(): CopilotSessionDir[] {\n if (!isDirectory(this.sessionStateDir)) return [];\n\n const sessionDirs: CopilotSessionDir[] = [];\n for (const sessionId of safeReaddir(this.sessionStateDir)) {\n const sessionDir = path.join(this.sessionStateDir, sessionId);\n if (!isDirectory(sessionDir)) continue;\n\n sessionDirs.push({ sessionDir, sessionId });\n }\n\n return sessionDirs;\n }\n\n findSessionDirById(sessionId: string): CopilotSessionDir | null {\n if (!isSafePathSegment(sessionId)) return null;\n const sessionDir = path.join(this.sessionStateDir, sessionId);\n return isDirectory(sessionDir) ? { sessionDir, sessionId } : null;\n }\n\n discoverActiveLocks(): CopilotLock[] {\n const locks: CopilotLock[] = [];\n for (const { sessionDir, sessionId } of this.listSessionDirs()) {\n for (const entry of safeReaddir(sessionDir)) {\n const match = entry.match(/^inuse\\.(\\d+)\\.lock$/);\n if (!match) continue;\n\n const pid = Number.parseInt(match[1], 10);\n if (!Number.isFinite(pid)) continue;\n locks.push({ sessionDir, sessionId, pid });\n }\n }\n\n return locks;\n }\n}\n"],"names":["path","isDirectory","isSafePathSegment","safeReaddir","CopilotSessionLocator","sessionStateDir","options","homeDir","process","env","HOME","USERPROFILE","join","listSessionDirs","sessionDirs","sessionId","sessionDir","push","findSessionDirById","discoverActiveLocks","locks","entry","match","pid","Number","parseInt","isFinite"],"mappings":"AAAA,YAAYA,UAAU,OAAO;AAC7B,SAASC,WAAW,EAAEC,iBAAiB,EAAEC,WAAW,QAAQ,yBAAyB;AAiBrF,OAAO,MAAMC;IACMC,gBAAwB;IAEzC,YAAYC,UAAwC,CAAC,CAAC,CAAE;QACtD,MAAMC,UAAUC,QAAQC,GAAG,CAACC,IAAI,IAAIF,QAAQC,GAAG,CAACE,WAAW,IAAI;QAC/D,IAAI,CAACN,eAAe,GAClBC,QAAQD,eAAe,IAAIL,KAAKY,IAAI,CAACL,SAAS,YAAY;IAC9D;IAEAM,kBAAuC;QACrC,IAAI,CAACZ,YAAY,IAAI,CAACI,eAAe,GAAG,OAAO,EAAE;QAEjD,MAAMS,cAAmC,EAAE;QAC3C,KAAK,MAAMC,aAAaZ,YAAY,IAAI,CAACE,eAAe,EAAG;YACzD,MAAMW,aAAahB,KAAKY,IAAI,CAAC,IAAI,CAACP,eAAe,EAAEU;YACnD,IAAI,CAACd,YAAYe,aAAa;YAE9BF,YAAYG,IAAI,CAAC;gBAAED;gBAAYD;YAAU;QAC3C;QAEA,OAAOD;IACT;IAEAI,mBAAmBH,SAAiB,EAA4B;QAC9D,IAAI,CAACb,kBAAkBa,YAAY,OAAO;QAC1C,MAAMC,aAAahB,KAAKY,IAAI,CAAC,IAAI,CAACP,eAAe,EAAEU;QACnD,OAAOd,YAAYe,cAAc;YAAEA;YAAYD;QAAU,IAAI;IAC/D;IAEAI,sBAAqC;QACnC,MAAMC,QAAuB,EAAE;QAC/B,KAAK,MAAM,EAAEJ,UAAU,EAAED,SAAS,EAAE,IAAI,IAAI,CAACF,eAAe,GAAI;YAC9D,KAAK,MAAMQ,SAASlB,YAAYa,YAAa;gBAC3C,MAAMM,QAAQD,MAAMC,KAAK,CAAC;gBAC1B,IAAI,CAACA,OAAO;gBAEZ,MAAMC,MAAMC,OAAOC,QAAQ,CAACH,KAAK,CAAC,EAAE,EAAE;gBACtC,IAAI,CAACE,OAAOE,QAAQ,CAACH,MAAM;gBAC3BH,MAAMH,IAAI,CAAC;oBAAED;oBAAYD;oBAAWQ;gBAAI;YAC1C;QACF;QAEA,OAAOH;IACT;AACF"}
|
|
@@ -40,6 +40,7 @@ export declare class GeminiCliAdapter implements AgentAdapter {
|
|
|
40
40
|
verbose?: boolean;
|
|
41
41
|
}): ConversationMessage[];
|
|
42
42
|
listSessions(opts?: ListSessionsOptions): Promise<SessionSummary[]>;
|
|
43
|
+
findSessionsById(sessionId: string): Promise<SessionSummary[]>;
|
|
43
44
|
private getGeminiProcesses;
|
|
44
45
|
private mapSessionDiscovery;
|
|
45
46
|
private mapProcessOnlyAgents;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GeminiCliAdapter.d.ts","sourceRoot":"","sources":["../../../src/providers/gemini/GeminiCliAdapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,SAAS,EACT,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,gCAAgC,CAAC;AASxC,OAAO,EAAE,aAAa,EAAsB,MAAM,8BAA8B,CAAC;AAKjF,MAAM,WAAW,uBAAuB;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,gBAAiB,YAAW,YAAY;IACnD,QAAQ,CAAC,IAAI,EAAG,YAAY,CAAU;IACtC,QAAQ,CAAC,YAAY,oBAAqB;IAE1C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAC3C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;gBAG7C,QAAQ,GAAE,aAAuC,EACjD,OAAO,GAAE,uBAA4B;IAQvC,SAAS,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO;IAI5C;;;;;;;;;;;OAWG;IACG,YAAY,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAoBzE,eAAe,CAAC,eAAe,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,mBAAmB,EAAE;IAI1F,YAAY,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"GeminiCliAdapter.d.ts","sourceRoot":"","sources":["../../../src/providers/gemini/GeminiCliAdapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,SAAS,EACT,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,gCAAgC,CAAC;AASxC,OAAO,EAAE,aAAa,EAAsB,MAAM,8BAA8B,CAAC;AAKjF,MAAM,WAAW,uBAAuB;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,gBAAiB,YAAW,YAAY;IACnD,QAAQ,CAAC,IAAI,EAAG,YAAY,CAAU;IACtC,QAAQ,CAAC,YAAY,oBAAqB;IAE1C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAC3C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;gBAG7C,QAAQ,GAAE,aAAuC,EACjD,OAAO,GAAE,uBAA4B;IAQvC,SAAS,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO;IAI5C;;;;;;;;;;;OAWG;IACG,YAAY,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAoBzE,eAAe,CAAC,eAAe,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,mBAAmB,EAAE;IAI1F,YAAY,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAanE,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;YAQtD,kBAAkB;IAMhC,OAAO,CAAC,mBAAmB;IAwC3B,OAAO,CAAC,oBAAoB;IAY5B,OAAO,CAAC,wBAAwB;IAahC,OAAO,CAAC,wBAAwB;IA4BhC,OAAO,CAAC,uBAAuB;IAQ/B,OAAO,CAAC,gBAAgB;IAmCxB,OAAO,CAAC,kBAAkB;CAa3B"}
|
|
@@ -74,6 +74,15 @@ export class GeminiCliAdapter {
|
|
|
74
74
|
}
|
|
75
75
|
return summaries;
|
|
76
76
|
}
|
|
77
|
+
async findSessionsById(sessionId) {
|
|
78
|
+
for (const filePath of this.locator.discoverHistoricalSessionFiles()){
|
|
79
|
+
const summary = this.parser.fileToSessionSummary(filePath);
|
|
80
|
+
if (summary?.sessionId === sessionId) return [
|
|
81
|
+
summary
|
|
82
|
+
];
|
|
83
|
+
}
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
77
86
|
async getGeminiProcesses(context) {
|
|
78
87
|
const snapshot = context?.processes ?? await captureProcessSnapshot(this.processNames);
|
|
79
88
|
const relevant = filterByProcessNames(snapshot, this.processNames);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/providers/gemini/GeminiCliAdapter.ts"],"sourcesContent":["/**\n * Gemini CLI Adapter\n *\n * Detects running Gemini CLI agents by:\n * 1. Filtering Gemini Node processes from a shared asynchronous process snapshot\n * 2. Using snapshot CWD and start-time enrichment\n * 3. Discovering session files from ~/.gemini/tmp/<shortId>/chats/session-*.json\n * 4. Matching sessions to processes via shared matchProcessesToSessions()\n * using sha256(cwd) === session.projectHash as the resolvedCwd source\n * 5. Extracting summary from the most recent user message in the session JSON\n */\n\nimport type {\n AgentAdapter,\n AgentInfo,\n ProcessInfo,\n ConversationMessage,\n SessionSummary,\n ListSessionsOptions,\n AgentDetectionContext,\n} from \"../../adapters/AgentAdapter.js\";\nimport {\n captureProcessSnapshot,\n executableBasename,\n filterByProcessNames,\n findWrapperProcess,\n findWrapperProcessPids,\n} from \"../../utils/process.js\";\nimport { matchProcessesToSessions } from \"../../utils/matching.js\";\nimport { AgentRegistry, type RegistryEntry } from \"../../utils/AgentRegistry.js\";\nimport { GeminiAgentMapper } from \"./GeminiAgentMapper.js\";\nimport { GeminiSessionLocator, type GeminiSessionDiscovery } from \"./GeminiSessionLocator.js\";\nimport { GeminiSessionParser } from \"./GeminiSessionParser.js\";\n\nexport interface GeminiCliAdapterOptions {\n geminiTmpDir?: string;\n}\n\nexport class GeminiCliAdapter implements AgentAdapter {\n readonly type = \"gemini_cli\" as const;\n readonly processNames = [\"node\"] as const;\n\n private readonly registry: AgentRegistry;\n private readonly parser: GeminiSessionParser;\n private readonly mapper: GeminiAgentMapper;\n private readonly locator: GeminiSessionLocator;\n\n constructor(\n registry: AgentRegistry = AgentRegistry.default(),\n options: GeminiCliAdapterOptions = {},\n ) {\n this.registry = registry;\n this.parser = new GeminiSessionParser();\n this.mapper = new GeminiAgentMapper(this.parser);\n this.locator = new GeminiSessionLocator(options);\n }\n\n canHandle(processInfo: ProcessInfo): boolean {\n return this.isGeminiExecutable(processInfo.command);\n }\n\n /**\n * Detect running Gemini CLI agents.\n *\n * Gemini CLI ships as a Node script (`bundle/gemini.js` with shebang\n * `#!/usr/bin/env node`) — unlike Claude Code (native binary per\n * platform) or Codex CLI (Node wrapper that execs a native Rust\n * binary). The primary running process is therefore the Node runtime\n * itself, and `ps aux` lists it as `node /path/to/gemini ...` with\n * argv[0] = `node`. We scan the Node process pool via the shared\n * snapshot and keep only those whose command line references the gemini\n * executable or script via isGeminiExecutable().\n */\n async detectAgents(context?: AgentDetectionContext): Promise<AgentInfo[]> {\n const processes = await this.getGeminiProcesses(context);\n if (processes.length === 0) return [];\n\n const wrapperPids = findWrapperProcessPids(processes);\n const { cachedAgents, remaining } = this.tryRegistryCache(processes, wrapperPids);\n if (remaining.length === 0) return this.deduplicateSessionAgents(cachedAgents);\n\n const candidateProcesses = remaining.filter((proc) => !wrapperPids.has(proc.pid));\n const registryEntriesByPid = new Map(this.registry.list().map((entry) => [entry.pid, entry]));\n const discovery = this.locator.discoverSessions(candidateProcesses);\n\n const agents =\n discovery.sessions.length === 0\n ? this.mapProcessOnlyAgents(candidateProcesses, processes, registryEntriesByPid)\n : this.mapSessionDiscovery(discovery, candidateProcesses, processes, registryEntriesByPid);\n\n return this.deduplicateSessionAgents([...cachedAgents, ...agents]);\n }\n\n getConversation(sessionFilePath: string, options?: { verbose?: boolean }): ConversationMessage[] {\n return this.parser.getConversation(sessionFilePath, options);\n }\n\n async listSessions(opts?: ListSessionsOptions): Promise<SessionSummary[]> {\n const summaries: SessionSummary[] = [];\n\n for (const filePath of this.locator.discoverHistoricalSessionFiles()) {\n const summary = this.parser.fileToSessionSummary(filePath);\n if (!summary) continue;\n if (opts?.cwd !== undefined && summary.cwd !== opts.cwd) continue;\n summaries.push(summary);\n }\n\n return summaries;\n }\n\n private async getGeminiProcesses(context?: AgentDetectionContext): Promise<ProcessInfo[]> {\n const snapshot = context?.processes ?? (await captureProcessSnapshot(this.processNames));\n const relevant = filterByProcessNames(snapshot, this.processNames);\n return relevant.filter((process) => this.canHandle(process));\n }\n\n private mapSessionDiscovery(\n discovery: GeminiSessionDiscovery,\n candidateProcesses: ProcessInfo[],\n allProcesses: ProcessInfo[],\n registryEntriesByPid: Map<number, RegistryEntry>,\n ): AgentInfo[] {\n const matches = matchProcessesToSessions(candidateProcesses, discovery.sessions);\n const matchedPids = new Set(matches.map((m) => m.process.pid));\n const matchedProcesses: ProcessInfo[] = [];\n const agents: AgentInfo[] = [];\n\n for (const match of matches) {\n const cachedContent = discovery.contentCache.get(match.session.filePath);\n const sessionData = this.parser.parseSession(cachedContent, match.session.filePath);\n if (!sessionData) {\n matchedPids.delete(match.process.pid);\n continue;\n }\n\n const agent = this.mapper.mapSessionToAgent(\n sessionData,\n match.process,\n match.session.filePath,\n );\n this.applyWrapperRegistryName(agent, match.process, allProcesses, registryEntriesByPid);\n agents.push(agent);\n matchedProcesses.push(match.process);\n }\n\n const matchedWrapperPids = findWrapperProcessPids(candidateProcesses, matchedProcesses);\n const unmatchedProcesses = candidateProcesses.filter(\n (process) => !matchedPids.has(process.pid) && !matchedWrapperPids.has(process.pid),\n );\n\n agents.push(\n ...this.mapProcessOnlyAgents(unmatchedProcesses, allProcesses, registryEntriesByPid),\n );\n return agents;\n }\n\n private mapProcessOnlyAgents(\n processes: ProcessInfo[],\n allProcesses: ProcessInfo[],\n registryEntriesByPid: Map<number, RegistryEntry>,\n ): AgentInfo[] {\n return processes.map((processInfo) => {\n const agent = this.mapper.mapProcessOnlyAgent(processInfo);\n this.applyWrapperRegistryName(agent, processInfo, allProcesses, registryEntriesByPid);\n return agent;\n });\n }\n\n private applyWrapperRegistryName(\n agent: AgentInfo,\n processInfo: ProcessInfo,\n processes: ProcessInfo[],\n registryEntriesByPid: Map<number, RegistryEntry>,\n ): void {\n const wrapper = findWrapperProcess(processes, processInfo);\n const wrapperEntry = wrapper ? registryEntriesByPid.get(wrapper.pid) : undefined;\n if (wrapperEntry?.type === this.type) {\n agent.name = wrapperEntry.name;\n }\n }\n\n private deduplicateSessionAgents(agents: AgentInfo[]): AgentInfo[] {\n const bySession = new Map<string, AgentInfo>();\n const result: AgentInfo[] = [];\n\n for (const agent of agents) {\n const sessionKey = this.sessionDeduplicationKey(agent);\n if (!sessionKey) {\n result.push(agent);\n continue;\n }\n\n const existing = bySession.get(sessionKey);\n if (!existing) {\n bySession.set(sessionKey, agent);\n result.push(agent);\n continue;\n }\n\n if (agent.pid > existing.pid) {\n bySession.set(sessionKey, agent);\n const index = result.indexOf(existing);\n if (index >= 0) result[index] = agent;\n }\n }\n\n return result;\n }\n\n private sessionDeduplicationKey(agent: AgentInfo): string | null {\n if (agent.sessionFilePath) return `file:${agent.sessionFilePath}`;\n if (agent.sessionId && !agent.sessionId.startsWith(\"pid-\")) {\n return `session:${agent.sessionId}`;\n }\n return null;\n }\n\n private tryRegistryCache(\n processes: ProcessInfo[],\n wrapperPids: Set<number>,\n ): {\n cachedAgents: AgentInfo[];\n remaining: ProcessInfo[];\n } {\n const cachedAgents: AgentInfo[] = [];\n const remaining: ProcessInfo[] = [];\n const byPid = new Map(this.registry.list().map((e) => [e.pid, e]));\n\n for (const proc of processes) {\n const entry = byPid.get(proc.pid);\n if (\n wrapperPids.has(proc.pid) ||\n !entry ||\n entry.type !== this.type ||\n !entry.sessionFilePath\n ) {\n remaining.push(proc);\n continue;\n }\n\n const sessionData = this.parser.parseSession(undefined, entry.sessionFilePath);\n if (!sessionData) {\n remaining.push(proc);\n continue;\n }\n\n cachedAgents.push(this.mapper.mapSessionToAgent(sessionData, proc, entry.sessionFilePath));\n }\n\n return { cachedAgents, remaining };\n }\n\n private isGeminiExecutable(command: string): boolean {\n // Accept any token in the command line whose basename matches a\n // known gemini entrypoint. This is intentionally broader than the\n // other adapters' argv[0]-only check because the Node-script\n // distribution puts the real gemini path in argv[1..], not argv[0].\n for (const token of command.trim().split(/\\s+/)) {\n const base = executableBasename(token);\n if (base === \"gemini\" || base === \"gemini.exe\" || base === \"gemini.js\") {\n return true;\n }\n }\n return false;\n }\n}\n"],"names":["captureProcessSnapshot","executableBasename","filterByProcessNames","findWrapperProcess","findWrapperProcessPids","matchProcessesToSessions","AgentRegistry","GeminiAgentMapper","GeminiSessionLocator","GeminiSessionParser","GeminiCliAdapter","type","processNames","registry","parser","mapper","locator","default","options","canHandle","processInfo","isGeminiExecutable","command","detectAgents","context","processes","getGeminiProcesses","length","wrapperPids","cachedAgents","remaining","tryRegistryCache","deduplicateSessionAgents","candidateProcesses","filter","proc","has","pid","registryEntriesByPid","Map","list","map","entry","discovery","discoverSessions","agents","sessions","mapProcessOnlyAgents","mapSessionDiscovery","getConversation","sessionFilePath","listSessions","opts","summaries","filePath","discoverHistoricalSessionFiles","summary","fileToSessionSummary","cwd","undefined","push","snapshot","relevant","process","allProcesses","matches","matchedPids","Set","m","matchedProcesses","match","cachedContent","contentCache","get","session","sessionData","parseSession","delete","agent","mapSessionToAgent","applyWrapperRegistryName","matchedWrapperPids","unmatchedProcesses","mapProcessOnlyAgent","wrapper","wrapperEntry","name","bySession","result","sessionKey","sessionDeduplicationKey","existing","set","index","indexOf","sessionId","startsWith","byPid","e","token","trim","split","base"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/providers/gemini/GeminiCliAdapter.ts"],"sourcesContent":["/**\n * Gemini CLI Adapter\n *\n * Detects running Gemini CLI agents by:\n * 1. Filtering Gemini Node processes from a shared asynchronous process snapshot\n * 2. Using snapshot CWD and start-time enrichment\n * 3. Discovering session files from ~/.gemini/tmp/<shortId>/chats/session-*.json\n * 4. Matching sessions to processes via shared matchProcessesToSessions()\n * using sha256(cwd) === session.projectHash as the resolvedCwd source\n * 5. Extracting summary from the most recent user message in the session JSON\n */\n\nimport type {\n AgentAdapter,\n AgentInfo,\n ProcessInfo,\n ConversationMessage,\n SessionSummary,\n ListSessionsOptions,\n AgentDetectionContext,\n} from \"../../adapters/AgentAdapter.js\";\nimport {\n captureProcessSnapshot,\n executableBasename,\n filterByProcessNames,\n findWrapperProcess,\n findWrapperProcessPids,\n} from \"../../utils/process.js\";\nimport { matchProcessesToSessions } from \"../../utils/matching.js\";\nimport { AgentRegistry, type RegistryEntry } from \"../../utils/AgentRegistry.js\";\nimport { GeminiAgentMapper } from \"./GeminiAgentMapper.js\";\nimport { GeminiSessionLocator, type GeminiSessionDiscovery } from \"./GeminiSessionLocator.js\";\nimport { GeminiSessionParser } from \"./GeminiSessionParser.js\";\n\nexport interface GeminiCliAdapterOptions {\n geminiTmpDir?: string;\n}\n\nexport class GeminiCliAdapter implements AgentAdapter {\n readonly type = \"gemini_cli\" as const;\n readonly processNames = [\"node\"] as const;\n\n private readonly registry: AgentRegistry;\n private readonly parser: GeminiSessionParser;\n private readonly mapper: GeminiAgentMapper;\n private readonly locator: GeminiSessionLocator;\n\n constructor(\n registry: AgentRegistry = AgentRegistry.default(),\n options: GeminiCliAdapterOptions = {},\n ) {\n this.registry = registry;\n this.parser = new GeminiSessionParser();\n this.mapper = new GeminiAgentMapper(this.parser);\n this.locator = new GeminiSessionLocator(options);\n }\n\n canHandle(processInfo: ProcessInfo): boolean {\n return this.isGeminiExecutable(processInfo.command);\n }\n\n /**\n * Detect running Gemini CLI agents.\n *\n * Gemini CLI ships as a Node script (`bundle/gemini.js` with shebang\n * `#!/usr/bin/env node`) — unlike Claude Code (native binary per\n * platform) or Codex CLI (Node wrapper that execs a native Rust\n * binary). The primary running process is therefore the Node runtime\n * itself, and `ps aux` lists it as `node /path/to/gemini ...` with\n * argv[0] = `node`. We scan the Node process pool via the shared\n * snapshot and keep only those whose command line references the gemini\n * executable or script via isGeminiExecutable().\n */\n async detectAgents(context?: AgentDetectionContext): Promise<AgentInfo[]> {\n const processes = await this.getGeminiProcesses(context);\n if (processes.length === 0) return [];\n\n const wrapperPids = findWrapperProcessPids(processes);\n const { cachedAgents, remaining } = this.tryRegistryCache(processes, wrapperPids);\n if (remaining.length === 0) return this.deduplicateSessionAgents(cachedAgents);\n\n const candidateProcesses = remaining.filter((proc) => !wrapperPids.has(proc.pid));\n const registryEntriesByPid = new Map(this.registry.list().map((entry) => [entry.pid, entry]));\n const discovery = this.locator.discoverSessions(candidateProcesses);\n\n const agents =\n discovery.sessions.length === 0\n ? this.mapProcessOnlyAgents(candidateProcesses, processes, registryEntriesByPid)\n : this.mapSessionDiscovery(discovery, candidateProcesses, processes, registryEntriesByPid);\n\n return this.deduplicateSessionAgents([...cachedAgents, ...agents]);\n }\n\n getConversation(sessionFilePath: string, options?: { verbose?: boolean }): ConversationMessage[] {\n return this.parser.getConversation(sessionFilePath, options);\n }\n\n async listSessions(opts?: ListSessionsOptions): Promise<SessionSummary[]> {\n const summaries: SessionSummary[] = [];\n\n for (const filePath of this.locator.discoverHistoricalSessionFiles()) {\n const summary = this.parser.fileToSessionSummary(filePath);\n if (!summary) continue;\n if (opts?.cwd !== undefined && summary.cwd !== opts.cwd) continue;\n summaries.push(summary);\n }\n\n return summaries;\n }\n\n async findSessionsById(sessionId: string): Promise<SessionSummary[]> {\n for (const filePath of this.locator.discoverHistoricalSessionFiles()) {\n const summary = this.parser.fileToSessionSummary(filePath);\n if (summary?.sessionId === sessionId) return [summary];\n }\n return [];\n }\n\n private async getGeminiProcesses(context?: AgentDetectionContext): Promise<ProcessInfo[]> {\n const snapshot = context?.processes ?? (await captureProcessSnapshot(this.processNames));\n const relevant = filterByProcessNames(snapshot, this.processNames);\n return relevant.filter((process) => this.canHandle(process));\n }\n\n private mapSessionDiscovery(\n discovery: GeminiSessionDiscovery,\n candidateProcesses: ProcessInfo[],\n allProcesses: ProcessInfo[],\n registryEntriesByPid: Map<number, RegistryEntry>,\n ): AgentInfo[] {\n const matches = matchProcessesToSessions(candidateProcesses, discovery.sessions);\n const matchedPids = new Set(matches.map((m) => m.process.pid));\n const matchedProcesses: ProcessInfo[] = [];\n const agents: AgentInfo[] = [];\n\n for (const match of matches) {\n const cachedContent = discovery.contentCache.get(match.session.filePath);\n const sessionData = this.parser.parseSession(cachedContent, match.session.filePath);\n if (!sessionData) {\n matchedPids.delete(match.process.pid);\n continue;\n }\n\n const agent = this.mapper.mapSessionToAgent(\n sessionData,\n match.process,\n match.session.filePath,\n );\n this.applyWrapperRegistryName(agent, match.process, allProcesses, registryEntriesByPid);\n agents.push(agent);\n matchedProcesses.push(match.process);\n }\n\n const matchedWrapperPids = findWrapperProcessPids(candidateProcesses, matchedProcesses);\n const unmatchedProcesses = candidateProcesses.filter(\n (process) => !matchedPids.has(process.pid) && !matchedWrapperPids.has(process.pid),\n );\n\n agents.push(\n ...this.mapProcessOnlyAgents(unmatchedProcesses, allProcesses, registryEntriesByPid),\n );\n return agents;\n }\n\n private mapProcessOnlyAgents(\n processes: ProcessInfo[],\n allProcesses: ProcessInfo[],\n registryEntriesByPid: Map<number, RegistryEntry>,\n ): AgentInfo[] {\n return processes.map((processInfo) => {\n const agent = this.mapper.mapProcessOnlyAgent(processInfo);\n this.applyWrapperRegistryName(agent, processInfo, allProcesses, registryEntriesByPid);\n return agent;\n });\n }\n\n private applyWrapperRegistryName(\n agent: AgentInfo,\n processInfo: ProcessInfo,\n processes: ProcessInfo[],\n registryEntriesByPid: Map<number, RegistryEntry>,\n ): void {\n const wrapper = findWrapperProcess(processes, processInfo);\n const wrapperEntry = wrapper ? registryEntriesByPid.get(wrapper.pid) : undefined;\n if (wrapperEntry?.type === this.type) {\n agent.name = wrapperEntry.name;\n }\n }\n\n private deduplicateSessionAgents(agents: AgentInfo[]): AgentInfo[] {\n const bySession = new Map<string, AgentInfo>();\n const result: AgentInfo[] = [];\n\n for (const agent of agents) {\n const sessionKey = this.sessionDeduplicationKey(agent);\n if (!sessionKey) {\n result.push(agent);\n continue;\n }\n\n const existing = bySession.get(sessionKey);\n if (!existing) {\n bySession.set(sessionKey, agent);\n result.push(agent);\n continue;\n }\n\n if (agent.pid > existing.pid) {\n bySession.set(sessionKey, agent);\n const index = result.indexOf(existing);\n if (index >= 0) result[index] = agent;\n }\n }\n\n return result;\n }\n\n private sessionDeduplicationKey(agent: AgentInfo): string | null {\n if (agent.sessionFilePath) return `file:${agent.sessionFilePath}`;\n if (agent.sessionId && !agent.sessionId.startsWith(\"pid-\")) {\n return `session:${agent.sessionId}`;\n }\n return null;\n }\n\n private tryRegistryCache(\n processes: ProcessInfo[],\n wrapperPids: Set<number>,\n ): {\n cachedAgents: AgentInfo[];\n remaining: ProcessInfo[];\n } {\n const cachedAgents: AgentInfo[] = [];\n const remaining: ProcessInfo[] = [];\n const byPid = new Map(this.registry.list().map((e) => [e.pid, e]));\n\n for (const proc of processes) {\n const entry = byPid.get(proc.pid);\n if (\n wrapperPids.has(proc.pid) ||\n !entry ||\n entry.type !== this.type ||\n !entry.sessionFilePath\n ) {\n remaining.push(proc);\n continue;\n }\n\n const sessionData = this.parser.parseSession(undefined, entry.sessionFilePath);\n if (!sessionData) {\n remaining.push(proc);\n continue;\n }\n\n cachedAgents.push(this.mapper.mapSessionToAgent(sessionData, proc, entry.sessionFilePath));\n }\n\n return { cachedAgents, remaining };\n }\n\n private isGeminiExecutable(command: string): boolean {\n // Accept any token in the command line whose basename matches a\n // known gemini entrypoint. This is intentionally broader than the\n // other adapters' argv[0]-only check because the Node-script\n // distribution puts the real gemini path in argv[1..], not argv[0].\n for (const token of command.trim().split(/\\s+/)) {\n const base = executableBasename(token);\n if (base === \"gemini\" || base === \"gemini.exe\" || base === \"gemini.js\") {\n return true;\n }\n }\n return false;\n }\n}\n"],"names":["captureProcessSnapshot","executableBasename","filterByProcessNames","findWrapperProcess","findWrapperProcessPids","matchProcessesToSessions","AgentRegistry","GeminiAgentMapper","GeminiSessionLocator","GeminiSessionParser","GeminiCliAdapter","type","processNames","registry","parser","mapper","locator","default","options","canHandle","processInfo","isGeminiExecutable","command","detectAgents","context","processes","getGeminiProcesses","length","wrapperPids","cachedAgents","remaining","tryRegistryCache","deduplicateSessionAgents","candidateProcesses","filter","proc","has","pid","registryEntriesByPid","Map","list","map","entry","discovery","discoverSessions","agents","sessions","mapProcessOnlyAgents","mapSessionDiscovery","getConversation","sessionFilePath","listSessions","opts","summaries","filePath","discoverHistoricalSessionFiles","summary","fileToSessionSummary","cwd","undefined","push","findSessionsById","sessionId","snapshot","relevant","process","allProcesses","matches","matchedPids","Set","m","matchedProcesses","match","cachedContent","contentCache","get","session","sessionData","parseSession","delete","agent","mapSessionToAgent","applyWrapperRegistryName","matchedWrapperPids","unmatchedProcesses","mapProcessOnlyAgent","wrapper","wrapperEntry","name","bySession","result","sessionKey","sessionDeduplicationKey","existing","set","index","indexOf","startsWith","byPid","e","token","trim","split","base"],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AgentInfo, ProcessInfo } from "../../adapters/AgentAdapter.js";
|
|
2
|
+
import { GrokSessionParser, type GrokSession } from "./GrokSessionParser.js";
|
|
3
|
+
export interface GrokSessionAgentInput {
|
|
4
|
+
session: GrokSession;
|
|
5
|
+
processInfo: ProcessInfo;
|
|
6
|
+
}
|
|
7
|
+
export declare class GrokAgentMapper {
|
|
8
|
+
private readonly parser;
|
|
9
|
+
constructor(parser?: GrokSessionParser);
|
|
10
|
+
mapSessionToAgent({ session, processInfo }: GrokSessionAgentInput): AgentInfo;
|
|
11
|
+
/** `cwd` is already resolved by the locator (registry first, then process cwd). */
|
|
12
|
+
mapProcessOnlyAgent(processInfo: ProcessInfo, cwd: string): AgentInfo;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=GrokAgentMapper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GrokAgentMapper.d.ts","sourceRoot":"","sources":["../../../src/providers/grok/GrokAgentMapper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAG7E,OAAO,EAAE,iBAAiB,EAAE,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAE7E,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,WAAW,CAAC;IACrB,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,GAAE,iBAA2C;IAEhF,iBAAiB,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,qBAAqB,GAAG,SAAS;IAc7E,mFAAmF;IACnF,mBAAmB,CAAC,WAAW,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS;CAYtE"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { AgentStatus } from "../../adapters/AgentAdapter.js";
|
|
2
|
+
import { generateAgentName } from "../../utils/matching.js";
|
|
3
|
+
import { GrokSessionParser } from "./GrokSessionParser.js";
|
|
4
|
+
export class GrokAgentMapper {
|
|
5
|
+
parser;
|
|
6
|
+
constructor(parser = new GrokSessionParser()){
|
|
7
|
+
this.parser = parser;
|
|
8
|
+
}
|
|
9
|
+
mapSessionToAgent({ session, processInfo }) {
|
|
10
|
+
return {
|
|
11
|
+
name: generateAgentName(session.projectPath, processInfo.pid),
|
|
12
|
+
type: "grok_cli",
|
|
13
|
+
status: this.parser.determineStatus(session),
|
|
14
|
+
summary: session.lastUserMessage || "Grok CLI session active",
|
|
15
|
+
pid: processInfo.pid,
|
|
16
|
+
projectPath: session.projectPath,
|
|
17
|
+
sessionId: session.sessionId,
|
|
18
|
+
lastActive: session.lastActive,
|
|
19
|
+
sessionFilePath: session.sessionFilePath
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/** `cwd` is already resolved by the locator (registry first, then process cwd). */ mapProcessOnlyAgent(processInfo, cwd) {
|
|
23
|
+
return {
|
|
24
|
+
name: generateAgentName(cwd, processInfo.pid),
|
|
25
|
+
type: "grok_cli",
|
|
26
|
+
status: AgentStatus.RUNNING,
|
|
27
|
+
summary: "Grok CLI process running",
|
|
28
|
+
pid: processInfo.pid,
|
|
29
|
+
projectPath: cwd,
|
|
30
|
+
sessionId: `pid-${processInfo.pid}`,
|
|
31
|
+
lastActive: new Date()
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
//# sourceMappingURL=GrokAgentMapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/providers/grok/GrokAgentMapper.ts"],"sourcesContent":["import type { AgentInfo, ProcessInfo } from \"../../adapters/AgentAdapter.js\";\nimport { AgentStatus } from \"../../adapters/AgentAdapter.js\";\nimport { generateAgentName } from \"../../utils/matching.js\";\nimport { GrokSessionParser, type GrokSession } from \"./GrokSessionParser.js\";\n\nexport interface GrokSessionAgentInput {\n session: GrokSession;\n processInfo: ProcessInfo;\n}\n\nexport class GrokAgentMapper {\n constructor(private readonly parser: GrokSessionParser = new GrokSessionParser()) {}\n\n mapSessionToAgent({ session, processInfo }: GrokSessionAgentInput): AgentInfo {\n return {\n name: generateAgentName(session.projectPath, processInfo.pid),\n type: \"grok_cli\",\n status: this.parser.determineStatus(session),\n summary: session.lastUserMessage || \"Grok CLI session active\",\n pid: processInfo.pid,\n projectPath: session.projectPath,\n sessionId: session.sessionId,\n lastActive: session.lastActive,\n sessionFilePath: session.sessionFilePath,\n };\n }\n\n /** `cwd` is already resolved by the locator (registry first, then process cwd). */\n mapProcessOnlyAgent(processInfo: ProcessInfo, cwd: string): AgentInfo {\n return {\n name: generateAgentName(cwd, processInfo.pid),\n type: \"grok_cli\",\n status: AgentStatus.RUNNING,\n summary: \"Grok CLI process running\",\n pid: processInfo.pid,\n projectPath: cwd,\n sessionId: `pid-${processInfo.pid}`,\n lastActive: new Date(),\n };\n }\n}\n"],"names":["AgentStatus","generateAgentName","GrokSessionParser","GrokAgentMapper","parser","mapSessionToAgent","session","processInfo","name","projectPath","pid","type","status","determineStatus","summary","lastUserMessage","sessionId","lastActive","sessionFilePath","mapProcessOnlyAgent","cwd","RUNNING","Date"],"mappings":"AACA,SAASA,WAAW,QAAQ,iCAAiC;AAC7D,SAASC,iBAAiB,QAAQ,0BAA0B;AAC5D,SAASC,iBAAiB,QAA0B,yBAAyB;AAO7E,OAAO,MAAMC;;IACX,YAAY,AAAiBC,SAA4B,IAAIF,mBAAmB,CAAE;aAArDE,SAAAA;IAAsD;IAEnFC,kBAAkB,EAAEC,OAAO,EAAEC,WAAW,EAAyB,EAAa;QAC5E,OAAO;YACLC,MAAMP,kBAAkBK,QAAQG,WAAW,EAAEF,YAAYG,GAAG;YAC5DC,MAAM;YACNC,QAAQ,IAAI,CAACR,MAAM,CAACS,eAAe,CAACP;YACpCQ,SAASR,QAAQS,eAAe,IAAI;YACpCL,KAAKH,YAAYG,GAAG;YACpBD,aAAaH,QAAQG,WAAW;YAChCO,WAAWV,QAAQU,SAAS;YAC5BC,YAAYX,QAAQW,UAAU;YAC9BC,iBAAiBZ,QAAQY,eAAe;QAC1C;IACF;IAEA,iFAAiF,GACjFC,oBAAoBZ,WAAwB,EAAEa,GAAW,EAAa;QACpE,OAAO;YACLZ,MAAMP,kBAAkBmB,KAAKb,YAAYG,GAAG;YAC5CC,MAAM;YACNC,QAAQZ,YAAYqB,OAAO;YAC3BP,SAAS;YACTJ,KAAKH,YAAYG,GAAG;YACpBD,aAAaW;YACbJ,WAAW,CAAC,IAAI,EAAET,YAAYG,GAAG,EAAE;YACnCO,YAAY,IAAIK;QAClB;IACF;AACF"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { AgentAdapter, AgentInfo, ProcessInfo, ConversationMessage, SessionSummary, ListSessionsOptions, AgentDetectionContext } from "../../adapters/AgentAdapter.js";
|
|
2
|
+
/**
|
|
3
|
+
* Grok Build CLI Adapter
|
|
4
|
+
*
|
|
5
|
+
* Detects running Grok Build CLI agents by:
|
|
6
|
+
* 1. Filtering `grok` processes from a shared asynchronous snapshot — Grok is
|
|
7
|
+
* a native binary at ~/.grok/bin/grok, so argv[0] basename is `grok`.
|
|
8
|
+
* 2. Resolving each live process to its working directory via
|
|
9
|
+
* ~/.grok/active_sessions.json, which Grok maintains as a list of
|
|
10
|
+
* { pid, cwd, opened_at } for every running session. The cwd is then encoded
|
|
11
|
+
* into the session group dir ~/.grok/sessions/<encodeURIComponent(cwd)>/, and
|
|
12
|
+
* the most recently active session subdirectory is picked from it. The
|
|
13
|
+
* process cwd from lsof is only a fallback when the PID is not registered.
|
|
14
|
+
* 3. Reading the session transcript from chat_history.jsonl (the authoritative
|
|
15
|
+
* record of the conversation). The last user turn (the text inside
|
|
16
|
+
* <user_query>...</user_query>) is the summary; the file's mtime is the last
|
|
17
|
+
* activity time. summary.json / updates.jsonl are intentionally not used.
|
|
18
|
+
*/
|
|
19
|
+
export declare class GrokCliAdapter implements AgentAdapter {
|
|
20
|
+
readonly type: "grok_cli";
|
|
21
|
+
readonly processNames: readonly ["grok"];
|
|
22
|
+
private parser;
|
|
23
|
+
private mapper;
|
|
24
|
+
private locator;
|
|
25
|
+
constructor();
|
|
26
|
+
canHandle(processInfo: ProcessInfo): boolean;
|
|
27
|
+
private isGrokExecutable;
|
|
28
|
+
detectAgents(context?: AgentDetectionContext): Promise<AgentInfo[]>;
|
|
29
|
+
getConversation(sessionFilePath: string, options?: {
|
|
30
|
+
verbose?: boolean;
|
|
31
|
+
}): ConversationMessage[];
|
|
32
|
+
listSessions(opts?: ListSessionsOptions): Promise<SessionSummary[]>;
|
|
33
|
+
findSessionsById(sessionId: string): Promise<SessionSummary[]>;
|
|
34
|
+
private toSessionSummary;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=GrokCliAdapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GrokCliAdapter.d.ts","sourceRoot":"","sources":["../../../src/providers/grok/GrokCliAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,SAAS,EACT,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,gCAAgC,CAAC;AAUxC;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,cAAe,YAAW,YAAY;IACjD,QAAQ,CAAC,IAAI,EAAG,UAAU,CAAU;IACpC,QAAQ,CAAC,YAAY,oBAAqB;IAE1C,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,OAAO,CAAqB;;IAQpC,SAAS,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO;IAI5C,OAAO,CAAC,gBAAgB;IAKlB,YAAY,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAsBzE,eAAe,CAAC,eAAe,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,mBAAmB,EAAE;IAI1F,YAAY,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAgBnE,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAapE,OAAO,CAAC,gBAAgB;CAWzB"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { captureProcessSnapshot, executableBasename, filterByProcessNames } from "../../utils/process.js";
|
|
2
|
+
import { GrokAgentMapper } from "./GrokAgentMapper.js";
|
|
3
|
+
import { GrokSessionLocator } from "./GrokSessionLocator.js";
|
|
4
|
+
import { GrokSessionParser } from "./GrokSessionParser.js";
|
|
5
|
+
/**
|
|
6
|
+
* Grok Build CLI Adapter
|
|
7
|
+
*
|
|
8
|
+
* Detects running Grok Build CLI agents by:
|
|
9
|
+
* 1. Filtering `grok` processes from a shared asynchronous snapshot — Grok is
|
|
10
|
+
* a native binary at ~/.grok/bin/grok, so argv[0] basename is `grok`.
|
|
11
|
+
* 2. Resolving each live process to its working directory via
|
|
12
|
+
* ~/.grok/active_sessions.json, which Grok maintains as a list of
|
|
13
|
+
* { pid, cwd, opened_at } for every running session. The cwd is then encoded
|
|
14
|
+
* into the session group dir ~/.grok/sessions/<encodeURIComponent(cwd)>/, and
|
|
15
|
+
* the most recently active session subdirectory is picked from it. The
|
|
16
|
+
* process cwd from lsof is only a fallback when the PID is not registered.
|
|
17
|
+
* 3. Reading the session transcript from chat_history.jsonl (the authoritative
|
|
18
|
+
* record of the conversation). The last user turn (the text inside
|
|
19
|
+
* <user_query>...</user_query>) is the summary; the file's mtime is the last
|
|
20
|
+
* activity time. summary.json / updates.jsonl are intentionally not used.
|
|
21
|
+
*/ export class GrokCliAdapter {
|
|
22
|
+
type = "grok_cli";
|
|
23
|
+
processNames = [
|
|
24
|
+
"grok"
|
|
25
|
+
];
|
|
26
|
+
parser;
|
|
27
|
+
mapper;
|
|
28
|
+
locator;
|
|
29
|
+
constructor(){
|
|
30
|
+
this.parser = new GrokSessionParser();
|
|
31
|
+
this.mapper = new GrokAgentMapper(this.parser);
|
|
32
|
+
this.locator = new GrokSessionLocator();
|
|
33
|
+
}
|
|
34
|
+
canHandle(processInfo) {
|
|
35
|
+
return this.isGrokExecutable(processInfo.command);
|
|
36
|
+
}
|
|
37
|
+
isGrokExecutable(command) {
|
|
38
|
+
const base = executableBasename(command);
|
|
39
|
+
return base === "grok" || base === "grok.exe";
|
|
40
|
+
}
|
|
41
|
+
async detectAgents(context) {
|
|
42
|
+
const snapshot = context?.processes ?? await captureProcessSnapshot(this.processNames);
|
|
43
|
+
const relevant = filterByProcessNames(snapshot, this.processNames);
|
|
44
|
+
const processes = relevant.filter((process)=>this.canHandle(process));
|
|
45
|
+
if (processes.length === 0) {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
const agents = [];
|
|
49
|
+
for (const { process: proc, cwd, sessionDir } of this.locator.matchRunningProcesses(processes)){
|
|
50
|
+
const session = sessionDir ? this.parser.readSession(sessionDir, cwd) : null;
|
|
51
|
+
if (session) {
|
|
52
|
+
agents.push(this.mapper.mapSessionToAgent({
|
|
53
|
+
session,
|
|
54
|
+
processInfo: proc
|
|
55
|
+
}));
|
|
56
|
+
} else {
|
|
57
|
+
agents.push(this.mapper.mapProcessOnlyAgent(proc, cwd));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return agents;
|
|
61
|
+
}
|
|
62
|
+
getConversation(sessionFilePath, options) {
|
|
63
|
+
return this.parser.getConversation(sessionFilePath, options);
|
|
64
|
+
}
|
|
65
|
+
async listSessions(opts) {
|
|
66
|
+
const filterCwd = opts?.cwd;
|
|
67
|
+
const summaries = [];
|
|
68
|
+
for (const { sessionDir, defaultCwd } of this.locator.discoverHistoricalSessionDirs()){
|
|
69
|
+
const session = this.parser.readSession(sessionDir, defaultCwd);
|
|
70
|
+
if (!session) continue;
|
|
71
|
+
if (filterCwd !== undefined && session.projectPath !== filterCwd) continue;
|
|
72
|
+
summaries.push(this.toSessionSummary(session));
|
|
73
|
+
}
|
|
74
|
+
return summaries;
|
|
75
|
+
}
|
|
76
|
+
async findSessionsById(sessionId) {
|
|
77
|
+
const summaries = [];
|
|
78
|
+
for (const { sessionDir, defaultCwd } of this.locator.findHistoricalSessionDirsById(sessionId)){
|
|
79
|
+
const session = this.parser.readSession(sessionDir, defaultCwd);
|
|
80
|
+
if (!session) continue;
|
|
81
|
+
summaries.push(this.toSessionSummary(session));
|
|
82
|
+
}
|
|
83
|
+
return summaries;
|
|
84
|
+
}
|
|
85
|
+
toSessionSummary(session) {
|
|
86
|
+
return {
|
|
87
|
+
type: this.type,
|
|
88
|
+
sessionId: session.sessionId,
|
|
89
|
+
cwd: session.projectPath,
|
|
90
|
+
firstUserMessage: session.firstUserMessage || "",
|
|
91
|
+
lastActive: session.lastActive,
|
|
92
|
+
startedAt: session.sessionStart,
|
|
93
|
+
sessionFilePath: session.sessionFilePath
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
//# sourceMappingURL=GrokCliAdapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/providers/grok/GrokCliAdapter.ts"],"sourcesContent":["import type {\n AgentAdapter,\n AgentInfo,\n ProcessInfo,\n ConversationMessage,\n SessionSummary,\n ListSessionsOptions,\n AgentDetectionContext,\n} from \"../../adapters/AgentAdapter.js\";\nimport {\n captureProcessSnapshot,\n executableBasename,\n filterByProcessNames,\n} from \"../../utils/process.js\";\nimport { GrokAgentMapper } from \"./GrokAgentMapper.js\";\nimport { GrokSessionLocator } from \"./GrokSessionLocator.js\";\nimport { GrokSessionParser, type GrokSession } from \"./GrokSessionParser.js\";\n\n/**\n * Grok Build CLI Adapter\n *\n * Detects running Grok Build CLI agents by:\n * 1. Filtering `grok` processes from a shared asynchronous snapshot — Grok is\n * a native binary at ~/.grok/bin/grok, so argv[0] basename is `grok`.\n * 2. Resolving each live process to its working directory via\n * ~/.grok/active_sessions.json, which Grok maintains as a list of\n * { pid, cwd, opened_at } for every running session. The cwd is then encoded\n * into the session group dir ~/.grok/sessions/<encodeURIComponent(cwd)>/, and\n * the most recently active session subdirectory is picked from it. The\n * process cwd from lsof is only a fallback when the PID is not registered.\n * 3. Reading the session transcript from chat_history.jsonl (the authoritative\n * record of the conversation). The last user turn (the text inside\n * <user_query>...</user_query>) is the summary; the file's mtime is the last\n * activity time. summary.json / updates.jsonl are intentionally not used.\n */\nexport class GrokCliAdapter implements AgentAdapter {\n readonly type = \"grok_cli\" as const;\n readonly processNames = [\"grok\"] as const;\n\n private parser: GrokSessionParser;\n private mapper: GrokAgentMapper;\n private locator: GrokSessionLocator;\n\n constructor() {\n this.parser = new GrokSessionParser();\n this.mapper = new GrokAgentMapper(this.parser);\n this.locator = new GrokSessionLocator();\n }\n\n canHandle(processInfo: ProcessInfo): boolean {\n return this.isGrokExecutable(processInfo.command);\n }\n\n private isGrokExecutable(command: string): boolean {\n const base = executableBasename(command);\n return base === \"grok\" || base === \"grok.exe\";\n }\n\n async detectAgents(context?: AgentDetectionContext): Promise<AgentInfo[]> {\n const snapshot = context?.processes ?? (await captureProcessSnapshot(this.processNames));\n const relevant = filterByProcessNames(snapshot, this.processNames);\n const processes = relevant.filter((process) => this.canHandle(process));\n if (processes.length === 0) {\n return [];\n }\n\n const agents: AgentInfo[] = [];\n for (const { process: proc, cwd, sessionDir } of this.locator.matchRunningProcesses(\n processes,\n )) {\n const session = sessionDir ? this.parser.readSession(sessionDir, cwd) : null;\n if (session) {\n agents.push(this.mapper.mapSessionToAgent({ session, processInfo: proc }));\n } else {\n agents.push(this.mapper.mapProcessOnlyAgent(proc, cwd));\n }\n }\n return agents;\n }\n\n getConversation(sessionFilePath: string, options?: { verbose?: boolean }): ConversationMessage[] {\n return this.parser.getConversation(sessionFilePath, options);\n }\n\n async listSessions(opts?: ListSessionsOptions): Promise<SessionSummary[]> {\n const filterCwd = opts?.cwd;\n const summaries: SessionSummary[] = [];\n\n for (const { sessionDir, defaultCwd } of this.locator.discoverHistoricalSessionDirs()) {\n const session = this.parser.readSession(sessionDir, defaultCwd);\n if (!session) continue;\n\n if (filterCwd !== undefined && session.projectPath !== filterCwd) continue;\n\n summaries.push(this.toSessionSummary(session));\n }\n\n return summaries;\n }\n\n async findSessionsById(sessionId: string): Promise<SessionSummary[]> {\n const summaries: SessionSummary[] = [];\n for (const { sessionDir, defaultCwd } of this.locator.findHistoricalSessionDirsById(\n sessionId,\n )) {\n const session = this.parser.readSession(sessionDir, defaultCwd);\n if (!session) continue;\n\n summaries.push(this.toSessionSummary(session));\n }\n return summaries;\n }\n\n private toSessionSummary(session: GrokSession): SessionSummary {\n return {\n type: this.type,\n sessionId: session.sessionId,\n cwd: session.projectPath,\n firstUserMessage: session.firstUserMessage || \"\",\n lastActive: session.lastActive,\n startedAt: session.sessionStart,\n sessionFilePath: session.sessionFilePath,\n };\n }\n}\n"],"names":["captureProcessSnapshot","executableBasename","filterByProcessNames","GrokAgentMapper","GrokSessionLocator","GrokSessionParser","GrokCliAdapter","type","processNames","parser","mapper","locator","canHandle","processInfo","isGrokExecutable","command","base","detectAgents","context","snapshot","processes","relevant","filter","process","length","agents","proc","cwd","sessionDir","matchRunningProcesses","session","readSession","push","mapSessionToAgent","mapProcessOnlyAgent","getConversation","sessionFilePath","options","listSessions","opts","filterCwd","summaries","defaultCwd","discoverHistoricalSessionDirs","undefined","projectPath","toSessionSummary","findSessionsById","sessionId","findHistoricalSessionDirsById","firstUserMessage","lastActive","startedAt","sessionStart"],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { ProcessInfo } from "../../adapters/AgentAdapter.js";
|
|
2
|
+
/** A live Grok process paired with its resolved cwd and (if any) session dir. */
|
|
3
|
+
export interface GrokProcessMatch {
|
|
4
|
+
process: ProcessInfo;
|
|
5
|
+
cwd: string;
|
|
6
|
+
sessionDir: string | null;
|
|
7
|
+
}
|
|
8
|
+
/** A historical session dir plus the cwd decoded from its group dir. */
|
|
9
|
+
export interface GrokSessionDir {
|
|
10
|
+
sessionDir: string;
|
|
11
|
+
defaultCwd: string;
|
|
12
|
+
}
|
|
13
|
+
export interface GrokSessionLocatorOptions {
|
|
14
|
+
/** Overrides the ~/.grok base directory (defaults to GROK_HOME or ~/.grok). */
|
|
15
|
+
baseDir?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare class GrokSessionLocator {
|
|
18
|
+
private readonly baseDir;
|
|
19
|
+
private readonly sessionsDir;
|
|
20
|
+
constructor(options?: GrokSessionLocatorOptions);
|
|
21
|
+
/**
|
|
22
|
+
* Pair live Grok processes with their session dirs.
|
|
23
|
+
*
|
|
24
|
+
* active_sessions.json is the authoritative pid -> cwd map for live
|
|
25
|
+
* sessions; the lsof-derived process cwd is only a fallback. The most
|
|
26
|
+
* recently active session under that cwd's group dir is picked.
|
|
27
|
+
*/
|
|
28
|
+
matchRunningProcesses(processes: ProcessInfo[]): GrokProcessMatch[];
|
|
29
|
+
/** Every session dir under ~/.grok/sessions/, across all project groups. */
|
|
30
|
+
discoverHistoricalSessionDirs(): GrokSessionDir[];
|
|
31
|
+
/** Session dirs named exactly `sessionId`, across all project groups. */
|
|
32
|
+
findHistoricalSessionDirsById(sessionId: string): GrokSessionDir[];
|
|
33
|
+
/**
|
|
34
|
+
* Read ~/.grok/active_sessions.json into a pid -> cwd map. Grok writes one
|
|
35
|
+
* { pid, cwd, opened_at } entry per running session and removes it on exit,
|
|
36
|
+
* so this is the reliable way to learn a live process's working directory.
|
|
37
|
+
*/
|
|
38
|
+
private readActiveSessions;
|
|
39
|
+
/**
|
|
40
|
+
* Return the most recently active session subdirectory for a cwd, i.e. the
|
|
41
|
+
* ~/.grok/sessions/<encodeURIComponent(cwd)>/<id>/ whose chat_history.jsonl
|
|
42
|
+
* was written last. Returns null when the group dir or any transcript is
|
|
43
|
+
* missing.
|
|
44
|
+
*/
|
|
45
|
+
private latestSessionDir;
|
|
46
|
+
private listGroups;
|
|
47
|
+
/**
|
|
48
|
+
* Full paths of the session subdirectories directly under a group dir,
|
|
49
|
+
* skipping any non-directory entries (e.g. prompt_history.jsonl).
|
|
50
|
+
*/
|
|
51
|
+
private listSessionDirs;
|
|
52
|
+
/**
|
|
53
|
+
* Resolve the working directory a session group dir was created for.
|
|
54
|
+
*
|
|
55
|
+
* The common case is `decodeURIComponent(<group-name>)`. For paths whose
|
|
56
|
+
* encoded form exceeds the filesystem limit Grok uses a slug+hash and records
|
|
57
|
+
* the original path in a `.cwd` file inside the group — prefer that when
|
|
58
|
+
* present.
|
|
59
|
+
*/
|
|
60
|
+
private decodeGroupCwd;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=GrokSessionLocator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GrokSessionLocator.d.ts","sourceRoot":"","sources":["../../../src/providers/grok/GrokSessionLocator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAoBlE,iFAAiF;AACjF,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,WAAW,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,wEAAwE;AACxE,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,yBAAyB;IACxC,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;gBAEzB,OAAO,GAAE,yBAA8B;IASnD;;;;;;OAMG;IACH,qBAAqB,CAAC,SAAS,EAAE,WAAW,EAAE,GAAG,gBAAgB,EAAE;IAQnE,4EAA4E;IAC5E,6BAA6B,IAAI,cAAc,EAAE;IAUjD,yEAAyE;IACzE,6BAA6B,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,EAAE;IAalE;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IAqB1B;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;IAexB,OAAO,CAAC,UAAU;IAYlB;;;OAGG;IACH,OAAO,CAAC,eAAe;IAMvB;;;;;;;OAOG;IACH,OAAO,CAAC,cAAc;CASvB"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import * as path from "path";
|
|
2
|
+
import { isDirectory, isSafePathSegment, safeReadFile, safeReaddir, safeStat } from "../../utils/session.js";
|
|
3
|
+
import { CHAT_HISTORY_FILE } from "./GrokSessionParser.js";
|
|
4
|
+
const ACTIVE_SESSIONS_FILE = "active_sessions.json";
|
|
5
|
+
const CWD_FILE = ".cwd";
|
|
6
|
+
export class GrokSessionLocator {
|
|
7
|
+
baseDir;
|
|
8
|
+
sessionsDir;
|
|
9
|
+
constructor(options = {}){
|
|
10
|
+
// GROK_HOME overrides the ~/.grok base directory; sessions live under
|
|
11
|
+
// <base>/sessions/ and the active-session registry at
|
|
12
|
+
// <base>/active_sessions.json.
|
|
13
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
14
|
+
this.baseDir = options.baseDir ?? (process.env.GROK_HOME || path.join(homeDir, ".grok"));
|
|
15
|
+
this.sessionsDir = path.join(this.baseDir, "sessions");
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Pair live Grok processes with their session dirs.
|
|
19
|
+
*
|
|
20
|
+
* active_sessions.json is the authoritative pid -> cwd map for live
|
|
21
|
+
* sessions; the lsof-derived process cwd is only a fallback. The most
|
|
22
|
+
* recently active session under that cwd's group dir is picked.
|
|
23
|
+
*/ matchRunningProcesses(processes) {
|
|
24
|
+
const pidToCwd = this.readActiveSessions();
|
|
25
|
+
return processes.map((proc)=>{
|
|
26
|
+
const cwd = pidToCwd.get(proc.pid) || proc.cwd || "";
|
|
27
|
+
return {
|
|
28
|
+
process: proc,
|
|
29
|
+
cwd,
|
|
30
|
+
sessionDir: cwd ? this.latestSessionDir(cwd) : null
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
/** Every session dir under ~/.grok/sessions/, across all project groups. */ discoverHistoricalSessionDirs() {
|
|
35
|
+
const results = [];
|
|
36
|
+
for (const { groupDir, defaultCwd } of this.listGroups()){
|
|
37
|
+
for (const sessionDir of this.listSessionDirs(groupDir)){
|
|
38
|
+
results.push({
|
|
39
|
+
sessionDir,
|
|
40
|
+
defaultCwd
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return results;
|
|
45
|
+
}
|
|
46
|
+
/** Session dirs named exactly `sessionId`, across all project groups. */ findHistoricalSessionDirsById(sessionId) {
|
|
47
|
+
if (!isSafePathSegment(sessionId)) return [];
|
|
48
|
+
const results = [];
|
|
49
|
+
for (const { groupDir, defaultCwd } of this.listGroups()){
|
|
50
|
+
const sessionDir = path.join(groupDir, sessionId);
|
|
51
|
+
if (isDirectory(sessionDir)) {
|
|
52
|
+
results.push({
|
|
53
|
+
sessionDir,
|
|
54
|
+
defaultCwd
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return results;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Read ~/.grok/active_sessions.json into a pid -> cwd map. Grok writes one
|
|
62
|
+
* { pid, cwd, opened_at } entry per running session and removes it on exit,
|
|
63
|
+
* so this is the reliable way to learn a live process's working directory.
|
|
64
|
+
*/ readActiveSessions() {
|
|
65
|
+
const map = new Map();
|
|
66
|
+
const content = safeReadFile(path.join(this.baseDir, ACTIVE_SESSIONS_FILE));
|
|
67
|
+
if (content === undefined) return map;
|
|
68
|
+
let entries;
|
|
69
|
+
try {
|
|
70
|
+
entries = JSON.parse(content);
|
|
71
|
+
} catch {
|
|
72
|
+
return map;
|
|
73
|
+
}
|
|
74
|
+
if (!Array.isArray(entries)) return map;
|
|
75
|
+
for (const entry of entries){
|
|
76
|
+
if (typeof entry?.pid === "number" && typeof entry?.cwd === "string" && entry.cwd) {
|
|
77
|
+
map.set(entry.pid, entry.cwd);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return map;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Return the most recently active session subdirectory for a cwd, i.e. the
|
|
84
|
+
* ~/.grok/sessions/<encodeURIComponent(cwd)>/<id>/ whose chat_history.jsonl
|
|
85
|
+
* was written last. Returns null when the group dir or any transcript is
|
|
86
|
+
* missing.
|
|
87
|
+
*/ latestSessionDir(cwd) {
|
|
88
|
+
const groupDir = path.join(this.sessionsDir, encodeURIComponent(cwd));
|
|
89
|
+
if (!isDirectory(groupDir)) return null;
|
|
90
|
+
let best = null;
|
|
91
|
+
for (const sessionDir of this.listSessionDirs(groupDir)){
|
|
92
|
+
const stat = safeStat(path.join(sessionDir, CHAT_HISTORY_FILE));
|
|
93
|
+
if (!stat) continue;
|
|
94
|
+
if (!best || stat.mtimeMs > best.mtimeMs) {
|
|
95
|
+
best = {
|
|
96
|
+
dir: sessionDir,
|
|
97
|
+
mtimeMs: stat.mtimeMs
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return best?.dir ?? null;
|
|
102
|
+
}
|
|
103
|
+
listGroups() {
|
|
104
|
+
if (!isDirectory(this.sessionsDir)) return [];
|
|
105
|
+
const groups = [];
|
|
106
|
+
for (const groupName of safeReaddir(this.sessionsDir)){
|
|
107
|
+
const groupDir = path.join(this.sessionsDir, groupName);
|
|
108
|
+
if (!isDirectory(groupDir)) continue;
|
|
109
|
+
groups.push({
|
|
110
|
+
groupDir,
|
|
111
|
+
defaultCwd: this.decodeGroupCwd(groupName, groupDir)
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
return groups;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Full paths of the session subdirectories directly under a group dir,
|
|
118
|
+
* skipping any non-directory entries (e.g. prompt_history.jsonl).
|
|
119
|
+
*/ listSessionDirs(groupDir) {
|
|
120
|
+
return safeReaddir(groupDir).map((sessionId)=>path.join(groupDir, sessionId)).filter((sessionDir)=>isDirectory(sessionDir));
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Resolve the working directory a session group dir was created for.
|
|
124
|
+
*
|
|
125
|
+
* The common case is `decodeURIComponent(<group-name>)`. For paths whose
|
|
126
|
+
* encoded form exceeds the filesystem limit Grok uses a slug+hash and records
|
|
127
|
+
* the original path in a `.cwd` file inside the group — prefer that when
|
|
128
|
+
* present.
|
|
129
|
+
*/ decodeGroupCwd(groupName, groupDir) {
|
|
130
|
+
const fromFile = safeReadFile(path.join(groupDir, CWD_FILE));
|
|
131
|
+
if (fromFile !== undefined && fromFile.trim()) return fromFile.trim();
|
|
132
|
+
try {
|
|
133
|
+
return decodeURIComponent(groupName);
|
|
134
|
+
} catch {
|
|
135
|
+
return "";
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
//# sourceMappingURL=GrokSessionLocator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/providers/grok/GrokSessionLocator.ts"],"sourcesContent":["import * as path from \"path\";\nimport type { ProcessInfo } from \"../../adapters/AgentAdapter.js\";\nimport {\n isDirectory,\n isSafePathSegment,\n safeReadFile,\n safeReaddir,\n safeStat,\n} from \"../../utils/session.js\";\nimport { CHAT_HISTORY_FILE } from \"./GrokSessionParser.js\";\n\nconst ACTIVE_SESSIONS_FILE = \"active_sessions.json\";\nconst CWD_FILE = \".cwd\";\n\n/** One entry of ~/.grok/active_sessions.json. */\ninterface ActiveSessionEntry {\n pid?: number;\n cwd?: string;\n opened_at?: number | string;\n}\n\n/** A live Grok process paired with its resolved cwd and (if any) session dir. */\nexport interface GrokProcessMatch {\n process: ProcessInfo;\n cwd: string;\n sessionDir: string | null;\n}\n\n/** A historical session dir plus the cwd decoded from its group dir. */\nexport interface GrokSessionDir {\n sessionDir: string;\n defaultCwd: string;\n}\n\nexport interface GrokSessionLocatorOptions {\n /** Overrides the ~/.grok base directory (defaults to GROK_HOME or ~/.grok). */\n baseDir?: string;\n}\n\nexport class GrokSessionLocator {\n private readonly baseDir: string;\n private readonly sessionsDir: string;\n\n constructor(options: GrokSessionLocatorOptions = {}) {\n // GROK_HOME overrides the ~/.grok base directory; sessions live under\n // <base>/sessions/ and the active-session registry at\n // <base>/active_sessions.json.\n const homeDir = process.env.HOME || process.env.USERPROFILE || \"\";\n this.baseDir = options.baseDir ?? (process.env.GROK_HOME || path.join(homeDir, \".grok\"));\n this.sessionsDir = path.join(this.baseDir, \"sessions\");\n }\n\n /**\n * Pair live Grok processes with their session dirs.\n *\n * active_sessions.json is the authoritative pid -> cwd map for live\n * sessions; the lsof-derived process cwd is only a fallback. The most\n * recently active session under that cwd's group dir is picked.\n */\n matchRunningProcesses(processes: ProcessInfo[]): GrokProcessMatch[] {\n const pidToCwd = this.readActiveSessions();\n return processes.map((proc) => {\n const cwd = pidToCwd.get(proc.pid) || proc.cwd || \"\";\n return { process: proc, cwd, sessionDir: cwd ? this.latestSessionDir(cwd) : null };\n });\n }\n\n /** Every session dir under ~/.grok/sessions/, across all project groups. */\n discoverHistoricalSessionDirs(): GrokSessionDir[] {\n const results: GrokSessionDir[] = [];\n for (const { groupDir, defaultCwd } of this.listGroups()) {\n for (const sessionDir of this.listSessionDirs(groupDir)) {\n results.push({ sessionDir, defaultCwd });\n }\n }\n return results;\n }\n\n /** Session dirs named exactly `sessionId`, across all project groups. */\n findHistoricalSessionDirsById(sessionId: string): GrokSessionDir[] {\n if (!isSafePathSegment(sessionId)) return [];\n\n const results: GrokSessionDir[] = [];\n for (const { groupDir, defaultCwd } of this.listGroups()) {\n const sessionDir = path.join(groupDir, sessionId);\n if (isDirectory(sessionDir)) {\n results.push({ sessionDir, defaultCwd });\n }\n }\n return results;\n }\n\n /**\n * Read ~/.grok/active_sessions.json into a pid -> cwd map. Grok writes one\n * { pid, cwd, opened_at } entry per running session and removes it on exit,\n * so this is the reliable way to learn a live process's working directory.\n */\n private readActiveSessions(): Map<number, string> {\n const map = new Map<number, string>();\n const content = safeReadFile(path.join(this.baseDir, ACTIVE_SESSIONS_FILE));\n if (content === undefined) return map;\n\n let entries: unknown;\n try {\n entries = JSON.parse(content);\n } catch {\n return map;\n }\n if (!Array.isArray(entries)) return map;\n\n for (const entry of entries as ActiveSessionEntry[]) {\n if (typeof entry?.pid === \"number\" && typeof entry?.cwd === \"string\" && entry.cwd) {\n map.set(entry.pid, entry.cwd);\n }\n }\n return map;\n }\n\n /**\n * Return the most recently active session subdirectory for a cwd, i.e. the\n * ~/.grok/sessions/<encodeURIComponent(cwd)>/<id>/ whose chat_history.jsonl\n * was written last. Returns null when the group dir or any transcript is\n * missing.\n */\n private latestSessionDir(cwd: string): string | null {\n const groupDir = path.join(this.sessionsDir, encodeURIComponent(cwd));\n if (!isDirectory(groupDir)) return null;\n\n let best: { dir: string; mtimeMs: number } | null = null;\n for (const sessionDir of this.listSessionDirs(groupDir)) {\n const stat = safeStat(path.join(sessionDir, CHAT_HISTORY_FILE));\n if (!stat) continue;\n if (!best || stat.mtimeMs > best.mtimeMs) {\n best = { dir: sessionDir, mtimeMs: stat.mtimeMs };\n }\n }\n return best?.dir ?? null;\n }\n\n private listGroups(): Array<{ groupDir: string; defaultCwd: string }> {\n if (!isDirectory(this.sessionsDir)) return [];\n\n const groups: Array<{ groupDir: string; defaultCwd: string }> = [];\n for (const groupName of safeReaddir(this.sessionsDir)) {\n const groupDir = path.join(this.sessionsDir, groupName);\n if (!isDirectory(groupDir)) continue;\n groups.push({ groupDir, defaultCwd: this.decodeGroupCwd(groupName, groupDir) });\n }\n return groups;\n }\n\n /**\n * Full paths of the session subdirectories directly under a group dir,\n * skipping any non-directory entries (e.g. prompt_history.jsonl).\n */\n private listSessionDirs(groupDir: string): string[] {\n return safeReaddir(groupDir)\n .map((sessionId) => path.join(groupDir, sessionId))\n .filter((sessionDir) => isDirectory(sessionDir));\n }\n\n /**\n * Resolve the working directory a session group dir was created for.\n *\n * The common case is `decodeURIComponent(<group-name>)`. For paths whose\n * encoded form exceeds the filesystem limit Grok uses a slug+hash and records\n * the original path in a `.cwd` file inside the group — prefer that when\n * present.\n */\n private decodeGroupCwd(groupName: string, groupDir: string): string {\n const fromFile = safeReadFile(path.join(groupDir, CWD_FILE));\n if (fromFile !== undefined && fromFile.trim()) return fromFile.trim();\n try {\n return decodeURIComponent(groupName);\n } catch {\n return \"\";\n }\n }\n}\n"],"names":["path","isDirectory","isSafePathSegment","safeReadFile","safeReaddir","safeStat","CHAT_HISTORY_FILE","ACTIVE_SESSIONS_FILE","CWD_FILE","GrokSessionLocator","baseDir","sessionsDir","options","homeDir","process","env","HOME","USERPROFILE","GROK_HOME","join","matchRunningProcesses","processes","pidToCwd","readActiveSessions","map","proc","cwd","get","pid","sessionDir","latestSessionDir","discoverHistoricalSessionDirs","results","groupDir","defaultCwd","listGroups","listSessionDirs","push","findHistoricalSessionDirsById","sessionId","Map","content","undefined","entries","JSON","parse","Array","isArray","entry","set","encodeURIComponent","best","stat","mtimeMs","dir","groups","groupName","decodeGroupCwd","filter","fromFile","trim","decodeURIComponent"],"mappings":"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"}
|