@axhub/genie 0.2.6 → 0.2.8
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/api-docs.html +2 -2
- package/dist/assets/App-CTKZtqB1.js +460 -0
- package/dist/assets/{ReviewApp-BEicSBzW.js → ReviewApp-DM6BNAzR.js} +1 -1
- package/dist/assets/{_basePickBy-DkiHsp3X.js → _basePickBy-CqJbRZ9y.js} +1 -1
- package/dist/assets/{_baseUniq-7ElXb2sX.js → _baseUniq-BS8YH8jO.js} +1 -1
- package/dist/assets/{arc-CEsS3MdK.js → arc-BBmKEN-S.js} +1 -1
- package/dist/assets/{architectureDiagram-2XIMDMQ5-BubZ7T3U.js → architectureDiagram-2XIMDMQ5-N5lcb82R.js} +1 -1
- package/dist/assets/{blockDiagram-WCTKOSBZ-Cza6M6Ht.js → blockDiagram-WCTKOSBZ-DTMwHuLn.js} +1 -1
- package/dist/assets/{c4Diagram-IC4MRINW-jhjtOQ12.js → c4Diagram-IC4MRINW-BTKlkXI9.js} +1 -1
- package/dist/assets/channel-1oJBvF-0.js +1 -0
- package/dist/assets/{chunk-4BX2VUAB--HkodwbY.js → chunk-4BX2VUAB-DUdoTxAc.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-CyBuez4e.js → chunk-55IACEB6-Bm_92xe4.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-CuzG4iAl.js → chunk-FMBD7UC4-CGW0g62g.js} +1 -1
- package/dist/assets/{chunk-JSJVCQXG-BNi8S861.js → chunk-JSJVCQXG-DYkTH3w1.js} +1 -1
- package/dist/assets/{chunk-KX2RTZJC-D817O-GT.js → chunk-KX2RTZJC-C9oTlISU.js} +1 -1
- package/dist/assets/{chunk-NQ4KR5QH-DyujyOvx.js → chunk-NQ4KR5QH-CM50ygWP.js} +1 -1
- package/dist/assets/{chunk-QZHKN3VN-VMEn-zxh.js → chunk-QZHKN3VN-7dzpYeNJ.js} +1 -1
- package/dist/assets/{chunk-WL4C6EOR-CQHHFLvx.js → chunk-WL4C6EOR-Cm9nQrsr.js} +1 -1
- package/dist/assets/classDiagram-VBA2DB6C-d5TeKFM4.js +1 -0
- package/dist/assets/classDiagram-v2-RAHNMMFH-d5TeKFM4.js +1 -0
- package/dist/assets/clone-CinxIlEu.js +1 -0
- package/dist/assets/{cose-bilkent-S5V4N54A-qykDd54p.js → cose-bilkent-S5V4N54A-Ccp_p0JZ.js} +1 -1
- package/dist/assets/{dagre-KLK3FWXG-Bqp7DjEa.js → dagre-KLK3FWXG-fBwTLUp9.js} +1 -1
- package/dist/assets/{diagram-E7M64L7V-BKtx468K.js → diagram-E7M64L7V-CeNVmFUp.js} +1 -1
- package/dist/assets/{diagram-IFDJBPK2--fHfW6V2.js → diagram-IFDJBPK2-CtavyLGa.js} +1 -1
- package/dist/assets/{diagram-P4PSJMXO-D1kQI5RB.js → diagram-P4PSJMXO-CpQTjQwc.js} +1 -1
- package/dist/assets/{erDiagram-INFDFZHY-DT9YzdNw.js → erDiagram-INFDFZHY-B8R5vwhd.js} +1 -1
- package/dist/assets/{flowDiagram-PKNHOUZH-DWeNr4yg.js → flowDiagram-PKNHOUZH-BvkVVwIQ.js} +1 -1
- package/dist/assets/{ganttDiagram-A5KZAMGK--IgwcUhI.js → ganttDiagram-A5KZAMGK-DOu3hSNa.js} +1 -1
- package/dist/assets/{gitGraphDiagram-K3NZZRJ6-B5a8UWjN.js → gitGraphDiagram-K3NZZRJ6-C7zT67YE.js} +1 -1
- package/dist/assets/{graph-Cw1rYoD9.js → graph-D11wiwHo.js} +1 -1
- package/dist/assets/{highlighted-body-TPN3WLV5-BCxJHuqY.js → highlighted-body-TPN3WLV5-Babpthg-.js} +1 -1
- package/dist/assets/index-DFxzgWoO.js +2 -0
- package/dist/assets/index-YCFGDVKw.css +1 -0
- package/dist/assets/{infoDiagram-LFFYTUFH-D2u70rhN.js → infoDiagram-LFFYTUFH-BmA7IpQG.js} +1 -1
- package/dist/assets/{ishikawaDiagram-PHBUUO56-Cl8yrezU.js → ishikawaDiagram-PHBUUO56-BEquZd3E.js} +1 -1
- package/dist/assets/{journeyDiagram-4ABVD52K-ddP0AMU9.js → journeyDiagram-4ABVD52K-BfemGz7f.js} +1 -1
- package/dist/assets/{kanban-definition-K7BYSVSG-DbVt0v29.js → kanban-definition-K7BYSVSG-CWja3mln.js} +1 -1
- package/dist/assets/{layout-W_tRx4UV.js → layout-BLUNf-PJ.js} +1 -1
- package/dist/assets/{linear-CcMb2ay-.js → linear-DukIV_Xv.js} +1 -1
- package/dist/assets/{mermaid-O7DHMXV3-BBJqt8pT.js → mermaid-O7DHMXV3-SgtM28qI.js} +265 -215
- package/dist/assets/{mindmap-definition-YRQLILUH-BGhZa7Na.js → mindmap-definition-YRQLILUH-4UjqXITU.js} +1 -1
- package/dist/assets/{pieDiagram-SKSYHLDU-CDyJaACv.js → pieDiagram-SKSYHLDU-8AxqJd0M.js} +1 -1
- package/dist/assets/{quadrantDiagram-337W2JSQ-BSYuqf0Q.js → quadrantDiagram-337W2JSQ-D60m8V8r.js} +1 -1
- package/dist/assets/{requirementDiagram-Z7DCOOCP-Cfi9YX9H.js → requirementDiagram-Z7DCOOCP-zqh9jBVf.js} +1 -1
- package/dist/assets/{sankeyDiagram-WA2Y5GQK-Di1ShaMF.js → sankeyDiagram-WA2Y5GQK-CDZILTLI.js} +1 -1
- package/dist/assets/{sequenceDiagram-2WXFIKYE-CYTTG38e.js → sequenceDiagram-2WXFIKYE-7BReFd0L.js} +1 -1
- package/dist/assets/{stateDiagram-RAJIS63D-CVZYMqyW.js → stateDiagram-RAJIS63D-HPTVdIG4.js} +1 -1
- package/dist/assets/stateDiagram-v2-FVOUBMTO-DTUf5_gC.js +1 -0
- package/dist/assets/{timeline-definition-YZTLITO2-B1sdb5mK.js → timeline-definition-YZTLITO2-CTVllFgr.js} +1 -1
- package/dist/assets/{treemap-KZPCXAKY-CGG4gx3C.js → treemap-KZPCXAKY-BtyxboJZ.js} +1 -1
- package/dist/assets/{vennDiagram-LZ73GAT5-Dds37L2k.js → vennDiagram-LZ73GAT5-D96ZI6Mg.js} +1 -1
- package/dist/assets/{xychartDiagram-JWTSCODW-C8QKSyRR.js → xychartDiagram-JWTSCODW-eRk-39YO.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +35 -33
- 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 +1872 -0
- package/server/acp-runtime/index.js +408 -0
- package/server/acp-runtime/registry.js +45 -0
- package/server/acp-runtime/session-store.js +254 -0
- package/server/channels/runtime/AgentRuntimeAdapter.js +22 -80
- package/server/claude-sdk.js +24 -946
- package/server/cli.js +140 -2
- package/server/external-agent/service.js +52 -63
- package/server/gemini-cli.js +21 -360
- package/server/index.js +133 -58
- package/server/openai-codex.js +19 -695
- package/server/opencode-cli.js +68 -640
- package/server/projects.js +128 -85
- package/server/routes/agent.js +2 -0
- package/server/routes/cc-connect.js +1131 -0
- package/server/routes/cli-auth.js +1 -73
- package/server/routes/commands.js +4 -9
- package/server/routes/git.js +3 -20
- package/server/routes/projects.js +45 -24
- 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 +8 -3
- package/server/session-core/runtimeState.js +16 -17
- package/server/session-core/runtimeWriter.js +19 -12
- package/server/utils/ccConnectManager.js +390 -0
- package/server/utils/ccConnectState.js +575 -0
- package/server/utils/resolveCommandPath.js +71 -0
- package/server/utils/workspaceRoots.js +154 -0
- package/shared/conversationEvents.js +347 -10
- package/dist/assets/App-CYTE30Cf.js +0 -484
- package/dist/assets/channel-RmqTALN0.js +0 -1
- package/dist/assets/classDiagram-VBA2DB6C-wvVV1ggz.js +0 -1
- package/dist/assets/classDiagram-v2-RAHNMMFH-wvVV1ggz.js +0 -1
- package/dist/assets/clone-oT5aWXpf.js +0 -1
- package/dist/assets/index-CBuAXA5S.js +0 -2
- package/dist/assets/index-CyLWKyxy.css +0 -1
- package/dist/assets/stateDiagram-v2-FVOUBMTO-Bbl0b4-i.js +0 -1
- package/server/cli.test.js +0 -76
- package/server/external-agent/service.test.js +0 -53
- package/server/external-agent/ws.test.js +0 -289
|
@@ -0,0 +1,575 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import TOML from '@iarna/toml';
|
|
5
|
+
|
|
6
|
+
export const CC_CONNECT_STATE_VERSION = 2;
|
|
7
|
+
export const CC_CONNECT_CONFIG_DIR = path.join(os.homedir(), '.cc-connect');
|
|
8
|
+
export const CC_CONNECT_CONFIG_PATH = path.join(CC_CONNECT_CONFIG_DIR, 'config.toml');
|
|
9
|
+
export const CC_CONNECT_STATE_PATH = path.join(CC_CONNECT_CONFIG_DIR, 'ai-web-ui-connect-state.json');
|
|
10
|
+
export const LEGACY_WEIXIN_STATE_PATH = path.join(CC_CONNECT_CONFIG_DIR, 'axhub-weixin-state.json');
|
|
11
|
+
|
|
12
|
+
export const CC_CONNECT_PROVIDERS = [
|
|
13
|
+
{ id: 'claude', label: 'Claude', command: 'claude', ccAgentType: 'claudecode' },
|
|
14
|
+
{ id: 'codex', label: 'Codex', command: 'codex', ccAgentType: 'codex' },
|
|
15
|
+
{ id: 'gemini', label: 'Gemini', command: 'gemini', ccAgentType: 'gemini' },
|
|
16
|
+
{ id: 'opencode', label: 'OpenCode', command: 'opencode', ccAgentType: 'opencode' }
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
export const CC_CONNECT_PLATFORMS = [
|
|
20
|
+
{
|
|
21
|
+
id: 'weixin',
|
|
22
|
+
label: 'Weixin',
|
|
23
|
+
description: 'Connect Weixin personal via QR code',
|
|
24
|
+
blockedAllowFrom: 'blocked@im.wechat',
|
|
25
|
+
supportedModes: ['qr', 'token'],
|
|
26
|
+
timeoutSeconds: 480
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 'feishu',
|
|
30
|
+
label: 'Feishu',
|
|
31
|
+
description: 'Connect Feishu via QR onboarding or app credentials',
|
|
32
|
+
blockedAllowFrom: 'blocked_open_id',
|
|
33
|
+
supportedModes: ['qr', 'credentials'],
|
|
34
|
+
timeoutSeconds: 600
|
|
35
|
+
}
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const PROVIDER_MAP = Object.fromEntries(CC_CONNECT_PROVIDERS.map((provider) => [provider.id, provider]));
|
|
39
|
+
const PROVIDER_BY_AGENT_TYPE = {
|
|
40
|
+
claude: 'claude',
|
|
41
|
+
claudecode: 'claude',
|
|
42
|
+
codex: 'codex',
|
|
43
|
+
gemini: 'gemini',
|
|
44
|
+
opencode: 'opencode'
|
|
45
|
+
};
|
|
46
|
+
const PLATFORM_MAP = Object.fromEntries(CC_CONNECT_PLATFORMS.map((platform) => [platform.id, platform]));
|
|
47
|
+
const LEGACY_WEIXIN_PROJECT_NAMES = new Set(['axhub-claudecode', 'axhub-codex', 'axhub-gemini', 'axhub-opencode']);
|
|
48
|
+
|
|
49
|
+
function createEmptyState() {
|
|
50
|
+
return {
|
|
51
|
+
version: CC_CONNECT_STATE_VERSION,
|
|
52
|
+
connections: {}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function isPlainObject(value) {
|
|
57
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function dedupeOrdered(values) {
|
|
61
|
+
const seen = new Set();
|
|
62
|
+
return values.filter((value) => {
|
|
63
|
+
if (!value || seen.has(value)) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
seen.add(value);
|
|
67
|
+
return true;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function normalizeProviderId(value) {
|
|
72
|
+
const providerId = String(value || '').trim().toLowerCase();
|
|
73
|
+
return PROVIDER_MAP[providerId] ? providerId : null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function normalizeProviderFromAgentType(value) {
|
|
77
|
+
const normalized = String(value || '').trim().toLowerCase();
|
|
78
|
+
return PROVIDER_BY_AGENT_TYPE[normalized] || null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function normalizePlatformId(value) {
|
|
82
|
+
const platformId = String(value || '').trim().toLowerCase();
|
|
83
|
+
return PLATFORM_MAP[platformId] ? platformId : null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function sanitizePlainObject(value) {
|
|
87
|
+
if (!isPlainObject(value)) {
|
|
88
|
+
return {};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return Object.fromEntries(
|
|
92
|
+
Object.entries(value).filter(([, item]) => item !== undefined && item !== null)
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function normalizePlatformOptions(platformId, value) {
|
|
97
|
+
return sanitizePlainObject(value);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function ensureConfigDir() {
|
|
101
|
+
if (!fs.existsSync(CC_CONNECT_CONFIG_DIR)) {
|
|
102
|
+
fs.mkdirSync(CC_CONNECT_CONFIG_DIR, { recursive: true });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function readJsonFile(filePath) {
|
|
107
|
+
if (!fs.existsSync(filePath)) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
113
|
+
} catch {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function normalizeConnection(connection) {
|
|
119
|
+
if (!isPlainObject(connection)) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const configuredProviders = dedupeOrdered(
|
|
124
|
+
(Array.isArray(connection.configuredProviders) ? connection.configuredProviders : [])
|
|
125
|
+
.map(normalizeProviderId)
|
|
126
|
+
.filter(Boolean)
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
const activeProvider = normalizeProviderId(connection.activeProvider) || configuredProviders[0] || null;
|
|
130
|
+
const projectPath = typeof connection.projectPath === 'string' ? connection.projectPath.trim() : '';
|
|
131
|
+
const platformType = normalizePlatformId(connection.platformType) || null;
|
|
132
|
+
const connectionOptions = normalizePlatformOptions(platformType, connection.connectionOptions);
|
|
133
|
+
|
|
134
|
+
if (!activeProvider || configuredProviders.length === 0 || !projectPath) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
configuredProviders,
|
|
140
|
+
activeProvider: configuredProviders.includes(activeProvider) ? activeProvider : configuredProviders[0],
|
|
141
|
+
projectPath,
|
|
142
|
+
connectionOptions,
|
|
143
|
+
platformType,
|
|
144
|
+
connectedAt: typeof connection.connectedAt === 'string' ? connection.connectedAt : null,
|
|
145
|
+
updatedAt: typeof connection.updatedAt === 'string' ? connection.updatedAt : null
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function normalizeCcConnectState(input) {
|
|
150
|
+
const nextState = createEmptyState();
|
|
151
|
+
if (!isPlainObject(input)) {
|
|
152
|
+
return nextState;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const connections = isPlainObject(input.connections) ? input.connections : {};
|
|
156
|
+
for (const [rawPlatformId, rawConnection] of Object.entries(connections)) {
|
|
157
|
+
const platformId = normalizePlatformId(rawPlatformId);
|
|
158
|
+
const connection = normalizeConnection(rawConnection);
|
|
159
|
+
if (!platformId || !connection) {
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
nextState.connections[platformId] = {
|
|
164
|
+
...connection,
|
|
165
|
+
platformType: connection.platformType || platformId,
|
|
166
|
+
connectedAt: connection.connectedAt || new Date().toISOString(),
|
|
167
|
+
updatedAt: connection.updatedAt || new Date().toISOString()
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return nextState;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function readTomlConfig(content) {
|
|
175
|
+
if (typeof content !== 'string' || !content.trim()) {
|
|
176
|
+
return { projects: [] };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
const parsed = TOML.parse(content);
|
|
181
|
+
return isPlainObject(parsed) ? parsed : { projects: [] };
|
|
182
|
+
} catch {
|
|
183
|
+
return { projects: [] };
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function getProjectPlatformEntry(project, platformId) {
|
|
188
|
+
const platforms = Array.isArray(project?.platforms) ? project.platforms : [];
|
|
189
|
+
return platforms.find((entry) => normalizePlatformId(entry?.type) === platformId) || null;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function getManagedProjectName(platformId, providerId) {
|
|
193
|
+
return `ai-web-ui-${platformId}-${providerId}`;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function getCcConnectManagedProjectName(platformId, providerId) {
|
|
197
|
+
return getManagedProjectName(platformId, providerId);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function isManagedProject(project, platformId = null) {
|
|
201
|
+
const projectName = String(project?.name || '').trim().toLowerCase();
|
|
202
|
+
if (!projectName) {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (projectName.startsWith('ai-web-ui-')) {
|
|
207
|
+
if (!platformId) {
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
return projectName.startsWith(`ai-web-ui-${platformId}-`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (platformId === 'weixin' || platformId === null) {
|
|
214
|
+
return LEGACY_WEIXIN_PROJECT_NAMES.has(projectName);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function isBlockedAllowFrom(platformId, value) {
|
|
221
|
+
if (typeof value !== 'string') {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return value.trim() === PLATFORM_MAP[platformId]?.blockedAllowFrom;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function buildConnectionFromProjects(platformId, entries) {
|
|
229
|
+
if (!Array.isArray(entries) || entries.length === 0) {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const configuredProviders = dedupeOrdered(entries.map((entry) => entry.providerId));
|
|
234
|
+
const activeEntry = entries.find(({ options }) => {
|
|
235
|
+
const allowFrom = options?.allow_from;
|
|
236
|
+
if (platformId === 'weixin') {
|
|
237
|
+
return typeof allowFrom === 'string' && allowFrom.trim() && !isBlockedAllowFrom(platformId, allowFrom);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (typeof allowFrom !== 'string' || !allowFrom.trim()) {
|
|
241
|
+
return true;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return !isBlockedAllowFrom(platformId, allowFrom);
|
|
245
|
+
}) || entries[0];
|
|
246
|
+
|
|
247
|
+
const projectPath = String(activeEntry.project?.agent?.options?.work_dir || entries[0]?.project?.agent?.options?.work_dir || '').trim();
|
|
248
|
+
if (!configuredProviders.length || !projectPath) {
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
configuredProviders,
|
|
254
|
+
activeProvider: activeEntry.providerId,
|
|
255
|
+
projectPath,
|
|
256
|
+
connectionOptions: normalizePlatformOptions(platformId, activeEntry.options),
|
|
257
|
+
platformType: platformId,
|
|
258
|
+
connectedAt: new Date().toISOString(),
|
|
259
|
+
updatedAt: new Date().toISOString()
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export function bootstrapStateFromConfigContent(content) {
|
|
264
|
+
const config = readTomlConfig(content);
|
|
265
|
+
const projects = Array.isArray(config.projects) ? config.projects : [];
|
|
266
|
+
const nextState = createEmptyState();
|
|
267
|
+
|
|
268
|
+
for (const platform of CC_CONNECT_PLATFORMS) {
|
|
269
|
+
const entries = [];
|
|
270
|
+
for (const project of projects) {
|
|
271
|
+
if (!isManagedProject(project, platform.id)) {
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const providerId = normalizeProviderFromAgentType(project?.agent?.type);
|
|
276
|
+
if (!providerId) {
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const platformEntry = getProjectPlatformEntry(project, platform.id);
|
|
281
|
+
if (!platformEntry || !isPlainObject(platformEntry.options)) {
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
entries.push({
|
|
286
|
+
providerId,
|
|
287
|
+
project,
|
|
288
|
+
options: sanitizePlainObject(platformEntry.options)
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const connection = buildConnectionFromProjects(platform.id, entries);
|
|
293
|
+
if (connection) {
|
|
294
|
+
nextState.connections[platform.id] = connection;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return Object.keys(nextState.connections).length > 0 ? nextState : null;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function migrateLegacyWeixinState() {
|
|
302
|
+
const legacy = readJsonFile(LEGACY_WEIXIN_STATE_PATH);
|
|
303
|
+
if (!legacy || Number(legacy.version) !== 1) {
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const configuredProviders = dedupeOrdered(
|
|
308
|
+
(Array.isArray(legacy.configuredAgents) ? legacy.configuredAgents : [])
|
|
309
|
+
.map(normalizeProviderFromAgentType)
|
|
310
|
+
.filter(Boolean)
|
|
311
|
+
);
|
|
312
|
+
const activeProvider = normalizeProviderFromAgentType(legacy.activeAgent) || configuredProviders[0] || null;
|
|
313
|
+
const projectPath = typeof legacy.workDir === 'string' ? legacy.workDir.trim() : '';
|
|
314
|
+
const connectionOptions = normalizePlatformOptions('weixin', legacy.weixinOptions);
|
|
315
|
+
|
|
316
|
+
if (!configuredProviders.length || !activeProvider || !projectPath || !Object.keys(connectionOptions).length) {
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return normalizeCcConnectState({
|
|
321
|
+
version: CC_CONNECT_STATE_VERSION,
|
|
322
|
+
connections: {
|
|
323
|
+
weixin: {
|
|
324
|
+
configuredProviders,
|
|
325
|
+
activeProvider,
|
|
326
|
+
projectPath,
|
|
327
|
+
connectionOptions,
|
|
328
|
+
platformType: 'weixin',
|
|
329
|
+
connectedAt: new Date().toISOString(),
|
|
330
|
+
updatedAt: new Date().toISOString()
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export function readCcConnectState() {
|
|
337
|
+
const stored = readJsonFile(CC_CONNECT_STATE_PATH);
|
|
338
|
+
if (stored) {
|
|
339
|
+
return normalizeCcConnectState(stored);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const configContent = readCcConnectConfigContent();
|
|
343
|
+
const bootstrapped = bootstrapStateFromConfigContent(configContent);
|
|
344
|
+
if (bootstrapped) {
|
|
345
|
+
writeCcConnectState(bootstrapped);
|
|
346
|
+
return bootstrapped;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const migratedLegacy = migrateLegacyWeixinState();
|
|
350
|
+
if (migratedLegacy) {
|
|
351
|
+
writeCcConnectState(migratedLegacy);
|
|
352
|
+
return migratedLegacy;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return createEmptyState();
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
export function writeCcConnectState(state) {
|
|
359
|
+
ensureConfigDir();
|
|
360
|
+
const normalized = normalizeCcConnectState(state);
|
|
361
|
+
fs.writeFileSync(CC_CONNECT_STATE_PATH, JSON.stringify(normalized, null, 2), 'utf8');
|
|
362
|
+
return normalized;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export function readCcConnectConfigContent() {
|
|
366
|
+
if (!fs.existsSync(CC_CONNECT_CONFIG_PATH)) {
|
|
367
|
+
return '';
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
try {
|
|
371
|
+
return fs.readFileSync(CC_CONNECT_CONFIG_PATH, 'utf8');
|
|
372
|
+
} catch {
|
|
373
|
+
return '';
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function buildManagedProject(platformId, connection, providerId) {
|
|
378
|
+
const provider = PROVIDER_MAP[providerId];
|
|
379
|
+
const options = normalizePlatformOptions(platformId, connection.connectionOptions);
|
|
380
|
+
|
|
381
|
+
if (providerId !== connection.activeProvider) {
|
|
382
|
+
options.allow_from = PLATFORM_MAP[platformId].blockedAllowFrom;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return {
|
|
386
|
+
name: getManagedProjectName(platformId, providerId),
|
|
387
|
+
agent: {
|
|
388
|
+
type: provider.ccAgentType,
|
|
389
|
+
options: {
|
|
390
|
+
work_dir: connection.projectPath,
|
|
391
|
+
mode: 'yolo'
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
platforms: [
|
|
395
|
+
{
|
|
396
|
+
type: connection.platformType || platformId,
|
|
397
|
+
options
|
|
398
|
+
}
|
|
399
|
+
]
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function shouldMaterializeManagedProject(platformId, connection, providerId) {
|
|
404
|
+
if (platformId === 'feishu') {
|
|
405
|
+
return providerId === connection.activeProvider;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return true;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
export function buildManagedProjects(state) {
|
|
412
|
+
const normalized = normalizeCcConnectState(state);
|
|
413
|
+
const projects = [];
|
|
414
|
+
|
|
415
|
+
for (const platform of CC_CONNECT_PLATFORMS) {
|
|
416
|
+
const connection = normalized.connections[platform.id];
|
|
417
|
+
if (!connection) {
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
for (const providerId of connection.configuredProviders) {
|
|
422
|
+
if (!PROVIDER_MAP[providerId]) {
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
if (!shouldMaterializeManagedProject(platform.id, connection, providerId)) {
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
projects.push(buildManagedProject(platform.id, connection, providerId));
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
return projects;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
export function serializeCcConnectConfig(baseContent, state) {
|
|
436
|
+
const existingConfig = readTomlConfig(baseContent);
|
|
437
|
+
const existingProjects = Array.isArray(existingConfig.projects) ? existingConfig.projects : [];
|
|
438
|
+
const unmanagedProjects = existingProjects.filter((project) => !isManagedProject(project));
|
|
439
|
+
const managedProjects = buildManagedProjects(state);
|
|
440
|
+
const nextConfig = {
|
|
441
|
+
...existingConfig,
|
|
442
|
+
log: isPlainObject(existingConfig.log) ? existingConfig.log : { level: 'info' }
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
const allProjects = [...unmanagedProjects, ...managedProjects];
|
|
446
|
+
if (allProjects.length > 0) {
|
|
447
|
+
nextConfig.projects = allProjects;
|
|
448
|
+
} else {
|
|
449
|
+
delete nextConfig.projects;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
return TOML.stringify(nextConfig);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
export function writeCcConnectConfig(state) {
|
|
456
|
+
ensureConfigDir();
|
|
457
|
+
const content = serializeCcConnectConfig(readCcConnectConfigContent(), state);
|
|
458
|
+
fs.writeFileSync(CC_CONNECT_CONFIG_PATH, content, 'utf8');
|
|
459
|
+
return {
|
|
460
|
+
configPath: CC_CONNECT_CONFIG_PATH,
|
|
461
|
+
content
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
export function getPlatformConnection(state, platformId) {
|
|
466
|
+
const normalized = normalizeCcConnectState(state);
|
|
467
|
+
const resolvedPlatformId = normalizePlatformId(platformId);
|
|
468
|
+
if (!resolvedPlatformId) {
|
|
469
|
+
return null;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
return normalized.connections[resolvedPlatformId] || null;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
export function upsertPlatformConnection(state, platformId, connection) {
|
|
476
|
+
const normalized = normalizeCcConnectState(state);
|
|
477
|
+
const resolvedPlatformId = normalizePlatformId(platformId);
|
|
478
|
+
const normalizedConnection = normalizeConnection(connection);
|
|
479
|
+
if (!resolvedPlatformId || !normalizedConnection) {
|
|
480
|
+
return normalized;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
normalized.connections[resolvedPlatformId] = {
|
|
484
|
+
...normalizedConnection,
|
|
485
|
+
platformType: normalizedConnection.platformType || resolvedPlatformId,
|
|
486
|
+
connectedAt: normalizedConnection.connectedAt || new Date().toISOString(),
|
|
487
|
+
updatedAt: new Date().toISOString()
|
|
488
|
+
};
|
|
489
|
+
return normalized;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
export function removePlatformConnection(state, platformId) {
|
|
493
|
+
const normalized = normalizeCcConnectState(state);
|
|
494
|
+
const resolvedPlatformId = normalizePlatformId(platformId);
|
|
495
|
+
if (resolvedPlatformId) {
|
|
496
|
+
delete normalized.connections[resolvedPlatformId];
|
|
497
|
+
}
|
|
498
|
+
return normalized;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
export function createSetupConfigContent({
|
|
502
|
+
platformId,
|
|
503
|
+
providerId,
|
|
504
|
+
projectName,
|
|
505
|
+
projectPath,
|
|
506
|
+
setupOptions = {}
|
|
507
|
+
}) {
|
|
508
|
+
const platform = PLATFORM_MAP[platformId];
|
|
509
|
+
const provider = PROVIDER_MAP[providerId];
|
|
510
|
+
if (!platform || !provider) {
|
|
511
|
+
throw new Error('Unsupported platform or provider');
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
return TOML.stringify({
|
|
515
|
+
log: { level: 'info' },
|
|
516
|
+
projects: [
|
|
517
|
+
{
|
|
518
|
+
name: projectName,
|
|
519
|
+
agent: {
|
|
520
|
+
type: provider.ccAgentType,
|
|
521
|
+
options: {
|
|
522
|
+
work_dir: projectPath,
|
|
523
|
+
mode: 'yolo'
|
|
524
|
+
}
|
|
525
|
+
},
|
|
526
|
+
platforms: [
|
|
527
|
+
{
|
|
528
|
+
type: platform.id,
|
|
529
|
+
options: normalizePlatformOptions(platform.id, setupOptions)
|
|
530
|
+
}
|
|
531
|
+
]
|
|
532
|
+
}
|
|
533
|
+
]
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
export function extractPlatformOptionsFromConfigContent(content, projectName, platformId) {
|
|
538
|
+
const config = readTomlConfig(content);
|
|
539
|
+
const projects = Array.isArray(config.projects) ? config.projects : [];
|
|
540
|
+
const project = projects.find((item) => String(item?.name || '').trim() === String(projectName || '').trim());
|
|
541
|
+
if (!project) {
|
|
542
|
+
return null;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
const platformEntry = getProjectPlatformEntry(project, platformId);
|
|
546
|
+
if (!platformEntry || !isPlainObject(platformEntry.options)) {
|
|
547
|
+
return null;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
return normalizePlatformOptions(platformId, platformEntry.options);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
export function listCcConnectPlatformSummaries(state) {
|
|
554
|
+
const normalized = normalizeCcConnectState(state);
|
|
555
|
+
|
|
556
|
+
return CC_CONNECT_PLATFORMS.map((platform) => {
|
|
557
|
+
const connection = normalized.connections[platform.id] || null;
|
|
558
|
+
return {
|
|
559
|
+
id: platform.id,
|
|
560
|
+
label: platform.label,
|
|
561
|
+
description: platform.description,
|
|
562
|
+
connected: Boolean(connection),
|
|
563
|
+
activeProvider: connection?.activeProvider || null,
|
|
564
|
+
configuredProviders: connection?.configuredProviders || [],
|
|
565
|
+
projectPath: connection?.projectPath || null,
|
|
566
|
+
supportedModes: platform.supportedModes,
|
|
567
|
+
connectionMeta: connection
|
|
568
|
+
? {
|
|
569
|
+
optionKeys: Object.keys(connection.connectionOptions || {}),
|
|
570
|
+
hasAllowFrom: typeof connection.connectionOptions?.allow_from === 'string'
|
|
571
|
+
}
|
|
572
|
+
: null
|
|
573
|
+
};
|
|
574
|
+
});
|
|
575
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { constants as fsConstants } from 'fs';
|
|
2
|
+
import fs from 'fs/promises';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
async function isRunnableCommand(candidatePath) {
|
|
6
|
+
try {
|
|
7
|
+
if (process.platform === 'win32') {
|
|
8
|
+
await fs.access(candidatePath, fsConstants.F_OK);
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
await fs.access(candidatePath, fsConstants.X_OK);
|
|
13
|
+
return true;
|
|
14
|
+
} catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function resolveCommandPath(command) {
|
|
20
|
+
const normalizedCommand = String(command || '').trim();
|
|
21
|
+
if (!normalizedCommand) {
|
|
22
|
+
return {
|
|
23
|
+
found: false,
|
|
24
|
+
resolvedPath: null,
|
|
25
|
+
reason: 'Command name is empty'
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const isDirectPath = normalizedCommand.includes(path.sep) || (process.platform === 'win32' && normalizedCommand.includes('/'));
|
|
30
|
+
const pathEntries = isDirectPath
|
|
31
|
+
? ['']
|
|
32
|
+
: String(process.env.PATH || '')
|
|
33
|
+
.split(path.delimiter)
|
|
34
|
+
.map((entry) => entry.trim())
|
|
35
|
+
.filter(Boolean);
|
|
36
|
+
|
|
37
|
+
const windowsExtensions = process.platform === 'win32'
|
|
38
|
+
? String(process.env.PATHEXT || '.EXE;.CMD;.BAT;.COM')
|
|
39
|
+
.split(';')
|
|
40
|
+
.map((ext) => ext.trim())
|
|
41
|
+
.filter(Boolean)
|
|
42
|
+
: [''];
|
|
43
|
+
|
|
44
|
+
const candidatePaths = [];
|
|
45
|
+
for (const baseDir of pathEntries) {
|
|
46
|
+
const baseCandidate = isDirectPath ? normalizedCommand : path.join(baseDir, normalizedCommand);
|
|
47
|
+
candidatePaths.push(baseCandidate);
|
|
48
|
+
|
|
49
|
+
if (process.platform === 'win32' && !path.extname(baseCandidate)) {
|
|
50
|
+
for (const ext of windowsExtensions) {
|
|
51
|
+
candidatePaths.push(`${baseCandidate}${ext}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
for (const candidatePath of candidatePaths) {
|
|
57
|
+
if (await isRunnableCommand(candidatePath)) {
|
|
58
|
+
return {
|
|
59
|
+
found: true,
|
|
60
|
+
resolvedPath: candidatePath,
|
|
61
|
+
reason: null
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
found: false,
|
|
68
|
+
resolvedPath: null,
|
|
69
|
+
reason: `${normalizedCommand} not found in PATH`
|
|
70
|
+
};
|
|
71
|
+
}
|