@axhub/genie 0.2.7 → 0.2.9

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.
Files changed (131) hide show
  1. package/LICENSE +21 -675
  2. package/dist/api-docs.html +2 -2
  3. package/dist/assets/App-GBcTeeUS.js +460 -0
  4. package/dist/assets/App-qxJ8_QYu.css +32 -0
  5. package/dist/assets/ReviewApp-C9K--AQE.js +1 -0
  6. package/dist/assets/{_basePickBy-C19AekOu.js → _basePickBy-DR_8uFCo.js} +1 -1
  7. package/dist/assets/{_baseUniq-JsnevLw_.js → _baseUniq-D0njlQ_7.js} +1 -1
  8. package/dist/assets/{arc-BLpcuBlf.js → arc-CKlr_Rec.js} +1 -1
  9. package/dist/assets/architectureDiagram-2XIMDMQ5-BmO_uLUH.js +36 -0
  10. package/dist/assets/{blockDiagram-WCTKOSBZ-DQBLwsUS.js → blockDiagram-WCTKOSBZ-DhAeO-56.js} +3 -3
  11. package/dist/assets/c4Diagram-IC4MRINW-C67kFoXx.js +10 -0
  12. package/dist/assets/channel-V3MBjKys.js +1 -0
  13. package/dist/assets/{chunk-4BX2VUAB-De63kbgc.js → chunk-4BX2VUAB-mLLagvJi.js} +1 -1
  14. package/dist/assets/{chunk-55IACEB6-DtTDDdM9.js → chunk-55IACEB6-Lx-hOjlM.js} +1 -1
  15. package/dist/assets/{chunk-FMBD7UC4-DHuwd8tw.js → chunk-FMBD7UC4-Bt-XmVUV.js} +1 -1
  16. package/dist/assets/{chunk-JSJVCQXG-BgytFtmO.js → chunk-JSJVCQXG-Cya6gaDV.js} +1 -1
  17. package/dist/assets/{chunk-KX2RTZJC-nZdp86aN.js → chunk-KX2RTZJC-Bd7Ig6tF.js} +1 -1
  18. package/dist/assets/chunk-NQ4KR5QH-5UAE0Vg-.js +220 -0
  19. package/dist/assets/{chunk-QZHKN3VN-DvUQ3mnO.js → chunk-QZHKN3VN-BAxZ8m7w.js} +1 -1
  20. package/dist/assets/chunk-WL4C6EOR-DjDPvUUP.js +189 -0
  21. package/dist/assets/classDiagram-VBA2DB6C-C790yYiY.js +1 -0
  22. package/dist/assets/classDiagram-v2-RAHNMMFH-C790yYiY.js +1 -0
  23. package/dist/assets/clone-BbMGfZwt.js +1 -0
  24. package/dist/assets/cose-bilkent-S5V4N54A-D-60XrkJ.js +1 -0
  25. package/dist/assets/cytoscape.esm-2ZfV8NB5.js +331 -0
  26. package/dist/assets/{dagre-KLK3FWXG-CHYIvW47.js → dagre-KLK3FWXG-bqu3ZS4K.js} +1 -1
  27. package/dist/assets/diagram-E7M64L7V-BueeqoYm.js +24 -0
  28. package/dist/assets/{diagram-IFDJBPK2-Dzsiln_C.js → diagram-IFDJBPK2-D4fDv2E7.js} +1 -1
  29. package/dist/assets/{diagram-P4PSJMXO-DKnGbUpE.js → diagram-P4PSJMXO-WqipY3fN.js} +1 -1
  30. package/dist/assets/erDiagram-INFDFZHY-D0oVnO-x.js +70 -0
  31. package/dist/assets/{flowDiagram-PKNHOUZH-BAZ2-jKp.js → flowDiagram-PKNHOUZH-DzbGyxrr.js} +4 -4
  32. package/dist/assets/ganttDiagram-A5KZAMGK-BwhbbgCP.js +292 -0
  33. package/dist/assets/{gitGraphDiagram-K3NZZRJ6-BflpyjGy.js → gitGraphDiagram-K3NZZRJ6-DZgAh_KM.js} +1 -1
  34. package/dist/assets/{graph-suelaXFh.js → graph-DzKos-N0.js} +1 -1
  35. package/dist/assets/highlighted-body-TPN3WLV5-CKDMgz3X.js +1 -0
  36. package/dist/assets/index-DiQlHzGj.js +2 -0
  37. package/dist/assets/index-Drat2nB9.css +1 -0
  38. package/dist/assets/{infoDiagram-LFFYTUFH-pfD1FA3p.js → infoDiagram-LFFYTUFH-BFicZbTf.js} +1 -1
  39. package/dist/assets/ishikawaDiagram-PHBUUO56-CtihxDxl.js +70 -0
  40. package/dist/assets/journeyDiagram-4ABVD52K-Du00J8_d.js +139 -0
  41. package/dist/assets/{kanban-definition-K7BYSVSG-FWinmur1.js → kanban-definition-K7BYSVSG-BJi9S0iQ.js} +5 -5
  42. package/dist/assets/{layout-vcz43XvZ.js → layout-B80Sityu.js} +1 -1
  43. package/dist/assets/{linear-le4gc0vx.js → linear-sRQLOf5H.js} +1 -1
  44. package/dist/assets/mermaid-O7DHMXV3-CBuVs4eJ.js +1038 -0
  45. package/dist/assets/mindmap-definition-YRQLILUH-C5IL_xi-.js +68 -0
  46. package/dist/assets/{pieDiagram-SKSYHLDU-C7PKDh3b.js → pieDiagram-SKSYHLDU-CeTwlJ8z.js} +2 -2
  47. package/dist/assets/quadrantDiagram-337W2JSQ-COfUcLWt.js +7 -0
  48. package/dist/assets/requirementDiagram-Z7DCOOCP-DSb-CJ5B.js +73 -0
  49. package/dist/assets/{sankeyDiagram-WA2Y5GQK-4gulcOP4.js → sankeyDiagram-WA2Y5GQK-8jtuVb45.js} +3 -3
  50. package/dist/assets/sequenceDiagram-2WXFIKYE-C2VpkMwA.js +145 -0
  51. package/dist/assets/{stateDiagram-RAJIS63D-CB4Vl7qM.js → stateDiagram-RAJIS63D-fmwMqxxc.js} +1 -1
  52. package/dist/assets/stateDiagram-v2-FVOUBMTO-9GGXVWrR.js +1 -0
  53. package/dist/assets/timeline-definition-YZTLITO2-Dx1hP5lg.js +61 -0
  54. package/dist/assets/{treemap-KZPCXAKY-DZSEE6Hz.js → treemap-KZPCXAKY-CkLOdYCZ.js} +58 -58
  55. package/dist/assets/vendor-codemirror-BxPY6emf.js +39 -0
  56. package/dist/assets/vendor-react-xmA_f8ig.js +59 -0
  57. package/dist/assets/vendor-xterm-DfaPXD3y.js +66 -0
  58. package/dist/assets/{vennDiagram-LZ73GAT5-8E_G06fI.js → vennDiagram-LZ73GAT5-D6KWcnln.js} +4 -4
  59. package/dist/assets/xychartDiagram-JWTSCODW-6fh6qmzN.js +7 -0
  60. package/dist/index.html +5 -5
  61. package/package.json +36 -35
  62. package/server/acp-runtime/client.js +91 -17
  63. package/server/acp-runtime/index.js +5 -16
  64. package/server/acp-runtime/session-store.js +4 -4
  65. package/server/channels/runtime/AgentRuntimeAdapter.js +1 -10
  66. package/server/claude-sdk.js +1 -3
  67. package/server/cli.js +159 -2
  68. package/server/external-agent/service.js +24 -6
  69. package/server/external-agent/ws.js +63 -3
  70. package/server/gemini-cli.js +1 -3
  71. package/server/index.js +120 -19
  72. package/server/openai-codex.js +1 -3
  73. package/server/opencode-cli.js +1 -3
  74. package/server/projects.js +654 -236
  75. package/server/routes/cc-connect.js +1131 -0
  76. package/server/routes/cli-auth.js +1 -73
  77. package/server/routes/commands.js +4 -9
  78. package/server/routes/projects.js +45 -24
  79. package/server/routes/session-core.js +149 -86
  80. package/server/session-core/eventStore.js +45 -18
  81. package/server/session-core/providerAdapters.js +50 -13
  82. package/server/session-core/providerDiscovery.js +8 -3
  83. package/server/session-core/runtimeState.js +8 -0
  84. package/server/utils/ccConnectManager.js +390 -0
  85. package/server/utils/ccConnectState.js +575 -0
  86. package/server/utils/resolveCommandPath.js +71 -0
  87. package/server/utils/workspaceRoots.js +154 -0
  88. package/shared/conversationEvents.js +78 -14
  89. package/dist/assets/App-BWSqiXAT.js +0 -220
  90. package/dist/assets/App-DrlLKa8f.css +0 -1
  91. package/dist/assets/ReviewApp-nz3mbArg.js +0 -1
  92. package/dist/assets/architectureDiagram-2XIMDMQ5-CarjBOOv.js +0 -36
  93. package/dist/assets/c4Diagram-IC4MRINW-CGobwBIj.js +0 -10
  94. package/dist/assets/channel-DkFNxV_H.js +0 -1
  95. package/dist/assets/chunk-NQ4KR5QH-CMH6EDP2.js +0 -220
  96. package/dist/assets/chunk-WL4C6EOR-Dn7db_6t.js +0 -189
  97. package/dist/assets/classDiagram-VBA2DB6C-DtwCEe8S.js +0 -1
  98. package/dist/assets/classDiagram-v2-RAHNMMFH-DtwCEe8S.js +0 -1
  99. package/dist/assets/clone-C0lCEIEO.js +0 -1
  100. package/dist/assets/cose-bilkent-S5V4N54A-DD_nzqsz.js +0 -1
  101. package/dist/assets/cytoscape.esm-5J0xJHOV.js +0 -321
  102. package/dist/assets/diagram-E7M64L7V-TVdvHtGc.js +0 -24
  103. package/dist/assets/erDiagram-INFDFZHY-5Kw0bByo.js +0 -70
  104. package/dist/assets/ganttDiagram-A5KZAMGK-CsADFkcq.js +0 -292
  105. package/dist/assets/highlighted-body-OFNGDK62-CZrBMazC.js +0 -1
  106. package/dist/assets/index-B01NxbUv.css +0 -1
  107. package/dist/assets/index-DW5pGgQ_.js +0 -2
  108. package/dist/assets/ishikawaDiagram-PHBUUO56-ndm9snwO.js +0 -70
  109. package/dist/assets/journeyDiagram-4ABVD52K-HgF2t7z5.js +0 -139
  110. package/dist/assets/mermaid-GHXKKRXX-CK8m3lad.js +0 -870
  111. package/dist/assets/mindmap-definition-YRQLILUH-CNq9SKj4.js +0 -68
  112. package/dist/assets/quadrantDiagram-337W2JSQ-B7FnztNO.js +0 -7
  113. package/dist/assets/requirementDiagram-Z7DCOOCP-Bl_BM2Th.js +0 -73
  114. package/dist/assets/sequenceDiagram-2WXFIKYE-VEuJDwyJ.js +0 -145
  115. package/dist/assets/stateDiagram-v2-FVOUBMTO-C85ucl39.js +0 -1
  116. package/dist/assets/timeline-definition-YZTLITO2-BPGKhi7f.js +0 -61
  117. package/dist/assets/vendor-codemirror-CyOKkaQZ.js +0 -31
  118. package/dist/assets/vendor-react-CP4yFTs7.js +0 -8
  119. package/dist/assets/vendor-xterm-DfcmCpbH.js +0 -66
  120. package/dist/assets/xychartDiagram-JWTSCODW-CbBk50-O.js +0 -7
  121. package/server/_legacy-providers/README.md +0 -30
  122. package/server/_legacy-providers/claude-sdk.js +0 -956
  123. package/server/_legacy-providers/gemini-cli.js +0 -368
  124. package/server/_legacy-providers/openai-codex.js +0 -705
  125. package/server/_legacy-providers/opencode-cli.js +0 -674
  126. package/server/acp-runtime/client.test.js +0 -688
  127. package/server/acp-runtime/session-store.test.js +0 -89
  128. package/server/cli.test.js +0 -76
  129. package/server/external-agent/service.test.js +0 -53
  130. package/server/external-agent/ws.test.js +0 -289
  131. package/shared/conversationEvents.test.js +0 -403
@@ -2,7 +2,6 @@ import fs from 'fs/promises';
2
2
  import { randomUUID } from 'crypto';
3
3
  import os from 'os';
4
4
  import path from 'path';
5
- import { spawn } from 'child_process';
6
5
  import { Readable, Writable } from 'stream';
7
6
 
8
7
  import {
@@ -13,9 +12,12 @@ import {
13
12
 
14
13
  import {
15
14
  CONVERSATION_EVENT_KINDS,
16
- createConversationEvent
15
+ createConversationEvent,
16
+ stripAssistantProtocolTags
17
17
  } from '../../shared/conversationEvents.js';
18
18
  import { parseDataUrl } from '../utils/agentImages.js';
19
+ import { spawnCommand } from '../utils/spawnCommand.js';
20
+ import { resolveCommandPath } from '../utils/resolveCommandPath.js';
19
21
  import { resolveAgentCommand } from './registry.js';
20
22
 
21
23
  const DEFAULT_TERMINAL_OUTPUT_LIMIT = 256 * 1024;
@@ -672,7 +674,7 @@ async function resolveGeminiCommand(commandLine) {
672
674
  try {
673
675
  const versionOutput = await new Promise((resolve, reject) => {
674
676
  let stdout = '';
675
- const childProcess = spawn(parsed.command, ['--version'], {
677
+ const childProcess = spawnCommand(parsed.command, ['--version'], {
676
678
  stdio: ['ignore', 'pipe', 'ignore']
677
679
  });
678
680
 
@@ -703,17 +705,41 @@ async function resolveGeminiCommand(commandLine) {
703
705
  }
704
706
  }
705
707
 
706
- async function resolveLaunchCommand(agentKey, overrides = {}) {
708
+ async function resolveNpxFallbackCommand(commandLine, agentKey) {
709
+ const parsed = splitCommandLine(commandLine);
710
+ if (parsed.command !== 'npx') {
711
+ return commandLine;
712
+ }
713
+
714
+ const npxCheck = await resolveCommandPath('npx');
715
+ if (npxCheck.found) {
716
+ return commandLine;
717
+ }
718
+
719
+ const npmCheck = await resolveCommandPath('npm');
720
+ if (npmCheck.found) {
721
+ return ['npm', 'exec', '--yes', '--', ...parsed.args].join(' ');
722
+ }
723
+
724
+ throw new Error(
725
+ `Unable to launch ${agentKey} ACP adapter because neither npx nor npm is available in PATH.`
726
+ );
727
+ }
728
+
729
+ export async function resolveLaunchCommand(agentKey, overrides = {}) {
707
730
  const commandLine = resolveAgentCommand(agentKey, overrides);
708
731
  if (!commandLine) {
709
732
  throw new Error(`Unsupported ACP agent: ${agentKey}`);
710
733
  }
711
734
 
712
- if (String(agentKey || '').trim().toLowerCase() === 'gemini') {
713
- return resolveGeminiCommand(commandLine);
735
+ const normalizedAgentKey = String(agentKey || '').trim().toLowerCase();
736
+ const resolvedCommandLine = await resolveNpxFallbackCommand(commandLine, normalizedAgentKey || 'unknown');
737
+
738
+ if (normalizedAgentKey === 'gemini') {
739
+ return resolveGeminiCommand(resolvedCommandLine);
714
740
  }
715
741
 
716
- return commandLine;
742
+ return resolvedCommandLine;
717
743
  }
718
744
 
719
745
  function buildPromptBlocks(promptText, attachments = {}) {
@@ -786,7 +812,7 @@ class LocalTerminalProcess {
786
812
  }, {})
787
813
  : {};
788
814
 
789
- this.child = spawn(this.command, this.args, {
815
+ this.child = spawnCommand(this.command, this.args, {
790
816
  cwd: this.cwd,
791
817
  env: sanitizeProcessEnv(process.env, envEntries),
792
818
  stdio: ['ignore', 'pipe', 'pipe']
@@ -881,6 +907,7 @@ export class AcpClient {
881
907
  this.isPromptInFlight = false;
882
908
  this.pendingPromptDiagnostic = null;
883
909
  this.hasReportedPromptFatalError = false;
910
+ this.agentDiagnosticBuffer = '';
884
911
  this.streamState = {
885
912
  turnId: null,
886
913
  assistantSegmentIndex: 0,
@@ -899,6 +926,7 @@ export class AcpClient {
899
926
  this.writer.send({
900
927
  provider: this.agentKey,
901
928
  sessionId: this.sessionId,
929
+ projectPath: this.projectPath,
902
930
  runtime: 'acp',
903
931
  timestamp: nowIso(),
904
932
  ...payload
@@ -1001,7 +1029,19 @@ export class AcpClient {
1001
1029
  }
1002
1030
 
1003
1031
  handleAgentDiagnostic(text) {
1004
- const fatalError = detectFatalAgentDiagnostic(this.agentKey, text);
1032
+ const diagnosticText = String(text || '').trim();
1033
+ if (!diagnosticText) {
1034
+ return;
1035
+ }
1036
+
1037
+ const nextBuffer = this.agentDiagnosticBuffer
1038
+ ? `${this.agentDiagnosticBuffer}\n${diagnosticText}`
1039
+ : diagnosticText;
1040
+ this.agentDiagnosticBuffer = nextBuffer.length > 8192
1041
+ ? nextBuffer.slice(-8192)
1042
+ : nextBuffer;
1043
+
1044
+ const fatalError = detectFatalAgentDiagnostic(this.agentKey, this.agentDiagnosticBuffer);
1005
1045
  if (!fatalError || !this.isPromptInFlight) {
1006
1046
  return;
1007
1047
  }
@@ -1210,6 +1250,11 @@ export class AcpClient {
1210
1250
 
1211
1251
  if (update.sessionUpdate === 'agent_message_chunk') {
1212
1252
  if (update.content?.type === 'text' && typeof update.content.text === 'string') {
1253
+ const cleanText = stripAssistantProtocolTags(update.content.text);
1254
+ if (!cleanText) {
1255
+ return;
1256
+ }
1257
+
1213
1258
  const messageId = this.streamState.assistantMessageId
1214
1259
  || this.createAssistantMessageId('assistant_text');
1215
1260
 
@@ -1230,7 +1275,7 @@ export class AcpClient {
1230
1275
  CONVERSATION_EVENT_KINDS.ASSISTANT_TEXT_DELTA,
1231
1276
  {
1232
1277
  messageId,
1233
- text: update.content.text
1278
+ text: cleanText
1234
1279
  },
1235
1280
  {
1236
1281
  timestamp,
@@ -1442,7 +1487,7 @@ export class AcpClient {
1442
1487
  const spawnEnvironment = await prepareSpawnEnvironment(this.agentKey);
1443
1488
  this.spawnCleanup = spawnEnvironment.cleanup;
1444
1489
 
1445
- this.childProcess = spawn(command, args, {
1490
+ this.childProcess = spawnCommand(command, args, {
1446
1491
  cwd: this.projectPath,
1447
1492
  stdio: ['pipe', 'pipe', 'pipe'],
1448
1493
  windowsHide: true,
@@ -1603,16 +1648,44 @@ export class AcpClient {
1603
1648
  }
1604
1649
 
1605
1650
  async setModel(modelId) {
1606
- if (!modelId) {
1651
+ if (!modelId || !this.sessionId || !this.connection) {
1607
1652
  return;
1608
1653
  }
1609
1654
 
1655
+ let lastError = null;
1656
+
1610
1657
  try {
1611
- await this.connection.setSessionModel({
1612
- sessionId: this.sessionId,
1613
- modelId
1614
- });
1615
- } catch {}
1658
+ if (typeof this.connection.setSessionConfigOption === 'function') {
1659
+ try {
1660
+ await this.connection.setSessionConfigOption({
1661
+ sessionId: this.sessionId,
1662
+ configId: 'model',
1663
+ value: modelId
1664
+ });
1665
+ return;
1666
+ } catch (error) {
1667
+ lastError = error;
1668
+ }
1669
+ }
1670
+
1671
+ if (typeof this.connection.unstable_setSessionModel === 'function') {
1672
+ try {
1673
+ await this.connection.unstable_setSessionModel({
1674
+ sessionId: this.sessionId,
1675
+ modelId
1676
+ });
1677
+ return;
1678
+ } catch (error) {
1679
+ lastError = error;
1680
+ }
1681
+ }
1682
+ } catch (error) {
1683
+ lastError = error;
1684
+ }
1685
+
1686
+ if (lastError) {
1687
+ console.warn(`[ACP:${this.agentKey}] Failed to set model "${modelId}": ${lastError.message}`);
1688
+ }
1616
1689
  }
1617
1690
 
1618
1691
  async setMode(modeId) {
@@ -1631,6 +1704,7 @@ export class AcpClient {
1631
1704
  async sendPrompt(promptText, attachments = {}) {
1632
1705
  this.resetTurnState();
1633
1706
  this.hasReportedPromptFatalError = false;
1707
+ this.agentDiagnosticBuffer = '';
1634
1708
  const promptPayload = buildPromptBlocks(promptText, attachments);
1635
1709
  const clientRequestId = typeof attachments?.clientRequestId === 'string' && attachments.clientRequestId.trim()
1636
1710
  ? attachments.clientRequestId.trim()
@@ -1,10 +1,4 @@
1
1
  import { resolveWorkingDirectory } from '../utils/defaultWorkingDirectory.js';
2
- import {
3
- CLAUDE_MODELS,
4
- CODEX_MODELS,
5
- GEMINI_MODELS,
6
- OPENCODE_MODELS
7
- } from '../../shared/modelConstants.js';
8
2
  import { AcpClient } from './client.js';
9
3
  import {
10
4
  findAcpSessionRecord,
@@ -54,16 +48,11 @@ function registerActiveClient(provider, sessionId, client) {
54
48
  return entry;
55
49
  }
56
50
 
57
- function resolveDefaultModel(agentKey, requestedModel) {
58
- if (requestedModel) {
59
- return requestedModel;
60
- }
61
-
62
- if (agentKey === 'claude') return CLAUDE_MODELS.DEFAULT;
63
- if (agentKey === 'codex') return CODEX_MODELS.DEFAULT;
64
- if (agentKey === 'gemini') return GEMINI_MODELS.DEFAULT;
65
- if (agentKey === 'opencode') return OPENCODE_MODELS.DEFAULT;
66
- return null;
51
+ export function resolveDefaultModel(agentKey, requestedModel) {
52
+ const normalizedRequestedModel = typeof requestedModel === 'string'
53
+ ? requestedModel.trim()
54
+ : '';
55
+ return normalizedRequestedModel || null;
67
56
  }
68
57
 
69
58
  function unregisterActiveClient(provider, sessionId, client = null) {
@@ -7,9 +7,6 @@ function parsePositiveInteger(value, fallback) {
7
7
  return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
8
8
  }
9
9
 
10
- const ACP_SESSION_ROOT = process.env.AXHUB_GENIE_ACP_SESSION_ROOT
11
- ? path.resolve(process.env.AXHUB_GENIE_ACP_SESSION_ROOT)
12
- : path.join(os.homedir(), '.axhub-genie', 'acp-sessions');
13
10
  const DEFAULT_SESSION_GC_MAX_AGE_DAYS = parsePositiveInteger(process.env.ACP_SESSION_GC_MAX_AGE_DAYS, 30);
14
11
 
15
12
  function normalizeProvider(value) {
@@ -22,7 +19,10 @@ function normalizeSessionId(value) {
22
19
  }
23
20
 
24
21
  function getProviderDir(provider) {
25
- return path.join(ACP_SESSION_ROOT, normalizeProvider(provider));
22
+ const sessionRoot = process.env.AXHUB_GENIE_ACP_SESSION_ROOT
23
+ ? path.resolve(process.env.AXHUB_GENIE_ACP_SESSION_ROOT)
24
+ : path.join(os.homedir(), '.axhub-genie', 'acp-sessions');
25
+ return path.join(sessionRoot, normalizeProvider(provider));
26
26
  }
27
27
 
28
28
  function getSessionFilePath(provider, sessionId) {
@@ -1,4 +1,3 @@
1
- import { CODEX_MODELS, GEMINI_MODELS, OPENCODE_MODELS } from '../../../shared/modelConstants.js';
2
1
  import { executeAgentPrompt } from '../../acp-runtime/index.js';
3
2
 
4
3
  function normalizeBackend(backend) {
@@ -9,13 +8,6 @@ function normalizeBackend(backend) {
9
8
  return 'codex';
10
9
  }
11
10
 
12
- function getDefaultModel(backend) {
13
- if (backend === 'codex') return CODEX_MODELS.DEFAULT;
14
- if (backend === 'gemini') return GEMINI_MODELS.DEFAULT;
15
- if (backend === 'opencode') return OPENCODE_MODELS.DEFAULT;
16
- return undefined;
17
- }
18
-
19
11
  function createWriter(callbacks) {
20
12
  const state = {
21
13
  sessionId: null,
@@ -96,7 +88,6 @@ export async function runBackendSession({
96
88
  onError,
97
89
  }) {
98
90
  const resolvedBackend = normalizeBackend(backend);
99
- const resolvedModel = model || getDefaultModel(resolvedBackend);
100
91
 
101
92
  const writer = createWriter({
102
93
  onSessionId,
@@ -108,7 +99,7 @@ export async function runBackendSession({
108
99
  projectPath,
109
100
  cwd: projectPath,
110
101
  sessionId: sessionId || undefined,
111
- model: resolvedModel,
102
+ model: model || undefined,
112
103
  permissionMode: 'bypassPermissions',
113
104
  resume: !!sessionId,
114
105
  skipPermissions: true,
@@ -5,15 +5,13 @@ import {
5
5
  isAgentSessionActive,
6
6
  resolveAgentPermission
7
7
  } from './acp-runtime/index.js';
8
- import { CLAUDE_MODELS } from '../shared/modelConstants.js';
9
8
 
10
9
  export async function queryClaudeSDK(command, options = {}, writer) {
11
10
  return executeAgentPrompt({
12
11
  agentKey: 'claude',
13
12
  command,
14
13
  options: {
15
- ...options,
16
- model: options.model || CLAUDE_MODELS.DEFAULT
14
+ ...options
17
15
  },
18
16
  writer
19
17
  });
package/server/cli.js CHANGED
@@ -24,6 +24,10 @@ import {
24
24
  DEFAULT_WORKING_DIRECTORY_ENV,
25
25
  getConfiguredDefaultProjectPath,
26
26
  } from './utils/defaultWorkingDirectory.js';
27
+ import {
28
+ getCcConnectConnectionSummary,
29
+ updateCcConnectPlatformBinding,
30
+ } from './utils/ccConnectManager.js';
27
31
 
28
32
  const __filename = fileURLToPath(import.meta.url);
29
33
  const __dirname = dirname(__filename);
@@ -374,6 +378,27 @@ function buildEditorErrorResponse({ requestId, channel = null, targetClientId =
374
378
  };
375
379
  }
376
380
 
381
+ function buildCcConnectSuccessResponse({ command, platform = null, data = {} }) {
382
+ return {
383
+ ok: true,
384
+ command,
385
+ platform,
386
+ data,
387
+ };
388
+ }
389
+
390
+ function buildCcConnectErrorResponse({ command, platform = null, code, message }) {
391
+ return {
392
+ ok: false,
393
+ command,
394
+ platform,
395
+ error: {
396
+ code,
397
+ message,
398
+ },
399
+ };
400
+ }
401
+
377
402
  function printJson(payload) {
378
403
  console.log(JSON.stringify(payload, null, 2));
379
404
  }
@@ -453,6 +478,27 @@ async function sendEditorWebSocketRequest({ apiBaseUrl, apiKey, message, timeout
453
478
  });
454
479
  }
455
480
 
481
+ const RETRYABLE_ERROR_CODES = new Set(['CONNECTION_ERROR', 'CONNECTION_CLOSED', 'REQUEST_TIMEOUT']);
482
+ const CLI_RETRY_DELAY_MS = 1000;
483
+ const CLI_MAX_RETRIES = 1;
484
+
485
+ async function sendEditorWebSocketRequestWithRetry(options) {
486
+ let lastError = null;
487
+ for (let attempt = 0; attempt <= CLI_MAX_RETRIES; attempt++) {
488
+ try {
489
+ return await sendEditorWebSocketRequest(options);
490
+ } catch (error) {
491
+ lastError = error;
492
+ if (attempt < CLI_MAX_RETRIES && RETRYABLE_ERROR_CODES.has(error?.code)) {
493
+ await new Promise((resolve) => setTimeout(resolve, CLI_RETRY_DELAY_MS));
494
+ continue;
495
+ }
496
+ throw error;
497
+ }
498
+ }
499
+ throw lastError;
500
+ }
501
+
456
502
  function resolveOutputDirPath(outputDir) {
457
503
  return path.resolve(outputDir || process.cwd());
458
504
  }
@@ -603,7 +649,7 @@ async function runEditorCommand(positionals, options) {
603
649
  const timeoutMs = parsePositiveInteger(options.timeoutMs || DEFAULT_EDITOR_TIMEOUT_MS, '--timeout-ms') || DEFAULT_EDITOR_TIMEOUT_MS;
604
650
 
605
651
  try {
606
- const response = await sendEditorWebSocketRequest({
652
+ const response = await sendEditorWebSocketRequestWithRetry({
607
653
  apiBaseUrl,
608
654
  apiKey: options.apiKey,
609
655
  message: editorRequest.message,
@@ -627,6 +673,96 @@ async function runEditorCommand(positionals, options) {
627
673
  }
628
674
  }
629
675
 
676
+ function buildCcConnectCommand(positionals, options) {
677
+ const commandPath = positionals.join(' ');
678
+ const requestedPlatform = typeof options.platform === 'string' ? options.platform.trim() : '';
679
+
680
+ if (commandPath === 'cc-connect' || commandPath === 'cc-connect status') {
681
+ return {
682
+ command: 'cc-connect status',
683
+ action: 'status',
684
+ platform: requestedPlatform || null,
685
+ };
686
+ }
687
+
688
+ if (commandPath === 'cc-connect set') {
689
+ const platform = requireOption(options, 'platform', '--platform is required');
690
+ const provider = typeof options.provider === 'string' && options.provider.trim()
691
+ ? options.provider.trim()
692
+ : null;
693
+ const projectPath = typeof options.projectPath === 'string' && options.projectPath.trim()
694
+ ? path.resolve(options.projectPath)
695
+ : null;
696
+
697
+ if (!provider && !projectPath) {
698
+ throw new Error('At least one of --provider or --project-path is required.');
699
+ }
700
+
701
+ return {
702
+ command: 'cc-connect set',
703
+ action: 'set',
704
+ platform,
705
+ provider,
706
+ projectPath,
707
+ };
708
+ }
709
+
710
+ throw new Error(`Unknown cc-connect command: ${commandPath}`);
711
+ }
712
+
713
+ async function runCcConnectCommand(positionals, options) {
714
+ const command = buildCcConnectCommand(positionals, options);
715
+
716
+ try {
717
+ if (command.action === 'status') {
718
+ const summary = await getCcConnectConnectionSummary(command.platform);
719
+ const data = command.platform
720
+ ? {
721
+ ccConnect: summary.ccConnect,
722
+ providers: summary.providers,
723
+ platform: summary.platform,
724
+ }
725
+ : {
726
+ ccConnect: summary.ccConnect,
727
+ providers: summary.providers,
728
+ platforms: summary.platforms,
729
+ };
730
+
731
+ printJson(buildCcConnectSuccessResponse({
732
+ command: command.command,
733
+ platform: command.platform,
734
+ data,
735
+ }));
736
+ return;
737
+ }
738
+
739
+ if (command.action === 'set') {
740
+ const result = await updateCcConnectPlatformBinding({
741
+ platformId: command.platform,
742
+ provider: command.provider === null ? undefined : command.provider,
743
+ projectPath: command.projectPath === null ? undefined : command.projectPath,
744
+ });
745
+
746
+ printJson(buildCcConnectSuccessResponse({
747
+ command: command.command,
748
+ platform: result.platform,
749
+ data: result,
750
+ }));
751
+ return;
752
+ }
753
+
754
+ throw new Error(`Unsupported cc-connect action: ${command.action}`);
755
+ } catch (error) {
756
+ printJson(buildCcConnectErrorResponse({
757
+ command: command.command,
758
+ platform: command.platform || null,
759
+ code: error.code || 'CC_CONNECT_COMMAND_FAILED',
760
+ message: error.message,
761
+ }));
762
+ process.exit(1);
763
+ }
764
+ }
765
+
630
766
  // Show status command
631
767
  function showStatus(options = {}) {
632
768
  const runtimeStatus = getRuntimeStatus();
@@ -754,6 +890,7 @@ Commands:
754
890
  start Start the Axhub Genie server (default)
755
891
  stop Stop the running Axhub Genie server
756
892
  status Show configuration and data locations
893
+ cc-connect Inspect or update platform connection bindings
757
894
  editor Query or control a connected web editor frontend
758
895
  update Update to the latest version
759
896
  help Show this help information
@@ -769,12 +906,15 @@ Options:
769
906
  --json Output JSON (status or editor commands)
770
907
  --api-base <url> Override API base URL for editor commands
771
908
  --api-key <key> API key for /api/agent/ws editor commands
909
+ --platform <id> Target platform for cc-connect commands
910
+ --provider <id> Provider for cc-connect updates or editor task references
772
911
  --channel <name> Integration channel for editor commands
773
912
  --target-client-id <id> Target frontend client ID for editor commands
774
913
  --client-id <id> Optional client filter for editor clients list
914
+ --project-path <path> Project path for cc-connect platform binding updates
775
915
  --element-key <key> Target element key for editor node commands
776
916
  --status <values> Comma-separated status filters for editor nodes list
777
- --state <editing|idle> Target editing state for editor editing set
917
+ --state <editing|idle|completed|error> Target editing state for editor editing set
778
918
  --limit <n> Maximum number of nodes returned by editor nodes list
779
919
  --output-dir <path> Download directory for screenshot/image export commands
780
920
  --timeout-ms <ms> Timeout for editor WebSocket requests (default: ${DEFAULT_EDITOR_TIMEOUT_MS})
@@ -790,6 +930,9 @@ Examples:
790
930
  $ axhub-genie stop # Stop running server
791
931
  $ axhub-genie status # Show configuration
792
932
  $ axhub-genie status --json # Get machine-readable status
933
+ $ axhub-genie cc-connect status
934
+ $ axhub-genie cc-connect set --platform weixin --provider codex
935
+ $ axhub-genie cc-connect set --platform feishu --project-path /path/to/repo
793
936
  $ axhub-genie editor clients list --channel project-a
794
937
  $ axhub-genie editor snapshot --channel project-a --target-client-id figma-123
795
938
  $ axhub-genie editor nodes list --channel project-a --target-client-id figma-123 --status pending-dispatch,dirty
@@ -940,10 +1083,18 @@ function parseArgs(args) {
940
1083
  parsed.options.apiKey = args[++i];
941
1084
  } else if (arg.startsWith('--api-key=')) {
942
1085
  parsed.options.apiKey = arg.split('=')[1];
1086
+ } else if (arg === '--platform') {
1087
+ parsed.options.platform = args[++i];
1088
+ } else if (arg.startsWith('--platform=')) {
1089
+ parsed.options.platform = arg.split('=')[1];
943
1090
  } else if (arg === '--channel') {
944
1091
  parsed.options.channel = args[++i];
945
1092
  } else if (arg.startsWith('--channel=')) {
946
1093
  parsed.options.channel = arg.split('=')[1];
1094
+ } else if (arg === '--project-path') {
1095
+ parsed.options.projectPath = args[++i];
1096
+ } else if (arg.startsWith('--project-path=')) {
1097
+ parsed.options.projectPath = arg.split('=')[1];
947
1098
  } else if (arg === '--target-client-id') {
948
1099
  parsed.options.targetClientId = args[++i];
949
1100
  } else if (arg.startsWith('--target-client-id=')) {
@@ -1041,6 +1192,9 @@ async function main() {
1041
1192
  case 'editor':
1042
1193
  await runEditorCommand(positionals, options);
1043
1194
  break;
1195
+ case 'cc-connect':
1196
+ await runCcConnectCommand(positionals, options);
1197
+ break;
1044
1198
  case 'status':
1045
1199
  case 'info':
1046
1200
  showStatus(options);
@@ -1078,6 +1232,9 @@ if (isDirectExecution) {
1078
1232
  }
1079
1233
 
1080
1234
  export {
1235
+ buildCcConnectCommand,
1236
+ buildCcConnectErrorResponse,
1237
+ buildCcConnectSuccessResponse,
1081
1238
  buildEditorRequest,
1082
1239
  buildEditorSuccessResponse,
1083
1240
  buildEditorErrorResponse,
@@ -62,15 +62,21 @@ async function ensureProviderInstalled(provider) {
62
62
  throw createRequestError(messageParts.join(' '), 400);
63
63
  }
64
64
 
65
- export function buildSessionNavigation(sessionId) {
65
+ export function buildSessionNavigation({
66
+ provider,
67
+ sessionId
68
+ } = {}) {
69
+ const normalizedProvider = typeof provider === 'string' && provider.trim() ? provider.trim().toLowerCase() : null;
66
70
  const normalizedSessionId = typeof sessionId === 'string' && sessionId.trim() ? sessionId.trim() : null;
67
- const sessionPath = normalizedSessionId ? `/session/${normalizedSessionId}` : null;
71
+ const sessionPath = normalizedProvider && normalizedSessionId
72
+ ? `/session/${normalizedProvider}/${encodeURIComponent(normalizedSessionId)}`
73
+ : null;
68
74
  const frontendPort = process.env.VITE_PORT || '5173';
69
75
  const configuredFrontendUrl = typeof process.env.FRONTEND_URL === 'string'
70
76
  ? process.env.FRONTEND_URL.trim().replace(/\/+$/, '')
71
77
  : '';
72
78
  const frontendBaseUrl = configuredFrontendUrl || `http://localhost:${frontendPort}`;
73
- const sessionUrl = normalizedSessionId ? `${frontendBaseUrl}${sessionPath}` : null;
79
+ const sessionUrl = sessionPath ? `${frontendBaseUrl}${sessionPath}` : null;
74
80
 
75
81
  return {
76
82
  sessionPath,
@@ -471,7 +477,11 @@ export async function runExternalAgentRequest({
471
477
  openOnly: true,
472
478
  runtime: 'acp',
473
479
  sessionId: normalized.normalizedSessionId,
474
- ...buildSessionNavigation(normalized.normalizedSessionId),
480
+ ...buildSessionNavigation({
481
+ provider: normalized.provider,
482
+ sessionId: normalized.normalizedSessionId,
483
+ projectPath: normalized.projectPath
484
+ }),
475
485
  isResumed: true,
476
486
  message: 'Session link generated. No model call triggered.'
477
487
  };
@@ -534,7 +544,11 @@ export async function runExternalAgentRequest({
534
544
  throw new AgentSessionAbortedError();
535
545
  }
536
546
 
537
- const navigation = buildSessionNavigation(callbackSessionId);
547
+ const navigation = buildSessionNavigation({
548
+ provider: normalized.provider,
549
+ sessionId: callbackSessionId,
550
+ projectPath: finalProjectPath
551
+ });
538
552
  const response = {
539
553
  success: true,
540
554
  sessionId: callbackSessionId,
@@ -580,7 +594,11 @@ export async function runExternalAgentRequest({
580
594
  } catch (error) {
581
595
  callbackSessionId = writer.getSessionId() || callbackCaptureWriter.getSessionId() || normalized.normalizedSessionId;
582
596
  callbackResult = {
583
- ...buildSessionNavigation(callbackSessionId),
597
+ ...buildSessionNavigation({
598
+ provider: normalized.provider,
599
+ sessionId: callbackSessionId,
600
+ projectPath: finalProjectPath
601
+ }),
584
602
  messages: callbackCaptureWriter.getAssistantMessages(),
585
603
  tokens: callbackCaptureWriter.getTotalTokens()
586
604
  };