@geminilight/mindos 1.0.10 → 1.0.29
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/bin/mindos-shim.cjs +318 -7
- package/dist/agent/prompts.d.ts +1 -1
- package/dist/agent/prompts.d.ts.map +1 -1
- package/dist/agent/prompts.js +14 -0
- package/dist/agent/prompts.js.map +1 -1
- package/dist/agent-runtime/claude-code-cli.d.ts +34 -0
- package/dist/agent-runtime/claude-code-cli.d.ts.map +1 -0
- package/dist/agent-runtime/claude-code-cli.js +258 -0
- package/dist/agent-runtime/claude-code-cli.js.map +1 -0
- package/dist/agent-runtime/claude-code-sdk.d.ts +24 -0
- package/dist/agent-runtime/claude-code-sdk.d.ts.map +1 -0
- package/dist/agent-runtime/claude-code-sdk.js +415 -0
- package/dist/agent-runtime/claude-code-sdk.js.map +1 -0
- package/dist/agent-runtime/codex-app-server.d.ts +151 -0
- package/dist/agent-runtime/codex-app-server.d.ts.map +1 -0
- package/dist/agent-runtime/codex-app-server.js +626 -0
- package/dist/agent-runtime/codex-app-server.js.map +1 -0
- package/dist/agent-runtime/index.d.ts +5 -0
- package/dist/agent-runtime/index.d.ts.map +1 -0
- package/dist/agent-runtime/index.js +5 -0
- package/dist/agent-runtime/index.js.map +1 -0
- package/dist/agent-runtime/run.d.ts +104 -0
- package/dist/agent-runtime/run.d.ts.map +1 -0
- package/dist/agent-runtime/run.js +494 -0
- package/dist/agent-runtime/run.js.map +1 -0
- package/dist/agent-runtime.d.ts +2 -0
- package/dist/agent-runtime.d.ts.map +1 -0
- package/dist/agent-runtime.js +2 -0
- package/dist/agent-runtime.js.map +1 -0
- package/dist/client.d.ts +8 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js.map +1 -1
- package/dist/foundation/config/schema.d.ts +30 -141
- package/dist/foundation/config/schema.d.ts.map +1 -1
- package/dist/foundation/config/schema.js +18 -4
- package/dist/foundation/config/schema.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/knowledge/git/index.d.ts.map +1 -1
- package/dist/knowledge/git/index.js +43 -2
- package/dist/knowledge/git/index.js.map +1 -1
- package/dist/protocols/acp/agent-descriptors.d.ts.map +1 -1
- package/dist/protocols/acp/agent-descriptors.js +0 -3
- package/dist/protocols/acp/agent-descriptors.js.map +1 -1
- package/dist/protocols/acp/index.js +39 -28
- package/dist/protocols/acp/session.d.ts.map +1 -1
- package/dist/protocols/acp/session.js +11 -14
- package/dist/protocols/acp/session.js.map +1 -1
- package/dist/protocols/acp/subprocess.d.ts +4 -1
- package/dist/protocols/acp/subprocess.d.ts.map +1 -1
- package/dist/protocols/acp/subprocess.js +36 -9
- package/dist/protocols/acp/subprocess.js.map +1 -1
- package/dist/protocols/mcp-server/index.cjs +80 -68
- package/dist/server/channel-contract.d.ts +13 -0
- package/dist/server/channel-contract.d.ts.map +1 -0
- package/dist/server/channel-contract.js +115 -0
- package/dist/server/channel-contract.js.map +1 -0
- package/dist/server/contract.d.ts.map +1 -1
- package/dist/server/contract.js +10 -0
- package/dist/server/contract.js.map +1 -1
- package/dist/server/handlers/agent-runtime-codex.d.ts +26 -0
- package/dist/server/handlers/agent-runtime-codex.d.ts.map +1 -0
- package/dist/server/handlers/agent-runtime-codex.js +170 -0
- package/dist/server/handlers/agent-runtime-codex.js.map +1 -0
- package/dist/server/handlers/agent-runtimes.d.ts +126 -0
- package/dist/server/handlers/agent-runtimes.d.ts.map +1 -0
- package/dist/server/handlers/agent-runtimes.js +690 -0
- package/dist/server/handlers/agent-runtimes.js.map +1 -0
- package/dist/server/handlers/agents.d.ts +6 -0
- package/dist/server/handlers/agents.d.ts.map +1 -1
- package/dist/server/handlers/agents.js +41 -6
- package/dist/server/handlers/agents.js.map +1 -1
- package/dist/server/handlers/ask.d.ts +18 -0
- package/dist/server/handlers/ask.d.ts.map +1 -1
- package/dist/server/handlers/ask.js +70 -0
- package/dist/server/handlers/ask.js.map +1 -1
- package/dist/server/handlers/channels-verify.d.ts +1 -1
- package/dist/server/handlers/channels-verify.d.ts.map +1 -1
- package/dist/server/handlers/channels-verify.js +1 -63
- package/dist/server/handlers/channels-verify.js.map +1 -1
- package/dist/server/handlers/extract-docx.d.ts +42 -0
- package/dist/server/handlers/extract-docx.d.ts.map +1 -0
- package/dist/server/handlers/extract-docx.js +101 -0
- package/dist/server/handlers/extract-docx.js.map +1 -0
- package/dist/server/handlers/extract-pdf.d.ts +32 -0
- package/dist/server/handlers/extract-pdf.d.ts.map +1 -0
- package/dist/server/handlers/extract-pdf.js +116 -0
- package/dist/server/handlers/extract-pdf.js.map +1 -0
- package/dist/server/handlers/im-config.d.ts +1 -1
- package/dist/server/handlers/im-config.d.ts.map +1 -1
- package/dist/server/handlers/im-config.js +69 -59
- package/dist/server/handlers/im-config.js.map +1 -1
- package/dist/server/handlers/im-feishu-oauth.d.ts +55 -0
- package/dist/server/handlers/im-feishu-oauth.d.ts.map +1 -0
- package/dist/server/handlers/im-feishu-oauth.js +218 -0
- package/dist/server/handlers/im-feishu-oauth.js.map +1 -0
- package/dist/server/handlers/im-status.d.ts +15 -0
- package/dist/server/handlers/im-status.d.ts.map +1 -1
- package/dist/server/handlers/im-status.js +41 -24
- package/dist/server/handlers/im-status.js.map +1 -1
- package/dist/server/handlers/inbox-source.d.ts +18 -0
- package/dist/server/handlers/inbox-source.d.ts.map +1 -0
- package/dist/server/handlers/inbox-source.js +108 -0
- package/dist/server/handlers/inbox-source.js.map +1 -0
- package/dist/server/handlers/inbox.d.ts +2 -0
- package/dist/server/handlers/inbox.d.ts.map +1 -1
- package/dist/server/handlers/inbox.js +34 -2
- package/dist/server/handlers/inbox.js.map +1 -1
- package/dist/server/handlers/mcp-agents.d.ts +16 -1
- package/dist/server/handlers/mcp-agents.d.ts.map +1 -1
- package/dist/server/handlers/mcp-agents.js +174 -44
- package/dist/server/handlers/mcp-agents.js.map +1 -1
- package/dist/server/handlers/mcp-install.d.ts +5 -0
- package/dist/server/handlers/mcp-install.d.ts.map +1 -1
- package/dist/server/handlers/mcp-install.js +68 -20
- package/dist/server/handlers/mcp-install.js.map +1 -1
- package/dist/server/handlers/mcp-restart.js +1 -1
- package/dist/server/handlers/mcp-restart.js.map +1 -1
- package/dist/server/handlers/settings-list-models.d.ts +1 -1
- package/dist/server/handlers/settings-list-models.d.ts.map +1 -1
- package/dist/server/handlers/settings-list-models.js +6 -5
- package/dist/server/handlers/settings-list-models.js.map +1 -1
- package/dist/server/handlers/settings-test-key.d.ts.map +1 -1
- package/dist/server/handlers/settings-test-key.js +17 -7
- package/dist/server/handlers/settings-test-key.js.map +1 -1
- package/dist/server/handlers/settings.d.ts +2 -1
- package/dist/server/handlers/settings.d.ts.map +1 -1
- package/dist/server/handlers/settings.js +49 -5
- package/dist/server/handlers/settings.js.map +1 -1
- package/dist/server/handlers/skills.d.ts +1 -0
- package/dist/server/handlers/skills.d.ts.map +1 -1
- package/dist/server/handlers/skills.js +7 -5
- package/dist/server/handlers/skills.js.map +1 -1
- package/dist/server/handlers/sync.d.ts +15 -4
- package/dist/server/handlers/sync.d.ts.map +1 -1
- package/dist/server/handlers/sync.js +552 -81
- package/dist/server/handlers/sync.js.map +1 -1
- package/dist/server/handlers/uninstall.js +1 -0
- package/dist/server/handlers/uninstall.js.map +1 -1
- package/dist/server/handlers/update.js +1 -0
- package/dist/server/handlers/update.js.map +1 -1
- package/dist/server/http.d.ts +20 -0
- package/dist/server/http.d.ts.map +1 -1
- package/dist/server/http.js +223 -24
- package/dist/server/http.js.map +1 -1
- package/dist/server/index.d.ts +12 -4
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +9 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/mcp-agent-registry.d.ts +184 -0
- package/dist/server/mcp-agent-registry.d.ts.map +1 -1
- package/dist/server/mcp-agent-registry.js +100 -9
- package/dist/server/mcp-agent-registry.js.map +1 -1
- package/dist/server/provider-settings.d.ts +38 -0
- package/dist/server/provider-settings.d.ts.map +1 -0
- package/dist/server/provider-settings.js +286 -0
- package/dist/server/provider-settings.js.map +1 -0
- package/dist/server/route-ownership.d.ts.map +1 -1
- package/dist/server/route-ownership.js +15 -2
- package/dist/server/route-ownership.js.map +1 -1
- package/dist/session/index.d.ts +94 -15
- package/dist/session/index.d.ts.map +1 -1
- package/dist/session/index.js +115 -18
- package/dist/session/index.js.map +1 -1
- package/dist/session/pi-coding-agent-runtime.js +3 -3
- package/dist/session/pi-coding-agent-runtime.js.map +1 -1
- package/dist/setup/index.d.ts +1 -0
- package/dist/setup/index.d.ts.map +1 -1
- package/dist/setup/index.js +13 -0
- package/dist/setup/index.js.map +1 -1
- package/package.json +18 -12
- package/src/cli.js +1 -0
|
@@ -0,0 +1,690 @@
|
|
|
1
|
+
import { detectLocalAcpAgents as defaultDetectLocalAcpAgents, resolveCommandPath, } from '../../protocols/acp/index.js';
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { errorResponse, json, privateCacheHeaders } from '../response.js';
|
|
7
|
+
const mindosCapabilities = {
|
|
8
|
+
ownsModelSelection: true,
|
|
9
|
+
supportsResume: true,
|
|
10
|
+
supportsFreshSession: true,
|
|
11
|
+
supportsListSessions: true,
|
|
12
|
+
supportsAttachExisting: false,
|
|
13
|
+
supportsFork: false,
|
|
14
|
+
supportsArchive: false,
|
|
15
|
+
supportsInterrupt: true,
|
|
16
|
+
supportsModelList: true,
|
|
17
|
+
supportsApprovals: false,
|
|
18
|
+
supportsUserInput: true,
|
|
19
|
+
supportsToolEvents: true,
|
|
20
|
+
supportsRuntimeStatus: true,
|
|
21
|
+
supportsDiffs: false,
|
|
22
|
+
supportsCheckpoints: false,
|
|
23
|
+
supportsBackgroundRuns: false,
|
|
24
|
+
supportsMcpConfig: true,
|
|
25
|
+
};
|
|
26
|
+
const nativeBaseCapabilities = {
|
|
27
|
+
ownsModelSelection: true,
|
|
28
|
+
supportsResume: true,
|
|
29
|
+
supportsFreshSession: true,
|
|
30
|
+
supportsListSessions: false,
|
|
31
|
+
supportsAttachExisting: false,
|
|
32
|
+
supportsFork: false,
|
|
33
|
+
supportsArchive: false,
|
|
34
|
+
supportsInterrupt: true,
|
|
35
|
+
supportsModelList: false,
|
|
36
|
+
supportsApprovals: true,
|
|
37
|
+
supportsUserInput: true,
|
|
38
|
+
supportsToolEvents: true,
|
|
39
|
+
supportsRuntimeStatus: true,
|
|
40
|
+
supportsDiffs: false,
|
|
41
|
+
supportsCheckpoints: false,
|
|
42
|
+
supportsBackgroundRuns: false,
|
|
43
|
+
supportsMcpConfig: true,
|
|
44
|
+
};
|
|
45
|
+
const codexCapabilities = {
|
|
46
|
+
...nativeBaseCapabilities,
|
|
47
|
+
supportsListSessions: true,
|
|
48
|
+
supportsAttachExisting: true,
|
|
49
|
+
supportsFork: true,
|
|
50
|
+
supportsArchive: true,
|
|
51
|
+
};
|
|
52
|
+
const claudeCapabilities = {
|
|
53
|
+
...nativeBaseCapabilities,
|
|
54
|
+
};
|
|
55
|
+
const acpCapabilities = {
|
|
56
|
+
ownsModelSelection: true,
|
|
57
|
+
supportsResume: false,
|
|
58
|
+
supportsFreshSession: false,
|
|
59
|
+
supportsListSessions: false,
|
|
60
|
+
supportsAttachExisting: false,
|
|
61
|
+
supportsFork: false,
|
|
62
|
+
supportsArchive: false,
|
|
63
|
+
supportsInterrupt: true,
|
|
64
|
+
supportsModelList: false,
|
|
65
|
+
supportsApprovals: false,
|
|
66
|
+
supportsUserInput: false,
|
|
67
|
+
supportsToolEvents: true,
|
|
68
|
+
supportsRuntimeStatus: false,
|
|
69
|
+
supportsDiffs: false,
|
|
70
|
+
supportsCheckpoints: false,
|
|
71
|
+
supportsBackgroundRuns: false,
|
|
72
|
+
supportsMcpConfig: false,
|
|
73
|
+
};
|
|
74
|
+
const RUNTIME_DETECTION_TIMEOUT_MS = 5000;
|
|
75
|
+
const NATIVE_HEALTH_TIMEOUT_MS = 20000;
|
|
76
|
+
const nativeRuntimeDefinitions = [
|
|
77
|
+
{ id: 'codex-acp', name: 'Codex', runtime: 'codex', command: 'codex', installCmd: 'npm install -g @openai/codex' },
|
|
78
|
+
{ id: 'claude', name: 'Claude Code', runtime: 'claude', command: 'claude', installCmd: 'npm install -g @anthropic-ai/claude-code' },
|
|
79
|
+
];
|
|
80
|
+
function isRecord(value) {
|
|
81
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
82
|
+
}
|
|
83
|
+
function classifyRuntimeFailure(message) {
|
|
84
|
+
const normalized = message.trim() || 'Runtime failed to start.';
|
|
85
|
+
if (/\b(login|log in|signin|sign in|auth|authentication|unauthori[sz]ed|credential|api key|token)\b/i.test(normalized)) {
|
|
86
|
+
return { status: 'signed-out', reason: normalized };
|
|
87
|
+
}
|
|
88
|
+
if (/missing environment variable/i.test(normalized)) {
|
|
89
|
+
return { status: 'signed-out', reason: normalized };
|
|
90
|
+
}
|
|
91
|
+
return { status: 'error', reason: normalized };
|
|
92
|
+
}
|
|
93
|
+
function isResolvedCommandSource(value) {
|
|
94
|
+
return value === 'user-override' || value === 'descriptor' || value === 'registry';
|
|
95
|
+
}
|
|
96
|
+
function isInstalledRuntimeStatus(value) {
|
|
97
|
+
return value === 'available' || value === 'signed-out' || value === 'error';
|
|
98
|
+
}
|
|
99
|
+
function isMissingRuntimeStatus(value) {
|
|
100
|
+
return value === 'missing' || value === 'error';
|
|
101
|
+
}
|
|
102
|
+
function normalizeResolvedCommand(value) {
|
|
103
|
+
if (!isRecord(value))
|
|
104
|
+
return null;
|
|
105
|
+
if (typeof value.cmd !== 'string' || !Array.isArray(value.args) || !value.args.every((arg) => typeof arg === 'string'))
|
|
106
|
+
return null;
|
|
107
|
+
if (!isResolvedCommandSource(value.source))
|
|
108
|
+
return null;
|
|
109
|
+
return {
|
|
110
|
+
cmd: value.cmd,
|
|
111
|
+
args: value.args,
|
|
112
|
+
source: value.source,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function normalizeDiagnosticHints(value) {
|
|
116
|
+
if (!Array.isArray(value))
|
|
117
|
+
return undefined;
|
|
118
|
+
const hints = value
|
|
119
|
+
.filter((hint) => typeof hint === 'string' && hint.trim().length > 0)
|
|
120
|
+
.map((hint) => hint.trim());
|
|
121
|
+
return hints.length > 0 ? Array.from(new Set(hints)) : undefined;
|
|
122
|
+
}
|
|
123
|
+
function normalizeInstalled(value) {
|
|
124
|
+
if (!isRecord(value))
|
|
125
|
+
return null;
|
|
126
|
+
if (typeof value.id !== 'string' || typeof value.name !== 'string' || typeof value.binaryPath !== 'string')
|
|
127
|
+
return null;
|
|
128
|
+
const resolved = normalizeResolvedCommand(value.resolvedCommand);
|
|
129
|
+
const diagnosticHints = normalizeDiagnosticHints(value.diagnosticHints);
|
|
130
|
+
return {
|
|
131
|
+
id: value.id,
|
|
132
|
+
name: value.name,
|
|
133
|
+
binaryPath: value.binaryPath,
|
|
134
|
+
...(resolved ? { resolvedCommand: resolved } : {}),
|
|
135
|
+
...(isInstalledRuntimeStatus(value.status) ? { status: value.status } : {}),
|
|
136
|
+
...(typeof value.reason === 'string' && value.reason.trim() ? { reason: value.reason } : {}),
|
|
137
|
+
...(diagnosticHints ? { diagnosticHints } : {}),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function normalizeMissing(value) {
|
|
141
|
+
if (!isRecord(value))
|
|
142
|
+
return null;
|
|
143
|
+
if (typeof value.id !== 'string' || typeof value.name !== 'string' || typeof value.installCmd !== 'string')
|
|
144
|
+
return null;
|
|
145
|
+
const diagnosticHints = normalizeDiagnosticHints(value.diagnosticHints);
|
|
146
|
+
return {
|
|
147
|
+
id: value.id,
|
|
148
|
+
name: value.name,
|
|
149
|
+
installCmd: value.installCmd,
|
|
150
|
+
...(typeof value.packageName === 'string' ? { packageName: value.packageName } : {}),
|
|
151
|
+
...(isMissingRuntimeStatus(value.status) ? { status: value.status } : {}),
|
|
152
|
+
...(typeof value.reason === 'string' && value.reason.trim() ? { reason: value.reason } : {}),
|
|
153
|
+
...(diagnosticHints ? { diagnosticHints } : {}),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function isCodexAgent(agent) {
|
|
157
|
+
const name = agent.name.toLowerCase();
|
|
158
|
+
return agent.id === 'codex' || agent.id === 'codex-acp' || name === 'codex' || name.includes('codex');
|
|
159
|
+
}
|
|
160
|
+
function isClaudeAgent(agent) {
|
|
161
|
+
const name = agent.name.toLowerCase();
|
|
162
|
+
return agent.id === 'claude' || agent.id === 'claude-code' || name.includes('claude');
|
|
163
|
+
}
|
|
164
|
+
function nativeDescriptor(input) {
|
|
165
|
+
const status = input.source ? input.source.status ?? 'available' : input.missing?.status ?? 'missing';
|
|
166
|
+
const reason = input.source?.reason ?? input.missing?.reason;
|
|
167
|
+
const diagnosticHints = Array.from(new Set([
|
|
168
|
+
...(input.source?.diagnosticHints ?? input.missing?.diagnosticHints ?? []),
|
|
169
|
+
...nativeRuntimeDiagnosticHints({
|
|
170
|
+
id: input.id,
|
|
171
|
+
name: input.name,
|
|
172
|
+
status,
|
|
173
|
+
reason,
|
|
174
|
+
binaryPath: input.source?.binaryPath,
|
|
175
|
+
installCmd: input.missing?.installCmd,
|
|
176
|
+
}),
|
|
177
|
+
]));
|
|
178
|
+
return {
|
|
179
|
+
id: input.id,
|
|
180
|
+
name: input.name,
|
|
181
|
+
kind: input.id,
|
|
182
|
+
adapter: input.id === 'codex' ? 'codex-app-server' : 'claude-sdk',
|
|
183
|
+
modelOwner: 'external',
|
|
184
|
+
authOwner: 'external',
|
|
185
|
+
permissionOwner: 'external',
|
|
186
|
+
sessionOwner: 'external',
|
|
187
|
+
status,
|
|
188
|
+
capabilities: input.id === 'codex' ? codexCapabilities : claudeCapabilities,
|
|
189
|
+
description: input.id === 'codex'
|
|
190
|
+
? 'Local Codex app-server runtime. Model, approval, and thread behavior are owned by Codex.'
|
|
191
|
+
: 'Local Claude Code runtime. Model, permission, and session behavior are owned by Claude Code.',
|
|
192
|
+
aliases: input.id === 'codex' ? ['codex-acp'] : ['claude-code', 'claude'],
|
|
193
|
+
...(input.id === 'codex' ? { mcpAgentKey: 'codex' } : { mcpAgentKey: 'claude-code' }),
|
|
194
|
+
...(input.source ? {
|
|
195
|
+
sourceAgentId: input.source.id,
|
|
196
|
+
canonicalAgentId: input.source.id,
|
|
197
|
+
binaryPath: input.source.binaryPath,
|
|
198
|
+
...(input.source.resolvedCommand ? { resolvedCommand: input.source.resolvedCommand } : {}),
|
|
199
|
+
} : {}),
|
|
200
|
+
...(!input.source && input.missing ? {
|
|
201
|
+
sourceAgentId: input.missing.id,
|
|
202
|
+
canonicalAgentId: input.missing.id,
|
|
203
|
+
installCmd: input.missing.installCmd,
|
|
204
|
+
...(input.missing.packageName ? { packageName: input.missing.packageName } : {}),
|
|
205
|
+
} : {}),
|
|
206
|
+
availability: {
|
|
207
|
+
checkedAt: input.checkedAt,
|
|
208
|
+
sources: ['native-health'],
|
|
209
|
+
...(reason
|
|
210
|
+
? { reason }
|
|
211
|
+
: !input.source
|
|
212
|
+
? { reason: `${input.name} executable was not detected.` }
|
|
213
|
+
: {}),
|
|
214
|
+
...(diagnosticHints.length > 0 ? { diagnosticHints } : {}),
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function nativeRuntimeDiagnosticHints(input) {
|
|
219
|
+
if (input.status === 'available')
|
|
220
|
+
return [];
|
|
221
|
+
const command = input.id === 'codex' ? 'codex' : 'claude';
|
|
222
|
+
const hints = [];
|
|
223
|
+
if (input.status === 'missing') {
|
|
224
|
+
hints.push(`MindOS checked command "${command}" on the server PATH.`);
|
|
225
|
+
hints.push(input.installCmd
|
|
226
|
+
? `Install it or add it to the PATH used to start MindOS: ${input.installCmd}`
|
|
227
|
+
: `Install ${input.name} or add it to the PATH used to start MindOS.`);
|
|
228
|
+
return hints;
|
|
229
|
+
}
|
|
230
|
+
if (input.binaryPath) {
|
|
231
|
+
hints.push(`MindOS detected ${input.name} at ${input.binaryPath}.`);
|
|
232
|
+
}
|
|
233
|
+
if (input.status === 'signed-out') {
|
|
234
|
+
hints.push(input.id === 'codex'
|
|
235
|
+
? 'Run "codex login status" from the same environment that starts MindOS.'
|
|
236
|
+
: 'Run Claude Code once from the same environment that starts MindOS.');
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
hints.push(input.id === 'codex'
|
|
240
|
+
? 'Run "codex app-server --help" from the MindOS server environment.'
|
|
241
|
+
: 'Run "claude --version" from the MindOS server environment.');
|
|
242
|
+
}
|
|
243
|
+
if (input.reason && /(environment variable|cannot see|env)/i.test(input.reason)) {
|
|
244
|
+
hints.push('Restart MindOS after exporting the required environment variable so the server process inherits it.');
|
|
245
|
+
}
|
|
246
|
+
return hints;
|
|
247
|
+
}
|
|
248
|
+
export function buildAgentRuntimesPayload(input) {
|
|
249
|
+
const installed = input.installed.map(normalizeInstalled).filter((agent) => !!agent);
|
|
250
|
+
const notInstalled = input.notInstalled.map(normalizeMissing).filter((agent) => !!agent);
|
|
251
|
+
const codexInstalled = installed.find(isCodexAgent);
|
|
252
|
+
const claudeInstalled = installed.find(isClaudeAgent);
|
|
253
|
+
const codexMissing = notInstalled.find(isCodexAgent);
|
|
254
|
+
const claudeMissing = notInstalled.find(isClaudeAgent);
|
|
255
|
+
const runtimes = [
|
|
256
|
+
{
|
|
257
|
+
id: 'mindos',
|
|
258
|
+
name: 'MindOS',
|
|
259
|
+
kind: 'mindos',
|
|
260
|
+
adapter: 'mindos',
|
|
261
|
+
modelOwner: 'mindos',
|
|
262
|
+
authOwner: 'mindos',
|
|
263
|
+
permissionOwner: 'mindos',
|
|
264
|
+
sessionOwner: 'mindos',
|
|
265
|
+
status: 'available',
|
|
266
|
+
capabilities: mindosCapabilities,
|
|
267
|
+
description: 'MindOS internal agent using the selected provider and model.',
|
|
268
|
+
availability: { checkedAt: input.checkedAt, sources: ['settings'] },
|
|
269
|
+
},
|
|
270
|
+
nativeDescriptor({
|
|
271
|
+
id: 'codex',
|
|
272
|
+
name: 'Codex',
|
|
273
|
+
checkedAt: input.checkedAt,
|
|
274
|
+
...(codexInstalled ? { source: codexInstalled } : {}),
|
|
275
|
+
...(codexMissing ? { missing: codexMissing } : {}),
|
|
276
|
+
}),
|
|
277
|
+
nativeDescriptor({
|
|
278
|
+
id: 'claude',
|
|
279
|
+
name: 'Claude Code',
|
|
280
|
+
checkedAt: input.checkedAt,
|
|
281
|
+
...(claudeInstalled ? { source: claudeInstalled } : {}),
|
|
282
|
+
...(claudeMissing ? { missing: claudeMissing } : {}),
|
|
283
|
+
}),
|
|
284
|
+
...installed
|
|
285
|
+
.filter((agent) => !isCodexAgent(agent) && !isClaudeAgent(agent))
|
|
286
|
+
.map((agent) => ({
|
|
287
|
+
id: agent.id,
|
|
288
|
+
name: agent.name,
|
|
289
|
+
kind: 'acp',
|
|
290
|
+
adapter: 'acp',
|
|
291
|
+
modelOwner: 'external',
|
|
292
|
+
authOwner: 'external',
|
|
293
|
+
permissionOwner: 'external',
|
|
294
|
+
sessionOwner: 'external',
|
|
295
|
+
status: agent.status ?? 'available',
|
|
296
|
+
capabilities: acpCapabilities,
|
|
297
|
+
description: 'ACP agent selected as the Chat Panel runtime.',
|
|
298
|
+
sourceAgentId: agent.id,
|
|
299
|
+
canonicalAgentId: agent.id,
|
|
300
|
+
binaryPath: agent.binaryPath,
|
|
301
|
+
...(agent.resolvedCommand ? { resolvedCommand: agent.resolvedCommand } : {}),
|
|
302
|
+
availability: {
|
|
303
|
+
checkedAt: input.checkedAt,
|
|
304
|
+
sources: agent.status && agent.status !== 'available' ? ['acp-detect', 'native-health'] : ['acp-detect'],
|
|
305
|
+
...(agent.reason ? { reason: agent.reason } : {}),
|
|
306
|
+
},
|
|
307
|
+
})),
|
|
308
|
+
];
|
|
309
|
+
return { runtimes, installed, notInstalled };
|
|
310
|
+
}
|
|
311
|
+
function buildAcpScopedPayload(input) {
|
|
312
|
+
const installed = input.installed
|
|
313
|
+
.map(normalizeInstalled)
|
|
314
|
+
.filter((agent) => !!agent && !isCodexAgent(agent) && !isClaudeAgent(agent));
|
|
315
|
+
const notInstalled = input.notInstalled
|
|
316
|
+
.map(normalizeMissing)
|
|
317
|
+
.filter((agent) => !!agent && !isCodexAgent(agent) && !isClaudeAgent(agent));
|
|
318
|
+
const runtimes = installed.map((agent) => ({
|
|
319
|
+
id: agent.id,
|
|
320
|
+
name: agent.name,
|
|
321
|
+
kind: 'acp',
|
|
322
|
+
adapter: 'acp',
|
|
323
|
+
modelOwner: 'external',
|
|
324
|
+
authOwner: 'external',
|
|
325
|
+
permissionOwner: 'external',
|
|
326
|
+
sessionOwner: 'external',
|
|
327
|
+
status: agent.status ?? 'available',
|
|
328
|
+
capabilities: acpCapabilities,
|
|
329
|
+
description: 'ACP agent selected as the Chat Panel runtime.',
|
|
330
|
+
sourceAgentId: agent.id,
|
|
331
|
+
canonicalAgentId: agent.id,
|
|
332
|
+
binaryPath: agent.binaryPath,
|
|
333
|
+
...(agent.resolvedCommand ? { resolvedCommand: agent.resolvedCommand } : {}),
|
|
334
|
+
availability: {
|
|
335
|
+
checkedAt: input.checkedAt,
|
|
336
|
+
sources: agent.status && agent.status !== 'available' ? ['acp-detect', 'native-health'] : ['acp-detect'],
|
|
337
|
+
...(agent.reason ? { reason: agent.reason } : {}),
|
|
338
|
+
},
|
|
339
|
+
}));
|
|
340
|
+
return { runtimes, installed, notInstalled };
|
|
341
|
+
}
|
|
342
|
+
async function checkProcessVersion(command, args, timeoutMs) {
|
|
343
|
+
return new Promise((resolve) => {
|
|
344
|
+
const child = spawn(command, args, { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
345
|
+
let stdout = '';
|
|
346
|
+
let stderr = '';
|
|
347
|
+
let done = false;
|
|
348
|
+
const finish = (result) => {
|
|
349
|
+
if (done)
|
|
350
|
+
return;
|
|
351
|
+
done = true;
|
|
352
|
+
clearTimeout(timer);
|
|
353
|
+
if (!child.killed)
|
|
354
|
+
child.kill();
|
|
355
|
+
resolve(result);
|
|
356
|
+
};
|
|
357
|
+
const timer = setTimeout(() => {
|
|
358
|
+
finish({ status: 'error', reason: `${command} health check timed out after ${timeoutMs}ms.` });
|
|
359
|
+
}, timeoutMs);
|
|
360
|
+
child.stdout.on('data', (chunk) => { stdout += String(chunk); });
|
|
361
|
+
child.stderr.on('data', (chunk) => { stderr += String(chunk); });
|
|
362
|
+
child.once('error', (error) => finish(classifyRuntimeFailure(error.message)));
|
|
363
|
+
child.once('exit', (code) => {
|
|
364
|
+
if (code === 0) {
|
|
365
|
+
finish({ status: 'available' });
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
finish(classifyRuntimeFailure((stderr || stdout || `${command} exited with code ${code ?? 'unknown'}`).trim()));
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
async function checkCodexCliRuntime(command, timeoutMs) {
|
|
373
|
+
const appServerHelp = await checkProcessVersion(command, ['app-server', '--help'], timeoutMs);
|
|
374
|
+
if (appServerHelp.status !== 'available')
|
|
375
|
+
return appServerHelp;
|
|
376
|
+
const providerEnvironment = checkCodexProviderEnvironment();
|
|
377
|
+
if (providerEnvironment.status !== 'available')
|
|
378
|
+
return providerEnvironment;
|
|
379
|
+
const loginStatus = await checkProcessVersion(command, ['login', 'status'], timeoutMs);
|
|
380
|
+
return mergeCodexProviderAndLoginHealth(providerEnvironment, loginStatus);
|
|
381
|
+
}
|
|
382
|
+
export function mergeCodexProviderAndLoginHealth(providerEnvironment, loginStatus) {
|
|
383
|
+
if (providerEnvironment.status !== 'available')
|
|
384
|
+
return providerEnvironment;
|
|
385
|
+
if (loginStatus.status === 'available')
|
|
386
|
+
return providerEnvironment;
|
|
387
|
+
const hints = [
|
|
388
|
+
...(providerEnvironment.diagnosticHints ?? []),
|
|
389
|
+
'Codex app-server is available. If this Codex profile uses account login, run "codex login status" from the same environment that starts MindOS.',
|
|
390
|
+
...(loginStatus.reason ? [`codex login status returned: ${loginStatus.reason}`] : []),
|
|
391
|
+
];
|
|
392
|
+
return {
|
|
393
|
+
status: 'available',
|
|
394
|
+
diagnosticHints: hints,
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
export function checkCodexProviderEnvironment(input = {}) {
|
|
398
|
+
const env = input.env ?? process.env;
|
|
399
|
+
const configText = input.configText ?? readCodexConfigText(input.configPath, env);
|
|
400
|
+
if (!configText)
|
|
401
|
+
return { status: 'available' };
|
|
402
|
+
const provider = extractTomlStringValue(configText, 'model_provider');
|
|
403
|
+
if (!provider)
|
|
404
|
+
return { status: 'available' };
|
|
405
|
+
const providerSection = extractTomlSection(configText, `model_providers.${provider}`);
|
|
406
|
+
const envKey = providerSection ? extractTomlStringValue(providerSection, 'env_key') : undefined;
|
|
407
|
+
if (!envKey || env[envKey])
|
|
408
|
+
return { status: 'available' };
|
|
409
|
+
return {
|
|
410
|
+
status: 'signed-out',
|
|
411
|
+
reason: `Codex model provider "${provider}" requires ${envKey}, but MindOS cannot see that environment variable. Export ${envKey} before starting MindOS, or switch Codex to a provider that does not require it.`,
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
function readCodexConfigText(configPath, env) {
|
|
415
|
+
const resolvedPath = configPath ?? join(env.CODEX_HOME || join(homedir(), '.codex'), 'config.toml');
|
|
416
|
+
try {
|
|
417
|
+
if (!existsSync(resolvedPath))
|
|
418
|
+
return undefined;
|
|
419
|
+
return readFileSync(resolvedPath, 'utf8');
|
|
420
|
+
}
|
|
421
|
+
catch {
|
|
422
|
+
return undefined;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
function extractTomlStringValue(text, key) {
|
|
426
|
+
const escapedKey = escapeRegExp(key);
|
|
427
|
+
const match = text.match(new RegExp(`^\\s*${escapedKey}\\s*=\\s*"([^"]*)"\\s*$`, 'm'));
|
|
428
|
+
return match?.[1]?.trim() || undefined;
|
|
429
|
+
}
|
|
430
|
+
function extractTomlSection(text, sectionName) {
|
|
431
|
+
const lines = text.split(/\r?\n/);
|
|
432
|
+
const targetHeader = `[${sectionName}]`;
|
|
433
|
+
const sectionLines = [];
|
|
434
|
+
let inSection = false;
|
|
435
|
+
for (const line of lines) {
|
|
436
|
+
const trimmed = line.trim();
|
|
437
|
+
if (trimmed.startsWith('[') && trimmed.endsWith(']')) {
|
|
438
|
+
if (inSection)
|
|
439
|
+
break;
|
|
440
|
+
inSection = trimmed === targetHeader;
|
|
441
|
+
continue;
|
|
442
|
+
}
|
|
443
|
+
if (inSection)
|
|
444
|
+
sectionLines.push(line);
|
|
445
|
+
}
|
|
446
|
+
return inSection ? sectionLines.join('\n') : undefined;
|
|
447
|
+
}
|
|
448
|
+
function escapeRegExp(value) {
|
|
449
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
450
|
+
}
|
|
451
|
+
export async function defaultCheckNativeRuntimeHealth(input) {
|
|
452
|
+
const timeoutMs = input.timeoutMs ?? NATIVE_HEALTH_TIMEOUT_MS;
|
|
453
|
+
if (input.runtime === 'codex') {
|
|
454
|
+
return checkCodexCliRuntime(input.agent.binaryPath, timeoutMs);
|
|
455
|
+
}
|
|
456
|
+
return checkClaudeSdkRuntime(input.agent.binaryPath, timeoutMs);
|
|
457
|
+
}
|
|
458
|
+
async function checkClaudeSdkRuntime(binaryPath, timeoutMs) {
|
|
459
|
+
try {
|
|
460
|
+
await withTimeout(import('@anthropic-ai/claude-agent-sdk'), timeoutMs, `Claude Agent SDK health check timed out after ${timeoutMs}ms.`);
|
|
461
|
+
return {
|
|
462
|
+
status: 'available',
|
|
463
|
+
diagnosticHints: [
|
|
464
|
+
binaryPath.startsWith('sdk:')
|
|
465
|
+
? 'Claude Agent SDK is bundled with MindOS; a global claude command is optional.'
|
|
466
|
+
: `Claude CLI was also detected at ${binaryPath}; MindOS uses Claude Agent SDK first and falls back to CLI if needed.`,
|
|
467
|
+
],
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
catch (error) {
|
|
471
|
+
if (!binaryPath.startsWith('sdk:')) {
|
|
472
|
+
const fallback = await checkProcessVersion(binaryPath, ['--version'], timeoutMs);
|
|
473
|
+
return {
|
|
474
|
+
...fallback,
|
|
475
|
+
diagnosticHints: [
|
|
476
|
+
...(fallback.diagnosticHints ?? []),
|
|
477
|
+
`Claude Agent SDK is unavailable; MindOS will use CLI fallback. ${error instanceof Error ? error.message : String(error)}`,
|
|
478
|
+
],
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
throw error;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
async function applyNativeRuntimeHealth(installed, services) {
|
|
485
|
+
const checkNativeRuntimeHealth = services.checkNativeRuntimeHealth ?? defaultCheckNativeRuntimeHealth;
|
|
486
|
+
const normalized = installed.map((agent) => ({ raw: agent, detected: normalizeInstalled(agent) }));
|
|
487
|
+
const enriched = await Promise.all(normalized.map(async ({ raw, detected }) => {
|
|
488
|
+
if (!detected)
|
|
489
|
+
return raw;
|
|
490
|
+
const runtime = isCodexAgent(detected) ? 'codex' : isClaudeAgent(detected) ? 'claude' : null;
|
|
491
|
+
if (!runtime)
|
|
492
|
+
return raw;
|
|
493
|
+
if (detected.status)
|
|
494
|
+
return raw;
|
|
495
|
+
try {
|
|
496
|
+
const health = await checkNativeRuntimeHealth({
|
|
497
|
+
runtime,
|
|
498
|
+
agent: detected,
|
|
499
|
+
timeoutMs: NATIVE_HEALTH_TIMEOUT_MS,
|
|
500
|
+
});
|
|
501
|
+
return {
|
|
502
|
+
...(isRecord(raw) ? raw : detected),
|
|
503
|
+
status: health.status,
|
|
504
|
+
...(health.reason ? { reason: health.reason } : {}),
|
|
505
|
+
...(health.diagnosticHints ? { diagnosticHints: health.diagnosticHints } : {}),
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
catch (error) {
|
|
509
|
+
const result = classifyRuntimeFailure(error instanceof Error ? error.message : String(error));
|
|
510
|
+
return {
|
|
511
|
+
...(isRecord(raw) ? raw : detected),
|
|
512
|
+
status: result.status,
|
|
513
|
+
...(result.reason ? { reason: result.reason } : {}),
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
}));
|
|
517
|
+
return enriched;
|
|
518
|
+
}
|
|
519
|
+
async function detectNativeRuntimeDefinition(candidate, services) {
|
|
520
|
+
const checkNativeRuntimeHealth = services.checkNativeRuntimeHealth ?? defaultCheckNativeRuntimeHealth;
|
|
521
|
+
const resolveRuntimeCommand = services.resolveRuntimeCommand ?? resolveCommandPath;
|
|
522
|
+
const binaryPath = await resolveRuntimeCommand(candidate.command);
|
|
523
|
+
if (!binaryPath) {
|
|
524
|
+
if (candidate.runtime === 'claude') {
|
|
525
|
+
const sdkBinaryPath = 'sdk:@anthropic-ai/claude-agent-sdk';
|
|
526
|
+
try {
|
|
527
|
+
const health = await checkNativeRuntimeHealth({
|
|
528
|
+
runtime: candidate.runtime,
|
|
529
|
+
agent: { id: candidate.id, name: candidate.name, binaryPath: sdkBinaryPath },
|
|
530
|
+
timeoutMs: NATIVE_HEALTH_TIMEOUT_MS,
|
|
531
|
+
});
|
|
532
|
+
return {
|
|
533
|
+
id: candidate.id,
|
|
534
|
+
name: candidate.name,
|
|
535
|
+
binaryPath: sdkBinaryPath,
|
|
536
|
+
status: health.status,
|
|
537
|
+
reason: health.reason,
|
|
538
|
+
diagnosticHints: [
|
|
539
|
+
...(health.diagnosticHints ?? []),
|
|
540
|
+
'Global claude command was not detected; CLI fallback will be unavailable until it is installed or on MindOS server PATH.',
|
|
541
|
+
],
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
catch (error) {
|
|
545
|
+
const result = classifyRuntimeFailure(error instanceof Error ? error.message : String(error));
|
|
546
|
+
return {
|
|
547
|
+
id: candidate.id,
|
|
548
|
+
name: candidate.name,
|
|
549
|
+
installCmd: candidate.installCmd,
|
|
550
|
+
packageName: candidate.installCmd.match(/npm install -g (.+)/)?.[1],
|
|
551
|
+
status: 'error',
|
|
552
|
+
reason: `Claude Agent SDK is unavailable and the global claude command was not detected. ${result.reason ?? ''}`.trim(),
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
return {
|
|
557
|
+
id: candidate.id,
|
|
558
|
+
name: candidate.name,
|
|
559
|
+
installCmd: candidate.installCmd,
|
|
560
|
+
packageName: candidate.installCmd.match(/npm install -g (.+)/)?.[1],
|
|
561
|
+
status: 'missing',
|
|
562
|
+
reason: `${candidate.name} executable was not detected.`,
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
try {
|
|
566
|
+
const health = await checkNativeRuntimeHealth({
|
|
567
|
+
runtime: candidate.runtime,
|
|
568
|
+
agent: { id: candidate.id, name: candidate.name, binaryPath },
|
|
569
|
+
timeoutMs: NATIVE_HEALTH_TIMEOUT_MS,
|
|
570
|
+
});
|
|
571
|
+
return {
|
|
572
|
+
id: candidate.id,
|
|
573
|
+
name: candidate.name,
|
|
574
|
+
binaryPath,
|
|
575
|
+
resolvedCommand: { cmd: candidate.command, args: [], source: 'descriptor' },
|
|
576
|
+
status: health.status,
|
|
577
|
+
...(health.reason ? { reason: health.reason } : {}),
|
|
578
|
+
...(health.diagnosticHints ? { diagnosticHints: health.diagnosticHints } : {}),
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
catch (error) {
|
|
582
|
+
const result = classifyRuntimeFailure(error instanceof Error ? error.message : String(error));
|
|
583
|
+
return {
|
|
584
|
+
id: candidate.id,
|
|
585
|
+
name: candidate.name,
|
|
586
|
+
binaryPath,
|
|
587
|
+
resolvedCommand: { cmd: candidate.command, args: [], source: 'descriptor' },
|
|
588
|
+
status: result.status,
|
|
589
|
+
...(result.reason ? { reason: result.reason } : {}),
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
async function detectNativeRuntimes(services) {
|
|
594
|
+
const results = await Promise.all(nativeRuntimeDefinitions.map((candidate) => detectNativeRuntimeDefinition(candidate, services)));
|
|
595
|
+
return {
|
|
596
|
+
installed: results.filter((agent) => 'binaryPath' in agent),
|
|
597
|
+
notInstalled: results.filter((agent) => 'installCmd' in agent),
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
function isNativeRuntimeId(value) {
|
|
601
|
+
return value === 'codex' || value === 'claude';
|
|
602
|
+
}
|
|
603
|
+
async function detectSingleNativeRuntime(runtime, services) {
|
|
604
|
+
const candidate = nativeRuntimeDefinitions.find((definition) => definition.runtime === runtime);
|
|
605
|
+
if (!candidate)
|
|
606
|
+
throw new Error(`Unsupported native runtime: ${runtime}`);
|
|
607
|
+
const result = await detectNativeRuntimeDefinition(candidate, services);
|
|
608
|
+
const checkedAt = new Date(services.now?.() ?? Date.now()).toISOString();
|
|
609
|
+
return nativeDescriptor({
|
|
610
|
+
id: runtime,
|
|
611
|
+
name: candidate.name,
|
|
612
|
+
checkedAt,
|
|
613
|
+
...('binaryPath' in result ? { source: result } : { missing: result }),
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
async function withTimeout(promise, timeoutMs, message) {
|
|
617
|
+
let timer = null;
|
|
618
|
+
try {
|
|
619
|
+
return await Promise.race([
|
|
620
|
+
promise,
|
|
621
|
+
new Promise((_, reject) => {
|
|
622
|
+
timer = setTimeout(() => reject(new Error(message)), timeoutMs);
|
|
623
|
+
}),
|
|
624
|
+
]);
|
|
625
|
+
}
|
|
626
|
+
finally {
|
|
627
|
+
if (timer)
|
|
628
|
+
clearTimeout(timer);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
export async function handleAgentRuntimesGet(searchParams, services = {}) {
|
|
632
|
+
try {
|
|
633
|
+
const scope = searchParams.get('scope');
|
|
634
|
+
if (scope && scope !== 'acp') {
|
|
635
|
+
return json({ error: `Unsupported scope: ${scope}` }, { status: 400 });
|
|
636
|
+
}
|
|
637
|
+
const runtime = searchParams.get('runtime');
|
|
638
|
+
if (runtime) {
|
|
639
|
+
if (!isNativeRuntimeId(runtime)) {
|
|
640
|
+
return json({ error: `Unsupported runtime: ${runtime}` }, { status: 400 });
|
|
641
|
+
}
|
|
642
|
+
const descriptor = await detectSingleNativeRuntime(runtime, services);
|
|
643
|
+
return json({ runtime: descriptor }, { headers: { 'Cache-Control': 'no-store' } });
|
|
644
|
+
}
|
|
645
|
+
const detectLocalAcpAgents = services.detectLocalAcpAgents ?? defaultDetectLocalAcpAgents;
|
|
646
|
+
if (scope === 'acp') {
|
|
647
|
+
const acpDetection = await withTimeout(detectLocalAcpAgents({ overrides: services.readSettings?.().acpAgents }), RUNTIME_DETECTION_TIMEOUT_MS, `Agent runtime detection timed out after ${RUNTIME_DETECTION_TIMEOUT_MS}ms.`);
|
|
648
|
+
return json(buildAcpScopedPayload({
|
|
649
|
+
installed: Array.isArray(acpDetection.installed) ? acpDetection.installed : [],
|
|
650
|
+
notInstalled: Array.isArray(acpDetection.notInstalled) ? acpDetection.notInstalled : [],
|
|
651
|
+
checkedAt: new Date(services.now?.() ?? Date.now()).toISOString(),
|
|
652
|
+
}), { headers: searchParams.get('force') === '1' ? { 'Cache-Control': 'no-store' } : privateCacheHeaders(1800) });
|
|
653
|
+
}
|
|
654
|
+
const nativeDetectionPromise = detectNativeRuntimes(services);
|
|
655
|
+
const acpDetectionPromise = (async () => {
|
|
656
|
+
try {
|
|
657
|
+
return await withTimeout(detectLocalAcpAgents({ overrides: services.readSettings?.().acpAgents }), RUNTIME_DETECTION_TIMEOUT_MS, `Agent runtime detection timed out after ${RUNTIME_DETECTION_TIMEOUT_MS}ms.`);
|
|
658
|
+
}
|
|
659
|
+
catch {
|
|
660
|
+
return { installed: [], notInstalled: [] };
|
|
661
|
+
}
|
|
662
|
+
})();
|
|
663
|
+
const [nativeDetection, acpDetection] = await Promise.all([nativeDetectionPromise, acpDetectionPromise]);
|
|
664
|
+
const acpInstalled = Array.isArray(acpDetection.installed) ? acpDetection.installed : [];
|
|
665
|
+
const acpNotInstalled = Array.isArray(acpDetection.notInstalled) ? acpDetection.notInstalled : [];
|
|
666
|
+
const acpRuntimeInstalled = acpInstalled.filter((agent) => {
|
|
667
|
+
const normalized = normalizeInstalled(agent);
|
|
668
|
+
return !normalized || (!isCodexAgent(normalized) && !isClaudeAgent(normalized));
|
|
669
|
+
});
|
|
670
|
+
const acpRuntimeNotInstalled = acpNotInstalled.filter((agent) => {
|
|
671
|
+
const normalized = normalizeMissing(agent);
|
|
672
|
+
return !normalized || (!isCodexAgent(normalized) && !isClaudeAgent(normalized));
|
|
673
|
+
});
|
|
674
|
+
const installed = await applyNativeRuntimeHealth([...nativeDetection.installed, ...acpRuntimeInstalled], services);
|
|
675
|
+
const payload = buildAgentRuntimesPayload({
|
|
676
|
+
installed,
|
|
677
|
+
notInstalled: [...nativeDetection.notInstalled, ...acpRuntimeNotInstalled],
|
|
678
|
+
checkedAt: new Date(services.now?.() ?? Date.now()).toISOString(),
|
|
679
|
+
});
|
|
680
|
+
return json({
|
|
681
|
+
...payload,
|
|
682
|
+
installed: acpInstalled.map(normalizeInstalled).filter((agent) => !!agent),
|
|
683
|
+
notInstalled: acpNotInstalled.map(normalizeMissing).filter((agent) => !!agent),
|
|
684
|
+
}, { headers: searchParams.get('force') === '1' ? { 'Cache-Control': 'no-store' } : privateCacheHeaders(1800) });
|
|
685
|
+
}
|
|
686
|
+
catch (error) {
|
|
687
|
+
return errorResponse(error);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
//# sourceMappingURL=agent-runtimes.js.map
|