@axhub/genie 0.2.5 → 0.2.7
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/assets/App-BWSqiXAT.js +220 -0
- package/dist/assets/App-DrlLKa8f.css +1 -0
- package/dist/assets/ReviewApp-nz3mbArg.js +1 -0
- package/dist/assets/{_basePickBy-CFRQvihx.js → _basePickBy-C19AekOu.js} +1 -1
- package/dist/assets/{_baseUniq-Dhh8nCvs.js → _baseUniq-JsnevLw_.js} +1 -1
- package/dist/assets/{arc-DQ0v3dU4.js → arc-BLpcuBlf.js} +1 -1
- package/dist/assets/architectureDiagram-2XIMDMQ5-CarjBOOv.js +36 -0
- package/dist/assets/{blockDiagram-WCTKOSBZ-Bbxhj5KC.js → blockDiagram-WCTKOSBZ-DQBLwsUS.js} +3 -3
- package/dist/assets/c4Diagram-IC4MRINW-CGobwBIj.js +10 -0
- package/dist/assets/channel-DkFNxV_H.js +1 -0
- package/dist/assets/{chunk-4BX2VUAB-DlvtrM0q.js → chunk-4BX2VUAB-De63kbgc.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-DJUSHyTa.js → chunk-55IACEB6-DtTDDdM9.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-C6Ch-htf.js → chunk-FMBD7UC4-DHuwd8tw.js} +1 -1
- package/dist/assets/{chunk-JSJVCQXG-DzQIht58.js → chunk-JSJVCQXG-BgytFtmO.js} +1 -1
- package/dist/assets/{chunk-KX2RTZJC-C05jARMH.js → chunk-KX2RTZJC-nZdp86aN.js} +1 -1
- package/dist/assets/chunk-NQ4KR5QH-CMH6EDP2.js +220 -0
- package/dist/assets/{chunk-QZHKN3VN-jxti9HTX.js → chunk-QZHKN3VN-DvUQ3mnO.js} +1 -1
- package/dist/assets/chunk-WL4C6EOR-Dn7db_6t.js +189 -0
- package/dist/assets/classDiagram-VBA2DB6C-DtwCEe8S.js +1 -0
- package/dist/assets/classDiagram-v2-RAHNMMFH-DtwCEe8S.js +1 -0
- package/dist/assets/clone-C0lCEIEO.js +1 -0
- package/dist/assets/cose-bilkent-S5V4N54A-DD_nzqsz.js +1 -0
- package/dist/assets/cytoscape.esm-5J0xJHOV.js +321 -0
- package/dist/assets/{dagre-KLK3FWXG-DJ3dNSYk.js → dagre-KLK3FWXG-CHYIvW47.js} +1 -1
- package/dist/assets/diagram-E7M64L7V-TVdvHtGc.js +24 -0
- package/dist/assets/{diagram-IFDJBPK2-Da6K4aP-.js → diagram-IFDJBPK2-Dzsiln_C.js} +1 -1
- package/dist/assets/{diagram-P4PSJMXO-vZZKB92A.js → diagram-P4PSJMXO-DKnGbUpE.js} +1 -1
- package/dist/assets/erDiagram-INFDFZHY-5Kw0bByo.js +70 -0
- package/dist/assets/{flowDiagram-PKNHOUZH-DUV13pHi.js → flowDiagram-PKNHOUZH-BAZ2-jKp.js} +4 -4
- package/dist/assets/ganttDiagram-A5KZAMGK-CsADFkcq.js +292 -0
- package/dist/assets/{gitGraphDiagram-K3NZZRJ6-BZ5gW69I.js → gitGraphDiagram-K3NZZRJ6-BflpyjGy.js} +1 -1
- package/dist/assets/{graph-BbvHswRd.js → graph-suelaXFh.js} +1 -1
- package/dist/assets/highlighted-body-OFNGDK62-CZrBMazC.js +1 -0
- package/dist/assets/index-B01NxbUv.css +1 -0
- package/dist/assets/index-DW5pGgQ_.js +2 -0
- package/dist/assets/{infoDiagram-LFFYTUFH-8auUIPKW.js → infoDiagram-LFFYTUFH-pfD1FA3p.js} +1 -1
- package/dist/assets/ishikawaDiagram-PHBUUO56-ndm9snwO.js +70 -0
- package/dist/assets/journeyDiagram-4ABVD52K-HgF2t7z5.js +139 -0
- package/dist/assets/{kanban-definition-K7BYSVSG-Bappd2YO.js → kanban-definition-K7BYSVSG-FWinmur1.js} +5 -5
- package/dist/assets/{layout-BmbfFZKy.js → layout-vcz43XvZ.js} +1 -1
- package/dist/assets/{linear-WZnF-PT6.js → linear-le4gc0vx.js} +1 -1
- package/dist/assets/mermaid-GHXKKRXX-CK8m3lad.js +870 -0
- package/dist/assets/mindmap-definition-YRQLILUH-CNq9SKj4.js +68 -0
- package/dist/assets/{pieDiagram-SKSYHLDU-uxjlAy1t.js → pieDiagram-SKSYHLDU-C7PKDh3b.js} +2 -2
- package/dist/assets/quadrantDiagram-337W2JSQ-B7FnztNO.js +7 -0
- package/dist/assets/requirementDiagram-Z7DCOOCP-Bl_BM2Th.js +73 -0
- package/dist/assets/{sankeyDiagram-WA2Y5GQK-2-FHHM-R.js → sankeyDiagram-WA2Y5GQK-4gulcOP4.js} +3 -3
- package/dist/assets/sequenceDiagram-2WXFIKYE-VEuJDwyJ.js +145 -0
- package/dist/assets/{stateDiagram-RAJIS63D-DoW8U53H.js → stateDiagram-RAJIS63D-CB4Vl7qM.js} +1 -1
- package/dist/assets/stateDiagram-v2-FVOUBMTO-C85ucl39.js +1 -0
- package/dist/assets/timeline-definition-YZTLITO2-BPGKhi7f.js +61 -0
- package/dist/assets/{treemap-KZPCXAKY-ajdAP-72.js → treemap-KZPCXAKY-DZSEE6Hz.js} +58 -58
- package/dist/assets/vendor-codemirror-CyOKkaQZ.js +31 -0
- package/dist/assets/vendor-react-CP4yFTs7.js +8 -0
- package/dist/assets/vendor-xterm-DfcmCpbH.js +66 -0
- package/dist/assets/{vennDiagram-LZ73GAT5-C9If0AT0.js → vennDiagram-LZ73GAT5-8E_G06fI.js} +4 -4
- package/dist/assets/xychartDiagram-JWTSCODW-CbBk50-O.js +7 -0
- package/dist/favicon.png +0 -0
- package/dist/icons/icon-128x128.png +0 -0
- package/dist/icons/icon-144x144.png +0 -0
- package/dist/icons/icon-152x152.png +0 -0
- package/dist/icons/icon-192x192.png +0 -0
- package/dist/icons/icon-384x384.png +0 -0
- package/dist/icons/icon-512x512.png +0 -0
- package/dist/icons/icon-72x72.png +0 -0
- package/dist/icons/icon-96x96.png +0 -0
- package/dist/index.html +4 -5
- package/dist/logo-128.png +0 -0
- package/dist/logo-256.png +0 -0
- package/dist/logo-32.png +0 -0
- package/dist/logo-512.png +0 -0
- package/dist/logo-64.png +0 -0
- package/package.json +2 -1
- package/server/_legacy-providers/README.md +30 -0
- package/server/_legacy-providers/claude-sdk.js +956 -0
- package/server/_legacy-providers/gemini-cli.js +368 -0
- package/server/_legacy-providers/openai-codex.js +705 -0
- package/server/_legacy-providers/opencode-cli.js +674 -0
- package/server/acp-runtime/client.js +1805 -0
- package/server/acp-runtime/client.test.js +688 -0
- package/server/acp-runtime/index.js +419 -0
- package/server/acp-runtime/registry.js +45 -0
- package/server/acp-runtime/session-store.js +254 -0
- package/server/acp-runtime/session-store.test.js +89 -0
- package/server/channels/runtime/AgentRuntimeAdapter.js +21 -70
- package/server/claude-sdk.js +24 -944
- package/server/cli.js +11 -5
- package/server/external-agent/service.js +77 -63
- package/server/gemini-cli.js +23 -360
- package/server/index.js +54 -46
- package/server/openai-codex.js +24 -698
- package/server/opencode-cli.js +70 -640
- package/server/routes/agent.js +2 -0
- package/server/routes/codex.js +5 -5
- package/server/routes/git.js +3 -20
- package/server/routes/mcp.js +18 -34
- package/server/routes/session-core.js +44 -10
- package/server/session-core/abortSession.js +2 -18
- package/server/session-core/eventStore.js +5 -1
- package/server/session-core/providerAdapters.js +98 -10
- package/server/session-core/providerDiscovery.js +2 -2
- package/server/session-core/runtimeState.js +16 -17
- package/server/session-core/runtimeWriter.js +19 -12
- package/server/utils/codexPath.js +3 -1
- package/server/utils/spawnCommand.js +7 -0
- package/shared/conversationEvents.js +347 -10
- package/shared/conversationEvents.test.js +403 -0
- package/dist/assets/App-BxazfNJn.js +0 -484
- package/dist/assets/App-qxJ8_QYu.css +0 -32
- package/dist/assets/ReviewApp-CsqTAlGU.js +0 -1
- package/dist/assets/architectureDiagram-2XIMDMQ5-DmUHdvQH.js +0 -36
- package/dist/assets/c4Diagram-IC4MRINW-BOivDlQU.js +0 -10
- package/dist/assets/channel-Cj8xVD0X.js +0 -1
- package/dist/assets/chunk-NQ4KR5QH-Ci-n7jfu.js +0 -220
- package/dist/assets/chunk-WL4C6EOR-C559Mk71.js +0 -189
- package/dist/assets/classDiagram-VBA2DB6C-CI2zklxw.js +0 -1
- package/dist/assets/classDiagram-v2-RAHNMMFH-CI2zklxw.js +0 -1
- package/dist/assets/clone-BEVqubrI.js +0 -1
- package/dist/assets/cose-bilkent-S5V4N54A-DNO9ncXL.js +0 -1
- package/dist/assets/cytoscape.esm-2ZfV8NB5.js +0 -331
- package/dist/assets/diagram-E7M64L7V-Ba_LGLun.js +0 -24
- package/dist/assets/erDiagram-INFDFZHY-Csb8dFdP.js +0 -70
- package/dist/assets/ganttDiagram-A5KZAMGK-B5Kv9Wfz.js +0 -292
- package/dist/assets/highlighted-body-TPN3WLV5-DZJajMGm.js +0 -1
- package/dist/assets/index-BFX9lxRB.css +0 -1
- package/dist/assets/index-BiErUGrv.js +0 -2
- package/dist/assets/ishikawaDiagram-PHBUUO56-JmsNlo2I.js +0 -70
- package/dist/assets/journeyDiagram-4ABVD52K-Cuudv7Vv.js +0 -139
- package/dist/assets/mermaid-O7DHMXV3-D-2fQRvw.js +0 -988
- package/dist/assets/mindmap-definition-YRQLILUH-BQHnzzud.js +0 -68
- package/dist/assets/quadrantDiagram-337W2JSQ-DpwZU-f_.js +0 -7
- package/dist/assets/requirementDiagram-Z7DCOOCP-C_9ClOWm.js +0 -73
- package/dist/assets/sequenceDiagram-2WXFIKYE-egns-0XI.js +0 -145
- package/dist/assets/stateDiagram-v2-FVOUBMTO-BoFZZ4Ds.js +0 -1
- package/dist/assets/timeline-definition-YZTLITO2-chPa8ppH.js +0 -61
- package/dist/assets/vendor-codemirror-Dz7_EqNA.js +0 -39
- package/dist/assets/vendor-react-Cpt6D04s.js +0 -59
- package/dist/assets/vendor-xterm-DfaPXD3y.js +0 -66
- package/dist/assets/xychartDiagram-JWTSCODW-DD42U6Or.js +0 -7
package/server/cli.js
CHANGED
|
@@ -740,6 +740,8 @@ function showStatus(options = {}) {
|
|
|
740
740
|
|
|
741
741
|
// Show help
|
|
742
742
|
function showHelp() {
|
|
743
|
+
const defaultProjectUrl = 'https://github.com/lintendo/AI-Web-UI';
|
|
744
|
+
const defaultIssuesUrl = `${defaultProjectUrl}/issues`;
|
|
743
745
|
console.log(`
|
|
744
746
|
╔═══════════════════════════════════════════════════════════════╗
|
|
745
747
|
║ Axhub Genie - Command Line Tool ║
|
|
@@ -805,10 +807,10 @@ Environment Variables:
|
|
|
805
807
|
AXHUB_GENIE_NO_OPEN Set to true to disable auto-opening the homepage
|
|
806
808
|
|
|
807
809
|
Documentation:
|
|
808
|
-
${packageJson.homepage ||
|
|
810
|
+
${packageJson.homepage || defaultProjectUrl}
|
|
809
811
|
|
|
810
812
|
Report Issues:
|
|
811
|
-
${packageJson.bugs?.url ||
|
|
813
|
+
${packageJson.bugs?.url || defaultIssuesUrl}
|
|
812
814
|
`);
|
|
813
815
|
}
|
|
814
816
|
|
|
@@ -817,6 +819,10 @@ function showVersion() {
|
|
|
817
819
|
console.log(`${packageJson.version}`);
|
|
818
820
|
}
|
|
819
821
|
|
|
822
|
+
function getManualUpdateCommand(packageName = packageJson.name || '@axhub/genie') {
|
|
823
|
+
return `npm install -g ${packageName}@latest`;
|
|
824
|
+
}
|
|
825
|
+
|
|
820
826
|
// Compare semver versions, returns true if v1 > v2
|
|
821
827
|
function isNewerVersion(v1, v2) {
|
|
822
828
|
const parts1 = v1.split('.').map(Number);
|
|
@@ -838,7 +844,7 @@ async function checkForUpdates(silent = false) {
|
|
|
838
844
|
|
|
839
845
|
if (isNewerVersion(latestVersion, currentVersion)) {
|
|
840
846
|
console.log(`\n${c.warn('[UPDATE]')} New version available: ${c.bright(latestVersion)} (current: ${currentVersion})`);
|
|
841
|
-
console.log(` Run ${c.bright(
|
|
847
|
+
console.log(` Run ${c.bright(getManualUpdateCommand(packageName))} to update\n`);
|
|
842
848
|
return { hasUpdate: true, latestVersion, currentVersion };
|
|
843
849
|
} else if (!silent) {
|
|
844
850
|
console.log(`${c.ok('[OK]')} You are on the latest version (${currentVersion})`);
|
|
@@ -867,11 +873,11 @@ async function updatePackage() {
|
|
|
867
873
|
}
|
|
868
874
|
|
|
869
875
|
console.log(`${c.info('[INFO]')} Updating from ${currentVersion} to ${latestVersion}...`);
|
|
870
|
-
execSync(
|
|
876
|
+
execSync(getManualUpdateCommand(packageName), { stdio: 'inherit' });
|
|
871
877
|
console.log(`${c.ok('[OK]')} Update complete! Restart axhub-genie to use the new version.`);
|
|
872
878
|
} catch (e) {
|
|
873
879
|
console.error(`${c.error('[ERROR]')} Update failed: ${e.message}`);
|
|
874
|
-
console.log(`${c.tip('[TIP]')} Try running manually:
|
|
880
|
+
console.log(`${c.tip('[TIP]')} Try running manually: ${getManualUpdateCommand()}`);
|
|
875
881
|
}
|
|
876
882
|
}
|
|
877
883
|
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { promises as fs } from 'fs';
|
|
2
2
|
|
|
3
3
|
import { addProjectManually } from '../projects.js';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
import {
|
|
5
|
+
applyConversationEventToTimelineMessages,
|
|
6
|
+
CONVERSATION_EVENT_KINDS
|
|
7
|
+
} from '../../shared/conversationEvents.js';
|
|
8
|
+
import { executeAgentPrompt } from '../acp-runtime/index.js';
|
|
9
|
+
import { detectProviderInstallationStatus } from '../routes/cli-auth.js';
|
|
8
10
|
import {
|
|
9
11
|
buildAgentCallbackPayload,
|
|
10
12
|
createAgentCallbackEventId,
|
|
@@ -15,7 +17,6 @@ import {
|
|
|
15
17
|
} from '../utils/agentCallback.js';
|
|
16
18
|
import { normalizeExternalImages } from '../utils/agentImages.js';
|
|
17
19
|
import { resolveWorkingDirectory } from '../utils/defaultWorkingDirectory.js';
|
|
18
|
-
import { CODEX_MODELS, GEMINI_MODELS, OPENCODE_MODELS } from '../../shared/modelConstants.js';
|
|
19
20
|
|
|
20
21
|
export const SUPPORTED_EXTERNAL_AGENT_PROVIDERS = ['claude', 'codex', 'gemini', 'opencode'];
|
|
21
22
|
|
|
@@ -38,6 +39,29 @@ function getDeprecatedGitHubFields(body) {
|
|
|
38
39
|
return deprecatedFields.filter((field) => body[field] !== undefined);
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
async function ensureProviderInstalled(provider) {
|
|
43
|
+
const status = await detectProviderInstallationStatus(provider);
|
|
44
|
+
if (status?.installed) {
|
|
45
|
+
return status;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const installHint = typeof status?.installHint === 'string' && status.installHint.trim()
|
|
49
|
+
? status.installHint.trim()
|
|
50
|
+
: null;
|
|
51
|
+
const reason = typeof status?.reason === 'string' && status.reason.trim()
|
|
52
|
+
? status.reason.trim()
|
|
53
|
+
: null;
|
|
54
|
+
|
|
55
|
+
const messageParts = [`Provider "${provider}" is not installed.`];
|
|
56
|
+
if (installHint) {
|
|
57
|
+
messageParts.push(installHint);
|
|
58
|
+
} else if (reason) {
|
|
59
|
+
messageParts.push(reason);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
throw createRequestError(messageParts.join(' '), 400);
|
|
63
|
+
}
|
|
64
|
+
|
|
41
65
|
export function buildSessionNavigation(sessionId) {
|
|
42
66
|
const normalizedSessionId = typeof sessionId === 'string' && sessionId.trim() ? sessionId.trim() : null;
|
|
43
67
|
const sessionPath = normalizedSessionId ? `/session/${normalizedSessionId}` : null;
|
|
@@ -250,7 +274,7 @@ function extractTerminalErrorMessage(payload) {
|
|
|
250
274
|
export class CallbackCaptureWriter {
|
|
251
275
|
constructor() {
|
|
252
276
|
this.sessionId = null;
|
|
253
|
-
this.
|
|
277
|
+
this.timelineMessages = [];
|
|
254
278
|
this.tokenSummary = createEmptyTokenSummary();
|
|
255
279
|
this.terminalState = null;
|
|
256
280
|
this.terminalErrorMessage = null;
|
|
@@ -279,24 +303,34 @@ export class CallbackCaptureWriter {
|
|
|
279
303
|
};
|
|
280
304
|
}
|
|
281
305
|
|
|
282
|
-
if (payload.type === '
|
|
283
|
-
this.
|
|
284
|
-
|
|
306
|
+
if (payload.type === 'conversation-event' && payload.event) {
|
|
307
|
+
this.timelineMessages = applyConversationEventToTimelineMessages(
|
|
308
|
+
this.timelineMessages,
|
|
309
|
+
payload.event,
|
|
310
|
+
payload.event.provider || 'claude'
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
if (payload.event.kind === CONVERSATION_EVENT_KINDS.ERROR) {
|
|
314
|
+
this.terminalState = 'errored';
|
|
315
|
+
this.terminalErrorMessage = payload.event.payload?.message || this.terminalErrorMessage || 'Agent session failed';
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
285
318
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
content: payload.data.content
|
|
319
|
+
if (payload.event.kind === CONVERSATION_EVENT_KINDS.SESSION_STATE_CHANGED) {
|
|
320
|
+
const nextState = String(payload.event.payload?.state || '').trim().toLowerCase();
|
|
321
|
+
if (nextState === 'completed') {
|
|
322
|
+
this.terminalState = 'completed';
|
|
323
|
+
this.terminalErrorMessage = null;
|
|
324
|
+
} else if (nextState === 'aborted') {
|
|
325
|
+
this.terminalState = 'aborted';
|
|
326
|
+
this.terminalErrorMessage = 'Session aborted';
|
|
327
|
+
} else if (nextState === 'errored') {
|
|
328
|
+
this.terminalState = 'errored';
|
|
329
|
+
this.terminalErrorMessage = payload.event.payload?.message || this.terminalErrorMessage || 'Agent session failed';
|
|
298
330
|
}
|
|
299
|
-
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return;
|
|
300
334
|
}
|
|
301
335
|
|
|
302
336
|
if (payload.type === 'session-aborted') {
|
|
@@ -335,7 +369,16 @@ export class CallbackCaptureWriter {
|
|
|
335
369
|
}
|
|
336
370
|
|
|
337
371
|
getAssistantMessages() {
|
|
338
|
-
return this.
|
|
372
|
+
return this.timelineMessages
|
|
373
|
+
.filter((message) => message?.type === 'assistant' && !message?.isToolUse && !message?.isSystemNotice)
|
|
374
|
+
.map((message) => ({
|
|
375
|
+
type: 'assistant',
|
|
376
|
+
message: {
|
|
377
|
+
role: 'assistant',
|
|
378
|
+
content: message.content || ''
|
|
379
|
+
},
|
|
380
|
+
timestamp: message.timestamp || null
|
|
381
|
+
}));
|
|
339
382
|
}
|
|
340
383
|
|
|
341
384
|
getTotalTokens() {
|
|
@@ -394,52 +437,19 @@ async function ensureProjectPathExists(projectPath) {
|
|
|
394
437
|
}
|
|
395
438
|
|
|
396
439
|
async function runProviderSession({ provider, message, images, finalProjectPath, sessionId, model, writer }) {
|
|
397
|
-
|
|
398
|
-
|
|
440
|
+
await executeAgentPrompt({
|
|
441
|
+
agentKey: provider,
|
|
442
|
+
command: message,
|
|
443
|
+
options: {
|
|
399
444
|
projectPath: finalProjectPath,
|
|
400
445
|
cwd: finalProjectPath,
|
|
401
446
|
sessionId,
|
|
402
447
|
model,
|
|
403
448
|
permissionMode: 'bypassPermissions',
|
|
404
449
|
images
|
|
405
|
-
},
|
|
406
|
-
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
if (provider === 'codex') {
|
|
410
|
-
await queryCodex(message, {
|
|
411
|
-
projectPath: finalProjectPath,
|
|
412
|
-
cwd: finalProjectPath,
|
|
413
|
-
sessionId,
|
|
414
|
-
model: model || CODEX_MODELS.DEFAULT,
|
|
415
|
-
permissionMode: 'bypassPermissions',
|
|
416
|
-
images
|
|
417
|
-
}, writer);
|
|
418
|
-
return;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
if (provider === 'gemini') {
|
|
422
|
-
await queryGemini(message, {
|
|
423
|
-
projectPath: finalProjectPath,
|
|
424
|
-
cwd: finalProjectPath,
|
|
425
|
-
sessionId,
|
|
426
|
-
resume: !!sessionId,
|
|
427
|
-
model: model || GEMINI_MODELS.DEFAULT,
|
|
428
|
-
permissionMode: 'bypassPermissions'
|
|
429
|
-
}, writer);
|
|
430
|
-
return;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
if (provider === 'opencode') {
|
|
434
|
-
await queryOpencode(message, {
|
|
435
|
-
projectPath: finalProjectPath,
|
|
436
|
-
cwd: finalProjectPath,
|
|
437
|
-
sessionId,
|
|
438
|
-
resume: !!sessionId,
|
|
439
|
-
model: model || OPENCODE_MODELS.DEFAULT,
|
|
440
|
-
permissionMode: 'bypassPermissions'
|
|
441
|
-
}, writer);
|
|
442
|
-
}
|
|
450
|
+
},
|
|
451
|
+
writer
|
|
452
|
+
});
|
|
443
453
|
}
|
|
444
454
|
|
|
445
455
|
export async function runExternalAgentRequest({
|
|
@@ -459,6 +469,7 @@ export async function runExternalAgentRequest({
|
|
|
459
469
|
const response = {
|
|
460
470
|
success: true,
|
|
461
471
|
openOnly: true,
|
|
472
|
+
runtime: 'acp',
|
|
462
473
|
sessionId: normalized.normalizedSessionId,
|
|
463
474
|
...buildSessionNavigation(normalized.normalizedSessionId),
|
|
464
475
|
isResumed: true,
|
|
@@ -467,6 +478,7 @@ export async function runExternalAgentRequest({
|
|
|
467
478
|
|
|
468
479
|
transportWriter?.send?.({
|
|
469
480
|
type: 'open-only',
|
|
481
|
+
runtime: 'acp',
|
|
470
482
|
...response
|
|
471
483
|
});
|
|
472
484
|
|
|
@@ -484,6 +496,7 @@ export async function runExternalAgentRequest({
|
|
|
484
496
|
let callbackResult = null;
|
|
485
497
|
|
|
486
498
|
try {
|
|
499
|
+
await ensureProviderInstalled(normalized.provider);
|
|
487
500
|
await ensureProjectPathExists(finalProjectPath);
|
|
488
501
|
|
|
489
502
|
try {
|
|
@@ -525,6 +538,7 @@ export async function runExternalAgentRequest({
|
|
|
525
538
|
const response = {
|
|
526
539
|
success: true,
|
|
527
540
|
sessionId: callbackSessionId,
|
|
541
|
+
runtime: 'acp',
|
|
528
542
|
...navigation,
|
|
529
543
|
isResumed: !!normalized.normalizedSessionId,
|
|
530
544
|
messages: callbackCaptureWriter.getAssistantMessages(),
|
package/server/gemini-cli.js
CHANGED
|
@@ -1,368 +1,31 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
function
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
});
|
|
19
|
-
return chunks;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (typeof payload !== 'object') {
|
|
23
|
-
return chunks;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const directKeys = ['text', 'response', 'content', 'message', 'delta'];
|
|
27
|
-
for (const key of directKeys) {
|
|
28
|
-
if (payload[key] !== undefined) {
|
|
29
|
-
chunks.push(...collectTextChunks(payload[key]));
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (Array.isArray(payload.parts)) {
|
|
34
|
-
payload.parts.forEach(part => {
|
|
35
|
-
chunks.push(...collectTextChunks(part));
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (payload.data && typeof payload.data === 'object') {
|
|
40
|
-
chunks.push(...collectTextChunks(payload.data));
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return chunks;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function getEventRole(event) {
|
|
47
|
-
return (
|
|
48
|
-
event?.role ||
|
|
49
|
-
event?.author ||
|
|
50
|
-
event?.sender ||
|
|
51
|
-
event?.message?.role ||
|
|
52
|
-
event?.data?.role ||
|
|
53
|
-
event?.content?.role ||
|
|
54
|
-
null
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function extractAssistantTextChunks(event, command) {
|
|
59
|
-
const role = String(getEventRole(event) || '').toLowerCase();
|
|
60
|
-
if (role === 'user') return [];
|
|
61
|
-
|
|
62
|
-
const eventType = String(event?.type || event?.event || event?.kind || '').toLowerCase();
|
|
63
|
-
const hasAssistantPayload = !!(event?.response || event?.candidates || event?.delta || event?.text || event?.content || event?.message);
|
|
64
|
-
if (eventType.includes('prompt') && !hasAssistantPayload) {
|
|
65
|
-
return [];
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const normalizedPrompt = String(command || '').trim();
|
|
69
|
-
return collectTextChunks(event).filter((text) => {
|
|
70
|
-
const trimmed = String(text || '').trim();
|
|
71
|
-
if (!trimmed) return false;
|
|
72
|
-
if (normalizedPrompt && trimmed === normalizedPrompt) return false;
|
|
73
|
-
return true;
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function parseGeminiJsonLine(line) {
|
|
78
|
-
const trimmed = line.trim();
|
|
79
|
-
if (!trimmed) return null;
|
|
80
|
-
|
|
81
|
-
const payload = trimmed.startsWith('data:') ? trimmed.slice(5).trim() : trimmed;
|
|
82
|
-
if (!payload) return null;
|
|
83
|
-
|
|
84
|
-
return JSON.parse(payload);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function emitTextChunks(ws, sessionId, chunks) {
|
|
88
|
-
chunks.forEach((text) => {
|
|
89
|
-
if (!text) return;
|
|
90
|
-
ws.send({
|
|
91
|
-
type: 'claude-response',
|
|
92
|
-
data: {
|
|
93
|
-
type: 'content_block_delta',
|
|
94
|
-
delta: {
|
|
95
|
-
type: 'text_delta',
|
|
96
|
-
text
|
|
97
|
-
}
|
|
98
|
-
},
|
|
99
|
-
provider: 'gemini',
|
|
100
|
-
sessionId
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function formatGeminiProcessError(code, stderrOutput) {
|
|
106
|
-
const normalizedStderr = String(stderrOutput || '').trim();
|
|
107
|
-
if (normalizedStderr) {
|
|
108
|
-
const apiMessageMatch =
|
|
109
|
-
normalizedStderr.match(/"message":"([^"]+)"/) ||
|
|
110
|
-
normalizedStderr.match(/message:\s*"([^"]+)"/i) ||
|
|
111
|
-
normalizedStderr.match(/ApiError:\s*\{.*?"message":"([^"]+)"/i) ||
|
|
112
|
-
normalizedStderr.match(/Error when talking to Gemini API[\s\S]*?message":"([^"]+)"/i);
|
|
113
|
-
|
|
114
|
-
if (apiMessageMatch?.[1]) {
|
|
115
|
-
return apiMessageMatch[1];
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const firstMeaningfulLine = normalizedStderr
|
|
119
|
-
.split('\n')
|
|
120
|
-
.map((line) => line.trim())
|
|
121
|
-
.find((line) => line && !line.startsWith('at '));
|
|
122
|
-
|
|
123
|
-
if (firstMeaningfulLine) {
|
|
124
|
-
return firstMeaningfulLine;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return normalizedStderr;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return `Gemini CLI exited with code ${code}`;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function extractGeminiEventError(event) {
|
|
134
|
-
if (!event || typeof event !== 'object') {
|
|
135
|
-
return null;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const candidates = [
|
|
139
|
-
event?.error?.message,
|
|
140
|
-
event?.error?.details,
|
|
141
|
-
event?.result?.error?.message,
|
|
142
|
-
event?.result?.error,
|
|
143
|
-
event?.data?.error?.message,
|
|
144
|
-
event?.data?.error,
|
|
145
|
-
event?.message
|
|
146
|
-
];
|
|
147
|
-
|
|
148
|
-
for (const candidate of candidates) {
|
|
149
|
-
const normalized = String(candidate || '').trim();
|
|
150
|
-
if (normalized) {
|
|
151
|
-
return normalized;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return null;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
async function queryGemini(command, options = {}, ws) {
|
|
159
|
-
return new Promise((resolve, reject) => {
|
|
160
|
-
const { sessionId, cwd, projectPath, resume, model, permissionMode } = options;
|
|
161
|
-
let capturedSessionId = sessionId;
|
|
162
|
-
let sentSessionCreated = false;
|
|
163
|
-
let stderrBuffer = '';
|
|
164
|
-
let fatalErrorSent = false;
|
|
165
|
-
let sawSuccessfulTerminalEvent = false;
|
|
166
|
-
let sawErroredTerminalEvent = false;
|
|
167
|
-
|
|
168
|
-
const args = ['-y', '@google/gemini-cli'];
|
|
169
|
-
|
|
170
|
-
if (sessionId && (resume || !command || !command.trim())) {
|
|
171
|
-
args.push('--resume', sessionId);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (command && command.trim()) {
|
|
175
|
-
args.push('--prompt', command);
|
|
176
|
-
args.push('--output-format', 'stream-json');
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
if (model) {
|
|
180
|
-
args.push('--model', model);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
if (permissionMode === 'bypassPermissions' || permissionMode === 'acceptEdits') {
|
|
184
|
-
args.push('--yolo');
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const workingDir = resolveWorkingDirectory({ cwd, projectPath });
|
|
188
|
-
|
|
189
|
-
const geminiProcess = spawnFunction('npx', args, {
|
|
190
|
-
cwd: workingDir,
|
|
191
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
192
|
-
env: {
|
|
193
|
-
...process.env,
|
|
194
|
-
GEMINI_NONINTERACTIVE: '1'
|
|
195
|
-
}
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
const processKey = capturedSessionId || Date.now().toString();
|
|
199
|
-
let processRegistryKey = processKey;
|
|
200
|
-
activeGeminiProcesses.set(processRegistryKey, geminiProcess);
|
|
201
|
-
|
|
202
|
-
const finalizeSessionId = () => capturedSessionId || sessionId || null;
|
|
203
|
-
const emitFatalError = (errorMessage) => {
|
|
204
|
-
if (fatalErrorSent || !errorMessage) {
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
fatalErrorSent = true;
|
|
209
|
-
ws.send({
|
|
210
|
-
type: 'claude-error',
|
|
211
|
-
error: errorMessage,
|
|
212
|
-
provider: 'gemini',
|
|
213
|
-
sessionId: finalizeSessionId()
|
|
214
|
-
});
|
|
215
|
-
};
|
|
216
|
-
|
|
217
|
-
const handleJsonEvent = (event) => {
|
|
218
|
-
const incomingSessionId = event?.session_id || event?.sessionId || event?.data?.session_id || event?.data?.sessionId;
|
|
219
|
-
if (incomingSessionId && !capturedSessionId) {
|
|
220
|
-
capturedSessionId = incomingSessionId;
|
|
221
|
-
if (ws.setSessionId && typeof ws.setSessionId === 'function') {
|
|
222
|
-
ws.setSessionId(capturedSessionId);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
if (processRegistryKey !== capturedSessionId) {
|
|
226
|
-
activeGeminiProcesses.delete(processRegistryKey);
|
|
227
|
-
activeGeminiProcesses.set(capturedSessionId, geminiProcess);
|
|
228
|
-
processRegistryKey = capturedSessionId;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (!sessionId && !sentSessionCreated) {
|
|
232
|
-
sentSessionCreated = true;
|
|
233
|
-
ws.send({
|
|
234
|
-
type: 'session-created',
|
|
235
|
-
sessionId: capturedSessionId,
|
|
236
|
-
provider: 'gemini'
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
const type = event?.type || event?.event || event?.kind;
|
|
242
|
-
if (type === 'result' || type === 'done' || type === 'complete') {
|
|
243
|
-
const eventStatus = String(event?.status || event?.result?.status || '').trim().toLowerCase();
|
|
244
|
-
const eventError = extractGeminiEventError(event);
|
|
245
|
-
if (eventStatus === 'error' || eventError) {
|
|
246
|
-
sawErroredTerminalEvent = true;
|
|
247
|
-
emitFatalError(eventError || 'Gemini CLI request failed');
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
sawSuccessfulTerminalEvent = true;
|
|
252
|
-
ws.send({
|
|
253
|
-
type: 'claude-response',
|
|
254
|
-
data: { type: 'content_block_stop' },
|
|
255
|
-
provider: 'gemini',
|
|
256
|
-
sessionId: finalizeSessionId()
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
ws.send({
|
|
260
|
-
type: 'gemini-result',
|
|
261
|
-
data: event,
|
|
262
|
-
sessionId: finalizeSessionId()
|
|
263
|
-
});
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
const textChunks = extractAssistantTextChunks(event, command);
|
|
268
|
-
emitTextChunks(ws, finalizeSessionId(), textChunks);
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
let stdoutBuffer = '';
|
|
272
|
-
geminiProcess.stdout.on('data', (data) => {
|
|
273
|
-
stdoutBuffer += data.toString();
|
|
274
|
-
const lines = stdoutBuffer.split('\n');
|
|
275
|
-
stdoutBuffer = lines.pop() || '';
|
|
276
|
-
|
|
277
|
-
for (const line of lines) {
|
|
278
|
-
try {
|
|
279
|
-
const event = parseGeminiJsonLine(line);
|
|
280
|
-
if (!event) continue;
|
|
281
|
-
handleJsonEvent(event);
|
|
282
|
-
} catch {}
|
|
283
|
-
}
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
geminiProcess.stderr.on('data', (data) => {
|
|
287
|
-
const chunk = data.toString();
|
|
288
|
-
stderrBuffer += chunk;
|
|
289
|
-
|
|
290
|
-
const normalizedChunk = chunk.trim();
|
|
291
|
-
if (normalizedChunk) {
|
|
292
|
-
console.warn('Gemini CLI stderr:', normalizedChunk);
|
|
293
|
-
}
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
geminiProcess.on('close', (code) => {
|
|
297
|
-
if (stdoutBuffer.trim()) {
|
|
298
|
-
try {
|
|
299
|
-
const finalEvent = parseGeminiJsonLine(stdoutBuffer);
|
|
300
|
-
if (finalEvent) {
|
|
301
|
-
handleJsonEvent(finalEvent);
|
|
302
|
-
}
|
|
303
|
-
} catch {}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
activeGeminiProcesses.delete(processRegistryKey);
|
|
307
|
-
|
|
308
|
-
if (code === 0 && !sawErroredTerminalEvent) {
|
|
309
|
-
if (!sawSuccessfulTerminalEvent) {
|
|
310
|
-
ws.send({
|
|
311
|
-
type: 'claude-response',
|
|
312
|
-
data: { type: 'content_block_stop' },
|
|
313
|
-
provider: 'gemini',
|
|
314
|
-
sessionId: finalizeSessionId()
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
ws.send({
|
|
319
|
-
type: 'claude-complete',
|
|
320
|
-
sessionId: finalizeSessionId(),
|
|
321
|
-
provider: 'gemini',
|
|
322
|
-
exitCode: code,
|
|
323
|
-
isNewSession: !sessionId && !!command
|
|
324
|
-
});
|
|
325
|
-
resolve();
|
|
326
|
-
} else {
|
|
327
|
-
const errorMessage = formatGeminiProcessError(code, stderrBuffer);
|
|
328
|
-
emitFatalError(errorMessage);
|
|
329
|
-
reject(new Error(errorMessage));
|
|
330
|
-
}
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
geminiProcess.on('error', (error) => {
|
|
334
|
-
activeGeminiProcesses.delete(processRegistryKey);
|
|
335
|
-
|
|
336
|
-
emitFatalError(error.message);
|
|
337
|
-
|
|
338
|
-
reject(error);
|
|
339
|
-
});
|
|
340
|
-
|
|
341
|
-
geminiProcess.stdin.end();
|
|
1
|
+
import {
|
|
2
|
+
abortAgentSession,
|
|
3
|
+
executeAgentPrompt,
|
|
4
|
+
getActiveAgentSessions,
|
|
5
|
+
isAgentSessionActive
|
|
6
|
+
} from './acp-runtime/index.js';
|
|
7
|
+
import { GEMINI_MODELS } from '../shared/modelConstants.js';
|
|
8
|
+
|
|
9
|
+
export async function queryGemini(command, options = {}, writer) {
|
|
10
|
+
return executeAgentPrompt({
|
|
11
|
+
agentKey: 'gemini',
|
|
12
|
+
command,
|
|
13
|
+
options: {
|
|
14
|
+
...options,
|
|
15
|
+
model: options.model || GEMINI_MODELS.DEFAULT
|
|
16
|
+
},
|
|
17
|
+
writer
|
|
342
18
|
});
|
|
343
19
|
}
|
|
344
20
|
|
|
345
|
-
function abortGeminiSession(sessionId) {
|
|
346
|
-
|
|
347
|
-
if (process) {
|
|
348
|
-
process.kill('SIGTERM');
|
|
349
|
-
activeGeminiProcesses.delete(sessionId);
|
|
350
|
-
return true;
|
|
351
|
-
}
|
|
352
|
-
return false;
|
|
21
|
+
export async function abortGeminiSession(sessionId) {
|
|
22
|
+
return abortAgentSession('gemini', sessionId);
|
|
353
23
|
}
|
|
354
24
|
|
|
355
|
-
function isGeminiSessionActive(sessionId) {
|
|
356
|
-
return
|
|
25
|
+
export function isGeminiSessionActive(sessionId) {
|
|
26
|
+
return isAgentSessionActive('gemini', sessionId);
|
|
357
27
|
}
|
|
358
28
|
|
|
359
|
-
function getActiveGeminiSessions() {
|
|
360
|
-
return
|
|
29
|
+
export function getActiveGeminiSessions() {
|
|
30
|
+
return getActiveAgentSessions('gemini');
|
|
361
31
|
}
|
|
362
|
-
|
|
363
|
-
export {
|
|
364
|
-
queryGemini,
|
|
365
|
-
abortGeminiSession,
|
|
366
|
-
isGeminiSessionActive,
|
|
367
|
-
getActiveGeminiSessions
|
|
368
|
-
};
|