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.
@@ -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.__setCommandResolutionProbeForTests = exports.probeCommandResolvesNonInteractively = exports.describeConfiguredCommandInvocationProblem = exports.decorateHostPlanWithConfiguredAgent = exports.commandContainsLiteralSecret = exports.parseConfiguredCommand = exports.AiHubConfiguredAgentStore = void 0;
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 — callers that omit it get the current platform's
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, env = process.env) {
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, configured_agent_command_1.parseConfiguredCommand)(agent.command.runner, agent.command.command);
184
- const problem = (0, configured_agent_command_1.describeConfiguredCommandInvocationProblem)(parsed, agent.baseHostId, agent.label);
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(error instanceof Error ? error.message : 'Configured agent command could not be parsed.');
227
+ reasons.push('Configured agent command contains unsupported syntax. ' + configured_agent_command_1.CONFIGURED_MIGRATION);
196
228
  }
197
229
  }
198
230
  }
199
- else if (agent.setupScript) {
231
+ if (agent.setupScript) {
200
232
  const command = agent.setupScript.command;
201
- const isPathLike = /[\\/]/.test(command) || /^[A-Za-z]:[\\/]/.test(command);
202
- if (isPathLike && !fs_1.default.existsSync(command)) {
203
- reasons.push(`Setup script is missing: ${path_1.default.basename(command)}`);
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, env);
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
- resolveConfiguredAgentEnv(agent);
221
- return check;
222
- }
223
- catch (error) {
224
- return {
225
- ...check,
226
- available: false,
227
- reasons: [
228
- ...check.reasons,
229
- error instanceof Error ? error.message : 'Configured agent setup script failed.',
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) — re-exported here so existing
318
+ // past the 500-line quality threshold) — re-exported here so existing
352
319
  // callers importing from `./configured-agents` are unaffected.
353
- var configured_agent_command_2 = require("./configured-agent-command");
354
- Object.defineProperty(exports, "parseConfiguredCommand", { enumerable: true, get: function () { return configured_agent_command_2.parseConfiguredCommand; } });
355
- Object.defineProperty(exports, "commandContainsLiteralSecret", { enumerable: true, get: function () { return configured_agent_command_2.commandContainsLiteralSecret; } });
356
- Object.defineProperty(exports, "decorateHostPlanWithConfiguredAgent", { enumerable: true, get: function () { return configured_agent_command_2.decorateHostPlanWithConfiguredAgent; } });
357
- Object.defineProperty(exports, "describeConfiguredCommandInvocationProblem", { enumerable: true, get: function () { return configured_agent_command_2.describeConfiguredCommandInvocationProblem; } });
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; } });