fraim-hub 2.0.351 → 2.0.352
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/src/ai-hub/configured-agent-child.js +172 -0
- package/dist/src/ai-hub/configured-agent-command.js +204 -162
- package/dist/src/ai-hub/configured-agent-runner.js +185 -0
- package/dist/src/ai-hub/configured-agents.js +95 -130
- package/dist/src/ai-hub/hosts.js +33 -25
- package/dist/src/ai-hub/server.js +30 -11
- package/dist/src/cli/utils/managed-agent-paths.js +25 -9
- package/package.json +2 -2
- package/public/ai-hub/script.js +46 -36
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CONFIGURED_FAILURES = void 0;
|
|
7
|
+
exports.configuredLaunchFailure = configuredLaunchFailure;
|
|
8
|
+
exports.spawnConfiguredAgentProcess = spawnConfiguredAgentProcess;
|
|
9
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
10
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
11
|
+
const node_child_process_1 = require("node:child_process");
|
|
12
|
+
const project_fraim_paths_1 = require("../core/utils/project-fraim-paths");
|
|
13
|
+
const background_command_1 = require("./background-command");
|
|
14
|
+
const configured_agent_command_1 = require("./configured-agent-command");
|
|
15
|
+
const POWERSHELL = String.raw `
|
|
16
|
+
$ErrorActionPreference = 'Stop'
|
|
17
|
+
function Invoke-FraimHelper {
|
|
18
|
+
$helperArgs = @()
|
|
19
|
+
if ($env:FRAIM_CONFIGURED_LOADER) { $helperArgs += $env:FRAIM_CONFIGURED_LOADER }
|
|
20
|
+
$helperArgs += $env:FRAIM_CONFIGURED_HELPER
|
|
21
|
+
& $env:FRAIM_CONFIGURED_NODE @helperArgs @args
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
$fraimSpec = ConvertFrom-Json $env:FRAIM_CONFIGURED_SPEC
|
|
25
|
+
if ($fraimSpec.setupScript) {
|
|
26
|
+
$LASTEXITCODE = 0
|
|
27
|
+
$fraimSetupArgs = @($fraimSpec.setupScript.args)
|
|
28
|
+
. $fraimSpec.setupScript.command @fraimSetupArgs *> $null
|
|
29
|
+
if ($LASTEXITCODE -ne 0) { throw 'Configured setup failed' }
|
|
30
|
+
}
|
|
31
|
+
$fraimSpec = ConvertFrom-Json $env:FRAIM_CONFIGURED_SPEC
|
|
32
|
+
if ($fraimSpec.envPrefixText) { Invoke-Expression $fraimSpec.envPrefixText *> $null }
|
|
33
|
+
} catch { Invoke-FraimHelper --fail preparation; exit 70 }
|
|
34
|
+
try {
|
|
35
|
+
$fraimSpec = ConvertFrom-Json $env:FRAIM_CONFIGURED_SPEC
|
|
36
|
+
$fraimTarget = Get-Command -Name ([System.Management.Automation.WildcardPattern]::Escape($fraimSpec.target)) -ErrorAction Stop | Select-Object -First 1
|
|
37
|
+
if ($fraimTarget.CommandType -in @('Function','Alias','Filter')) { Invoke-FraimHelper --fail migration; exit 74 }
|
|
38
|
+
if ($fraimTarget.CommandType -notin @('Application','ExternalScript')) { throw 'Unsupported target' }
|
|
39
|
+
} catch { Invoke-FraimHelper --fail resolution; exit 71 }
|
|
40
|
+
$env:FRAIM_CONFIGURED_TARGET = $fraimTarget.Source
|
|
41
|
+
if ($fraimTarget.CommandType -eq 'Application') {
|
|
42
|
+
Invoke-FraimHelper
|
|
43
|
+
exit $LASTEXITCODE
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
$fraimControl = $env:FRAIM_CONFIGURED_CONTROL
|
|
47
|
+
$fraimPrepared = Invoke-FraimHelper --prepare-shell | ConvertFrom-Json
|
|
48
|
+
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
49
|
+
foreach ($property in $fraimPrepared.env.PSObject.Properties) {
|
|
50
|
+
[Environment]::SetEnvironmentVariable($property.Name, [string]$property.Value, 'Process')
|
|
51
|
+
}
|
|
52
|
+
$fraimArgs = @($fraimPrepared.args)
|
|
53
|
+
Get-ChildItem Env:FRAIM_CONFIGURED_* | Remove-Item
|
|
54
|
+
& $fraimTarget @fraimArgs
|
|
55
|
+
if ($null -eq $LASTEXITCODE) { exit 0 }
|
|
56
|
+
exit $LASTEXITCODE
|
|
57
|
+
} catch { [IO.File]::WriteAllText((Join-Path $fraimControl 'failure'), 'invocation'); exit 72 }
|
|
58
|
+
`.trimStart();
|
|
59
|
+
const BASH = `#!/usr/bin/env bash
|
|
60
|
+
set -Ee
|
|
61
|
+
fraim_helper() {
|
|
62
|
+
local helper=("$FRAIM_CONFIGURED_NODE")
|
|
63
|
+
if [ -n "$FRAIM_CONFIGURED_LOADER" ]; then helper+=("$FRAIM_CONFIGURED_LOADER"); fi
|
|
64
|
+
helper+=("$FRAIM_CONFIGURED_HELPER")
|
|
65
|
+
"\${helper[@]}" "$@"
|
|
66
|
+
}
|
|
67
|
+
trap 'fraim_helper --fail preparation; exit 70' ERR
|
|
68
|
+
fraim_fields=()
|
|
69
|
+
while IFS= read -r -d '' fraim_field; do fraim_fields+=("$fraim_field"); done < <(fraim_helper --shell-data)
|
|
70
|
+
if [ "\${#fraim_fields[@]}" -lt 3 ]; then fraim_helper --fail preparation; exit 70; fi
|
|
71
|
+
if [ -n "\${fraim_fields[2]}" ]; then
|
|
72
|
+
source "\${fraim_fields[2]}" "\${fraim_fields[@]:3}" >/dev/null 2>&1
|
|
73
|
+
fi
|
|
74
|
+
if [ -n "\${fraim_fields[1]}" ]; then
|
|
75
|
+
set -a
|
|
76
|
+
eval "\${fraim_fields[1]}" >/dev/null 2>&1
|
|
77
|
+
set +a
|
|
78
|
+
fi
|
|
79
|
+
trap - ERR
|
|
80
|
+
if [ "$(type -t -- "\${fraim_fields[0]}")" = function ] || [ "$(type -t -- "\${fraim_fields[0]}")" = alias ]; then fraim_helper --fail migration; exit 74; fi
|
|
81
|
+
if ! fraim_target=$(type -P -- "\${fraim_fields[0]}"); then fraim_helper --fail resolution; exit 71; fi
|
|
82
|
+
case "$OSTYPE" in
|
|
83
|
+
msys*|cygwin*)
|
|
84
|
+
# Bash transparently resolves foo.exe as foo. Native Node needs the real
|
|
85
|
+
# filename and a Windows path, including installations outside /c mounts.
|
|
86
|
+
fraim_target=$(cygpath -w -- "$fraim_target") || { fraim_helper --fail resolution; exit 71; }
|
|
87
|
+
;;
|
|
88
|
+
esac
|
|
89
|
+
export FRAIM_CONFIGURED_TARGET="$fraim_target"
|
|
90
|
+
fraim_helper
|
|
91
|
+
exit $?
|
|
92
|
+
`;
|
|
93
|
+
exports.CONFIGURED_FAILURES = {
|
|
94
|
+
preparation: 'Configured agent preparation failed. Check the setup script and environment prefix. ' + configured_agent_command_1.CONFIGURED_TROUBLESHOOTING,
|
|
95
|
+
resolution: 'Base CLI was not found in the prepared session. Install the selected base CLI or repair its setup PATH. ' + configured_agent_command_1.CONFIGURED_TROUBLESHOOTING,
|
|
96
|
+
invocation: 'Base CLI could not be started. Check the installation and runner. ' + configured_agent_command_1.CONFIGURED_TROUBLESHOOTING,
|
|
97
|
+
migration: configured_agent_command_1.CONFIGURED_MIGRATION,
|
|
98
|
+
timeout: 'Configured agent preparation timed out. ' + configured_agent_command_1.CONFIGURED_TROUBLESHOOTING,
|
|
99
|
+
interrupted: 'Configured agent preparation ended before the CLI was ready. ' + configured_agent_command_1.CONFIGURED_TROUBLESHOOTING,
|
|
100
|
+
};
|
|
101
|
+
const failures = new WeakMap();
|
|
102
|
+
function configuredLaunchFailure(child) { return failures.get(child); }
|
|
103
|
+
/** One owned shell tree; the private control directory never stores commands or credentials. */
|
|
104
|
+
function spawnConfiguredAgentProcess(hostId, plan, cwd, env, onFailure) {
|
|
105
|
+
const invocation = plan.configuredInvocation;
|
|
106
|
+
const startedAtMs = Date.now();
|
|
107
|
+
const home = (0, project_fraim_paths_1.getUserFraimDirPath)();
|
|
108
|
+
node_fs_1.default.mkdirSync(home, { recursive: true });
|
|
109
|
+
const control = node_fs_1.default.mkdtempSync(node_path_1.default.join(home, 'configured-launch-'));
|
|
110
|
+
node_fs_1.default.chmodSync(control, 0o700);
|
|
111
|
+
const runnerPath = node_path_1.default.join(home, invocation.runner === 'powershell' ? 'hub-configured-launch.ps1' : 'hub-configured-launch.sh');
|
|
112
|
+
const script = invocation.runner === 'powershell' ? POWERSHELL : BASH;
|
|
113
|
+
if (!node_fs_1.default.existsSync(runnerPath) || node_fs_1.default.readFileSync(runnerPath, 'utf8') !== script) {
|
|
114
|
+
node_fs_1.default.writeFileSync(runnerPath, script, { mode: 0o700 });
|
|
115
|
+
}
|
|
116
|
+
const source = __filename.endsWith('.ts');
|
|
117
|
+
const helper = node_path_1.default.join(__dirname, `configured-agent-child.${source ? 'ts' : 'js'}`);
|
|
118
|
+
const shellPath = (value) => invocation.runner === 'bash' ? value.replace(/\\/g, '/') : value;
|
|
119
|
+
const childEnv = {
|
|
120
|
+
...env,
|
|
121
|
+
FRAIM_CONFIGURED_SPEC: JSON.stringify({ ...invocation, args: plan.args, hostId, browser: !plan.directMode }),
|
|
122
|
+
FRAIM_CONFIGURED_CONTROL: control,
|
|
123
|
+
FRAIM_CONFIGURED_HELPER: shellPath(helper),
|
|
124
|
+
FRAIM_CONFIGURED_LOADER: source ? shellPath(require.resolve('tsx/cli')) : '',
|
|
125
|
+
FRAIM_CONFIGURED_NODE: shellPath(process.execPath),
|
|
126
|
+
FRAIM_CONFIGURED_ELECTRON_MODE: env.ELECTRON_RUN_AS_NODE ?? '',
|
|
127
|
+
ELECTRON_RUN_AS_NODE: '1',
|
|
128
|
+
};
|
|
129
|
+
const child = (0, node_child_process_1.spawn)(invocation.runner === 'powershell' ? 'powershell.exe' : 'bash', invocation.runner === 'powershell'
|
|
130
|
+
? ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-File', runnerPath]
|
|
131
|
+
: ['--noprofile', '--norc', shellPath(runnerPath)], { cwd, env: childEnv, windowsHide: true, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
132
|
+
let ready = false;
|
|
133
|
+
let preparedAtMs;
|
|
134
|
+
let ended = false;
|
|
135
|
+
const fail = (stage) => {
|
|
136
|
+
if (failures.has(child))
|
|
137
|
+
return;
|
|
138
|
+
const reason = exports.CONFIGURED_FAILURES[stage] || exports.CONFIGURED_FAILURES.invocation;
|
|
139
|
+
failures.set(child, reason);
|
|
140
|
+
onFailure(reason);
|
|
141
|
+
};
|
|
142
|
+
const readControl = () => {
|
|
143
|
+
if (node_fs_1.default.existsSync(node_path_1.default.join(control, 'ready'))) {
|
|
144
|
+
preparedAtMs ??= Date.now();
|
|
145
|
+
ready = true;
|
|
146
|
+
clearTimeout(timer);
|
|
147
|
+
clearInterval(poll);
|
|
148
|
+
}
|
|
149
|
+
try {
|
|
150
|
+
const stage = node_fs_1.default.readFileSync(node_path_1.default.join(control, 'failure'), 'utf8');
|
|
151
|
+
fail(stage);
|
|
152
|
+
}
|
|
153
|
+
catch { /* The helper has not reported a failure. */ }
|
|
154
|
+
};
|
|
155
|
+
const poll = setInterval(readControl, 25);
|
|
156
|
+
const timer = setTimeout(() => {
|
|
157
|
+
readControl();
|
|
158
|
+
if (ready || ended)
|
|
159
|
+
return;
|
|
160
|
+
fail('timeout');
|
|
161
|
+
if (child.pid)
|
|
162
|
+
(0, background_command_1.killBackgroundProcessTree)(child.pid, 'SIGKILL');
|
|
163
|
+
}, invocation.timeoutMs);
|
|
164
|
+
child.on('error', () => fail('invocation'));
|
|
165
|
+
child.once('close', (exitCode, signal) => {
|
|
166
|
+
ended = true;
|
|
167
|
+
readControl();
|
|
168
|
+
if (!ready && !failures.has(child))
|
|
169
|
+
fail('interrupted');
|
|
170
|
+
clearInterval(poll);
|
|
171
|
+
clearTimeout(timer);
|
|
172
|
+
console.info('[ai-hub] hub.configured_agent_launch', {
|
|
173
|
+
configuredAgentId: invocation.configuredAgentId,
|
|
174
|
+
hostId,
|
|
175
|
+
runner: invocation.runner,
|
|
176
|
+
preparationDurationMs: Math.max(0, (preparedAtMs ?? Date.now()) - startedAtMs),
|
|
177
|
+
durationMs: Math.max(0, Date.now() - startedAtMs),
|
|
178
|
+
exitCode,
|
|
179
|
+
signal,
|
|
180
|
+
failure: failures.get(child),
|
|
181
|
+
});
|
|
182
|
+
node_fs_1.default.rmSync(control, { recursive: true, force: true });
|
|
183
|
+
});
|
|
184
|
+
return child;
|
|
185
|
+
}
|
|
@@ -1,22 +1,56 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
5
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
39
|
+
exports.describeConfiguredCommandInvocationProblem = exports.decorateHostPlanWithConfiguredAgent = exports.commandContainsLiteralSecret = exports.parseConfiguredCommand = exports.AiHubConfiguredAgentStore = void 0;
|
|
7
40
|
exports.synthesizeDefaultConfiguredAgents = synthesizeDefaultConfiguredAgents;
|
|
8
41
|
exports.checkConfiguredAgentAvailability = checkConfiguredAgentAvailability;
|
|
9
42
|
exports.checkConfiguredAgentReadiness = checkConfiguredAgentReadiness;
|
|
10
43
|
exports.projectConfiguredAgent = projectConfiguredAgent;
|
|
11
44
|
exports.resolveConfiguredAgentForHost = resolveConfiguredAgentForHost;
|
|
12
|
-
exports.resolveConfiguredAgentEnv = resolveConfiguredAgentEnv;
|
|
13
45
|
const fs_1 = __importDefault(require("fs"));
|
|
14
46
|
const path_1 = __importDefault(require("path"));
|
|
15
|
-
const child_process_1 = require("child_process");
|
|
16
47
|
const project_fraim_paths_1 = require("../core/utils/project-fraim-paths");
|
|
48
|
+
const background_command_1 = require("./background-command");
|
|
49
|
+
const configured_agent_runner_1 = require("./configured-agent-runner");
|
|
50
|
+
const run_working_directory_1 = require("./run-working-directory");
|
|
17
51
|
const configured_agent_command_1 = require("./configured-agent-command");
|
|
52
|
+
const configured_agent_command_2 = require("./configured-agent-command");
|
|
18
53
|
const VALID_HOSTS = ['codex', 'claude', 'gemini', 'copilot'];
|
|
19
|
-
const ENV_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
20
54
|
function nowIso() {
|
|
21
55
|
return new Date().toISOString();
|
|
22
56
|
}
|
|
@@ -82,7 +116,7 @@ function normalizeSetupScript(value) {
|
|
|
82
116
|
};
|
|
83
117
|
}
|
|
84
118
|
// Issue #1201 (R2.1): the single-text-field setup surface. `runner` is an
|
|
85
|
-
// internal execution detail
|
|
119
|
+
// internal execution detail — callers that omit it get the current platform's
|
|
86
120
|
// default shell so the UI never has to ask the user to pick one.
|
|
87
121
|
function normalizeCommand(value) {
|
|
88
122
|
if (!value)
|
|
@@ -166,69 +200,84 @@ class AiHubConfiguredAgentStore {
|
|
|
166
200
|
}
|
|
167
201
|
}
|
|
168
202
|
exports.AiHubConfiguredAgentStore = AiHubConfiguredAgentStore;
|
|
169
|
-
function checkConfiguredAgentAvailability(agent, hosts
|
|
203
|
+
function checkConfiguredAgentAvailability(agent, hosts) {
|
|
170
204
|
const reasons = [];
|
|
171
205
|
const warnings = [];
|
|
172
206
|
const host = hosts.find((entry) => entry.id === agent.baseHostId);
|
|
173
207
|
if (!agent.enabled)
|
|
174
|
-
reasons.push('Configured agent is disabled.');
|
|
175
|
-
if (!host?.available)
|
|
176
|
-
reasons.push(`${host?.label || agent.baseHostId} is not available on this machine
|
|
208
|
+
reasons.push('Configured agent is disabled. Enable this profile before starting it.');
|
|
209
|
+
if (!agent.command && !agent.setupScript && !host?.available)
|
|
210
|
+
reasons.push(`${host?.label || agent.baseHostId} is not available on this machine. Install this base CLI. ` + configured_agent_command_1.CONFIGURED_TROUBLESHOOTING);
|
|
211
|
+
if (agent.command && agent.setupScript && agent.command.runner !== agent.setupScript.runner) {
|
|
212
|
+
reasons.push('Configured command and setup script must use the same runner.');
|
|
213
|
+
}
|
|
177
214
|
if (agent.command) {
|
|
178
215
|
if (!agent.command.command.trim()) {
|
|
179
216
|
reasons.push('Command is empty.');
|
|
180
217
|
}
|
|
181
218
|
else {
|
|
182
219
|
try {
|
|
183
|
-
const parsed = (0,
|
|
184
|
-
const problem = (0,
|
|
220
|
+
const parsed = (0, configured_agent_command_2.parseConfiguredCommand)(agent.command.runner, agent.command.command);
|
|
221
|
+
const problem = (0, configured_agent_command_2.describeConfiguredCommandInvocationProblem)(parsed, agent.baseHostId);
|
|
185
222
|
if (problem) {
|
|
186
223
|
reasons.push(problem);
|
|
187
224
|
}
|
|
188
|
-
else if (parsed.invocationHead) {
|
|
189
|
-
const probeError = (0, configured_agent_command_1.probeCommandResolvesNonInteractively)(agent.command.runner, parsed.invocationHead);
|
|
190
|
-
if (probeError)
|
|
191
|
-
reasons.push(probeError);
|
|
192
|
-
}
|
|
193
225
|
}
|
|
194
226
|
catch (error) {
|
|
195
|
-
reasons.push(
|
|
227
|
+
reasons.push('Configured agent command contains unsupported syntax. ' + configured_agent_command_1.CONFIGURED_MIGRATION);
|
|
196
228
|
}
|
|
197
229
|
}
|
|
198
230
|
}
|
|
199
|
-
|
|
231
|
+
if (agent.setupScript) {
|
|
200
232
|
const command = agent.setupScript.command;
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
233
|
+
// Relative scripts belong to the selected run's cwd, which a roster check does
|
|
234
|
+
// not have. Let the common preparation runner resolve those at check/start.
|
|
235
|
+
if (path_1.default.isAbsolute(command) && !fs_1.default.existsSync(command)) {
|
|
236
|
+
reasons.push('Configured agent setup script is missing. Restore its setup file. ' + configured_agent_command_1.CONFIGURED_TROUBLESHOOTING);
|
|
204
237
|
}
|
|
205
238
|
}
|
|
206
|
-
else if (!agent.id.endsWith('-default')) {
|
|
239
|
+
else if (!agent.command && !agent.id.endsWith('-default')) {
|
|
207
240
|
warnings.push('No command configured; this profile launches the base CLI unchanged.');
|
|
208
241
|
}
|
|
209
242
|
return { id: agent.id, label: agent.label, baseHostId: agent.baseHostId, enabled: agent.enabled, available: reasons.length === 0, reasons, warnings };
|
|
210
243
|
}
|
|
211
|
-
function checkConfiguredAgentReadiness(agent, hosts, env = process.env) {
|
|
212
|
-
const check = checkConfiguredAgentAvailability(agent, hosts
|
|
244
|
+
async function checkConfiguredAgentReadiness(agent, hosts, env = process.env, cwd = process.cwd()) {
|
|
245
|
+
const check = checkConfiguredAgentAvailability(agent, hosts);
|
|
213
246
|
if (!check.available)
|
|
214
247
|
return check;
|
|
215
|
-
if (agent.command)
|
|
216
|
-
return check;
|
|
217
|
-
if (!agent.setupScript)
|
|
248
|
+
if (!agent.command && !agent.setupScript)
|
|
218
249
|
return check;
|
|
219
250
|
try {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
251
|
+
const { buildHostSpawnEnv } = await Promise.resolve().then(() => __importStar(require('./hosts')));
|
|
252
|
+
const plan = (0, configured_agent_command_1.decorateHostPlanWithConfiguredAgent)({ command: agent.baseHostId, args: ['--version'], directMode: true }, agent);
|
|
253
|
+
let failure;
|
|
254
|
+
const child = (0, configured_agent_runner_1.spawnConfiguredAgentProcess)(agent.baseHostId, plan, (0, run_working_directory_1.resolveHostWorkingDirectory)(cwd), buildHostSpawnEnv(plan, agent.baseHostId, env), reason => { failure = reason; });
|
|
255
|
+
child.stdin.end();
|
|
256
|
+
let bytes = 0;
|
|
257
|
+
const capture = (chunk) => {
|
|
258
|
+
bytes += chunk.length;
|
|
259
|
+
if (bytes > 1024 * 1024 && child.pid) {
|
|
260
|
+
failure = 'Configured agent readiness produced excessive output.';
|
|
261
|
+
(0, background_command_1.killBackgroundProcessTree)(child.pid, 'SIGKILL');
|
|
262
|
+
}
|
|
231
263
|
};
|
|
264
|
+
child.stdout.on('data', capture);
|
|
265
|
+
child.stderr.on('data', capture);
|
|
266
|
+
const code = await new Promise((resolve) => {
|
|
267
|
+
const timer = setTimeout(() => {
|
|
268
|
+
failure = 'Configured agent readiness timed out.';
|
|
269
|
+
if (child.pid)
|
|
270
|
+
(0, background_command_1.killBackgroundProcessTree)(child.pid, 'SIGKILL');
|
|
271
|
+
}, (plan.configuredInvocation?.timeoutMs || 30_000) + 5000);
|
|
272
|
+
child.once('close', code => { clearTimeout(timer); resolve(code); });
|
|
273
|
+
});
|
|
274
|
+
failure ||= (0, configured_agent_runner_1.configuredLaunchFailure)(child);
|
|
275
|
+
if (failure || code !== 0)
|
|
276
|
+
return { ...check, available: false, reasons: [failure || 'Configured agent version check failed. Check the base CLI installation and exit status. ' + configured_agent_command_1.CONFIGURED_TROUBLESHOOTING] };
|
|
277
|
+
return { ...check, warnings: [...check.warnings, 'The command passed its version check. Authentication and Hub protocol compatibility are verified when a conversation starts.'] };
|
|
278
|
+
}
|
|
279
|
+
catch {
|
|
280
|
+
return { ...check, available: false, reasons: ['Configured agent readiness could not be completed. Check its runner and setup. ' + configured_agent_command_1.CONFIGURED_TROUBLESHOOTING] };
|
|
232
281
|
}
|
|
233
282
|
}
|
|
234
283
|
function projectConfiguredAgent(agent, hosts) {
|
|
@@ -252,6 +301,8 @@ function resolveConfiguredAgentForHost(configuredAgentId, hostId, agents) {
|
|
|
252
301
|
const explicit = configuredAgentId ? agents.find((agent) => agent.id === configuredAgentId) : undefined;
|
|
253
302
|
if (explicit)
|
|
254
303
|
return { agent: explicit, baseHostId: explicit.baseHostId };
|
|
304
|
+
if (configuredAgentId)
|
|
305
|
+
throw new Error('Selected configured agent no longer exists. Select an available profile.');
|
|
255
306
|
const fallbackHost = hostId && VALID_HOSTS.includes(hostId) ? hostId : 'claude';
|
|
256
307
|
const fallback = agents.find((agent) => agent.id === `${fallbackHost}-default`) || {
|
|
257
308
|
id: `${fallbackHost}-default`,
|
|
@@ -261,99 +312,13 @@ function resolveConfiguredAgentForHost(configuredAgentId, hostId, agents) {
|
|
|
261
312
|
};
|
|
262
313
|
return { agent: fallback, baseHostId: fallback.baseHostId };
|
|
263
314
|
}
|
|
264
|
-
function resolveConfiguredAgentEnv(agent) {
|
|
265
|
-
if (!agent.setupScript)
|
|
266
|
-
return {};
|
|
267
|
-
return runSetupScript(agent.setupScript);
|
|
268
|
-
}
|
|
269
|
-
function runSetupScript(setupScript) {
|
|
270
|
-
const args = setupScript.args || [];
|
|
271
|
-
const captureEnv = {
|
|
272
|
-
...process.env,
|
|
273
|
-
FRAIM_HUB_SETUP_SCRIPT_COMMAND: setupScript.command,
|
|
274
|
-
FRAIM_HUB_SETUP_SCRIPT_ARGS_JSON: JSON.stringify(args),
|
|
275
|
-
};
|
|
276
|
-
const invocation = setupScript.runner === 'powershell'
|
|
277
|
-
? ['powershell.exe', [
|
|
278
|
-
'-NoProfile',
|
|
279
|
-
'-ExecutionPolicy',
|
|
280
|
-
'Bypass',
|
|
281
|
-
'-Command',
|
|
282
|
-
[
|
|
283
|
-
'$ErrorActionPreference = "Stop";',
|
|
284
|
-
'$before = @{};',
|
|
285
|
-
'[Environment]::GetEnvironmentVariables("Process").GetEnumerator() | ForEach-Object { $before[$_.Key] = [string]$_.Value };',
|
|
286
|
-
'$scriptArgs = @();',
|
|
287
|
-
'if ($env:FRAIM_HUB_SETUP_SCRIPT_ARGS_JSON) { $scriptArgs = @(ConvertFrom-Json $env:FRAIM_HUB_SETUP_SCRIPT_ARGS_JSON); }',
|
|
288
|
-
'. $env:FRAIM_HUB_SETUP_SCRIPT_COMMAND @scriptArgs *> $null;',
|
|
289
|
-
'[Environment]::GetEnvironmentVariables("Process").GetEnumerator() | ForEach-Object {',
|
|
290
|
-
' $name = [string]$_.Key;',
|
|
291
|
-
' $value = [string]$_.Value;',
|
|
292
|
-
' if ($name -match "^[A-Za-z_][A-Za-z0-9_]*$" -and (!$before.ContainsKey($name) -or $before[$name] -ne $value)) { "$name=$value" }',
|
|
293
|
-
'}',
|
|
294
|
-
].join(' '),
|
|
295
|
-
]]
|
|
296
|
-
: ['bash', [
|
|
297
|
-
'-lc',
|
|
298
|
-
[
|
|
299
|
-
'set -e',
|
|
300
|
-
'before="$(mktemp)"',
|
|
301
|
-
'after="$(mktemp)"',
|
|
302
|
-
'env | sort > "$before"',
|
|
303
|
-
'source "$1" "${@:2}" >/dev/null',
|
|
304
|
-
'env | sort > "$after"',
|
|
305
|
-
'comm -13 "$before" "$after"',
|
|
306
|
-
'rm -f "$before" "$after"',
|
|
307
|
-
].join('; '),
|
|
308
|
-
'fraim-hub-setup',
|
|
309
|
-
setupScript.command,
|
|
310
|
-
...args,
|
|
311
|
-
]];
|
|
312
|
-
const result = (0, child_process_1.spawnSync)(invocation[0], invocation[1], {
|
|
313
|
-
windowsHide: true,
|
|
314
|
-
encoding: 'utf8',
|
|
315
|
-
timeout: setupScript.timeoutMs || 30_000,
|
|
316
|
-
env: captureEnv,
|
|
317
|
-
});
|
|
318
|
-
if (result.error || result.status !== 0) {
|
|
319
|
-
const detail = result.error?.message || `exit ${result.status}`;
|
|
320
|
-
throw new Error(`Configured agent setup script failed: ${detail}`);
|
|
321
|
-
}
|
|
322
|
-
return filterSafeEnv(parseEnvLines(result.stdout || ''));
|
|
323
|
-
}
|
|
324
|
-
function parseEnvLines(stdout) {
|
|
325
|
-
const env = {};
|
|
326
|
-
for (const line of stdout.split(/\r?\n/)) {
|
|
327
|
-
const idx = line.indexOf('=');
|
|
328
|
-
if (idx <= 0)
|
|
329
|
-
continue;
|
|
330
|
-
const key = line.slice(0, idx).trim();
|
|
331
|
-
const value = line.slice(idx + 1);
|
|
332
|
-
if (ENV_NAME_PATTERN.test(key))
|
|
333
|
-
env[key] = value;
|
|
334
|
-
}
|
|
335
|
-
return env;
|
|
336
|
-
}
|
|
337
|
-
function filterSafeEnv(env) {
|
|
338
|
-
const safe = {};
|
|
339
|
-
for (const [key, value] of Object.entries(env)) {
|
|
340
|
-
if (!ENV_NAME_PATTERN.test(key))
|
|
341
|
-
continue;
|
|
342
|
-
if (key.startsWith('FRAIM_HUB_SETUP_SCRIPT_'))
|
|
343
|
-
continue;
|
|
344
|
-
safe[key] = value;
|
|
345
|
-
}
|
|
346
|
-
return safe;
|
|
347
|
-
}
|
|
348
315
|
// Issue #1201: command parsing, secret detection, and launch decoration for
|
|
349
316
|
// the single-command configured-agent surface live in their own module
|
|
350
317
|
// (kept a cohesive, separately-testable unit rather than growing this file
|
|
351
|
-
// past the 500-line quality threshold)
|
|
318
|
+
// past the 500-line quality threshold) — re-exported here so existing
|
|
352
319
|
// callers importing from `./configured-agents` are unaffected.
|
|
353
|
-
var
|
|
354
|
-
Object.defineProperty(exports, "parseConfiguredCommand", { enumerable: true, get: function () { return
|
|
355
|
-
Object.defineProperty(exports, "commandContainsLiteralSecret", { enumerable: true, get: function () { return
|
|
356
|
-
Object.defineProperty(exports, "decorateHostPlanWithConfiguredAgent", { enumerable: true, get: function () { return
|
|
357
|
-
Object.defineProperty(exports, "describeConfiguredCommandInvocationProblem", { enumerable: true, get: function () { return
|
|
358
|
-
Object.defineProperty(exports, "probeCommandResolvesNonInteractively", { enumerable: true, get: function () { return configured_agent_command_2.probeCommandResolvesNonInteractively; } });
|
|
359
|
-
Object.defineProperty(exports, "__setCommandResolutionProbeForTests", { enumerable: true, get: function () { return configured_agent_command_2.__setCommandResolutionProbeForTests; } });
|
|
320
|
+
var configured_agent_command_3 = require("./configured-agent-command");
|
|
321
|
+
Object.defineProperty(exports, "parseConfiguredCommand", { enumerable: true, get: function () { return configured_agent_command_3.parseConfiguredCommand; } });
|
|
322
|
+
Object.defineProperty(exports, "commandContainsLiteralSecret", { enumerable: true, get: function () { return configured_agent_command_3.commandContainsLiteralSecret; } });
|
|
323
|
+
Object.defineProperty(exports, "decorateHostPlanWithConfiguredAgent", { enumerable: true, get: function () { return configured_agent_command_3.decorateHostPlanWithConfiguredAgent; } });
|
|
324
|
+
Object.defineProperty(exports, "describeConfiguredCommandInvocationProblem", { enumerable: true, get: function () { return configured_agent_command_3.describeConfiguredCommandInvocationProblem; } });
|