fix: persist and select device-pod profiles
This commit is contained in:
@@ -2216,6 +2216,7 @@ function deviceAgent71FreqManifest({ profile = "dev", source, registryPrefix, ca
|
||||
{ name: "HWLAB_IMAGE_TAG", value: bridgeImageTag },
|
||||
{ name: "DEVICE_ID", value: "71-freq" },
|
||||
{ name: "DEVICE_WORKSPACE_ROOT", value: "F:\\Work\\ConStart" },
|
||||
{ name: "DEVICE_POD_PROFILE_DIR", value: "/workspace/hwlab/.device-pod" },
|
||||
{ name: "HWLAB_CLOUD_API_URL", value: `http://hwlab-cloud-api.${namespace}.svc.cluster.local:6667` },
|
||||
{ name: "HWLAB_GATEWAY_SESSION_ID", value: "gws_d601_win_71_freq" },
|
||||
{ name: "HWLAB_GATEWAY_RESOURCE_ID", value: "res_d601_windows_host" },
|
||||
@@ -2225,9 +2226,15 @@ function deviceAgent71FreqManifest({ profile = "dev", source, registryPrefix, ca
|
||||
readinessProbe: { httpGet: { path: "/health", port: "http" }, initialDelaySeconds: 3, periodSeconds: 10 },
|
||||
livenessProbe: { httpGet: { path: "/health", port: "http" }, initialDelaySeconds: 10, periodSeconds: 20 },
|
||||
resources: { requests: { cpu: "10m", memory: "64Mi" }, limits: { cpu: "200m", memory: "256Mi" } },
|
||||
volumeMounts: [{ name: "script", mountPath: "/opt/device-agent", readOnly: true }]
|
||||
volumeMounts: [
|
||||
{ name: "script", mountPath: "/opt/device-agent", readOnly: true },
|
||||
{ name: "hwlab-code-agent-workspace", mountPath: "/workspace" }
|
||||
]
|
||||
}],
|
||||
volumes: [{ name: "script", configMap: { name: `${name}-script` } }]
|
||||
volumes: [
|
||||
{ name: "script", configMap: { name: `${name}-script` } },
|
||||
{ name: "hwlab-code-agent-workspace", persistentVolumeClaim: { claimName: "hwlab-code-agent-workspace" } }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,9 +28,19 @@ device pod, debug probe control, or I/O probe reads such as UART boot logs.
|
||||
`/app/skills/device-pod-cli/assets/device-host-cli.mjs`. It is bundled with
|
||||
this skill so a runner can send the exact tested host-side CLI to a new
|
||||
Windows hardware PC before the first device-pod operation.
|
||||
- Profiles are read from `.device-pod/<devicePodId>.json` in the current code
|
||||
agent workspace, or from `DEVICE_POD_PROFILE` when a task explicitly points to
|
||||
a profile file.
|
||||
- Profiles are read from `DEVICE_POD_PROFILE_DIR/<devicePodId>.json` when that
|
||||
directory is set, otherwise from `.device-pod/<devicePodId>.json` in the
|
||||
current code agent workspace; `DEVICE_POD_PROFILE` may point to one explicit
|
||||
profile file. G14 device-agent runners should keep shared profiles in the
|
||||
persistent `/workspace/hwlab/.device-pod` directory rather than only in
|
||||
ephemeral `/app/.device-pod`.
|
||||
- Multiple profiles may coexist in the same runner workspace. Use the selector
|
||||
prefix, such as `device-pod-71-freq:...` or `device-pod-71-00075-11:...`, as
|
||||
the source of truth for the active pod. `device-pod-cli` automatically passes
|
||||
that selected profile to Windows `device-host-cli` as `--profile
|
||||
.device-pod\\<devicePodId>.json --pod-id <devicePodId>`, so host-side commands
|
||||
must not fall back to the default `device-pod-71-freq` profile when another
|
||||
pod is selected.
|
||||
- The CLI calls HWLAB cloud-api `hardware.invoke.shell`, which dispatches to an
|
||||
outbound `hwlab-gateway` session and then to the user PC side
|
||||
`device-host-cli`. Do not call gateway, serial ports, Keil, pyOCD, DAPLink, or
|
||||
@@ -75,7 +85,7 @@ default to hide slow transfers. If upload/download becomes awkward again, fix
|
||||
|
||||
```text
|
||||
node /app/tools/tran.mjs <gatewaySessionId>:/d/Work/HWLAB-workspace upload /app/skills/device-pod-cli/assets/device-host-cli.mjs tools/device-host-cli.mjs
|
||||
node /app/tools/tran.mjs <gatewaySessionId>:/d/Work/HWLAB-workspace cmd -- node tools\\device-host-cli.mjs health
|
||||
node /app/tools/tran.mjs <gatewaySessionId>:/d/Work/HWLAB-workspace cmd -- node tools\\device-host-cli.mjs --profile .device-pod\\<devicePodId>.json --pod-id <devicePodId> health
|
||||
```
|
||||
|
||||
When only HWLAB gateway `hardware.invoke.shell` is available, send the asset
|
||||
@@ -83,7 +93,7 @@ bytes as bounded base64 chunks through cmd/PowerShell into
|
||||
`tools\device-host-cli.mjs`, then verify with:
|
||||
|
||||
```text
|
||||
node tools\device-host-cli.mjs health
|
||||
node tools\device-host-cli.mjs --profile .device-pod\<devicePodId>.json --pod-id <devicePodId> health
|
||||
```
|
||||
|
||||
After that, `device-pod-cli` should use the profile's `hostCli`, normally
|
||||
@@ -126,12 +136,23 @@ node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-freq:wo
|
||||
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-freq:debug-probe chip-id
|
||||
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-freq:debug-probe download start --approved --reason "DEV smoke"
|
||||
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-freq:debug-probe download status <jobId>
|
||||
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-freq:debug-probe download start --capture-uart uart/1 --capture-duration-ms 8000 --approved --reason "DEV boot log capture"
|
||||
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-freq:debug-probe launch-flash --approved --reason "manual flash-vector launch"
|
||||
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-freq:io-probe:/uart/1 ports
|
||||
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-freq:io-probe:/uart/1 read --duration-ms 5000
|
||||
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-freq:io-probe:/uart/1 read --port COM4 --baud-rate 921600 --duration-ms 5000
|
||||
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-freq:io-probe:/uart/1 read-after-launch-flash --approved --reason "boot log capture"
|
||||
```
|
||||
|
||||
Use `--port COMx` and `--baud-rate N` only as a short-lived UART discovery
|
||||
override. After the real port is proven, write the stable value back into the
|
||||
device-pod profile so later agents do not depend on a hidden command override.
|
||||
|
||||
For boot logs that only appear during reset/download, prefer
|
||||
`debug-probe download start --capture-uart uart/1 --capture-duration-ms N` so
|
||||
the host opens the UART before Keil starts flashing and stores the captured
|
||||
bytes in the download job status under `result.uartCapture`.
|
||||
|
||||
## Safety
|
||||
|
||||
- Mutating operations such as workspace patching, download, reset,
|
||||
|
||||
@@ -7,10 +7,42 @@ import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const VERSION = '0.1.0-mvp';
|
||||
const DEFAULT_POD_ID = process.env.DEVICE_POD_ID || 'device-pod-71-freq';
|
||||
const DEFAULT_POD_ID = 'device-pod-71-freq';
|
||||
const GLOBAL_CLI = parseGlobalCli(process.argv.slice(2));
|
||||
|
||||
function parseGlobalCli(argv) {
|
||||
const options = {};
|
||||
const args = [];
|
||||
for (let i = 0; i < argv.length; i += 1) {
|
||||
const item = argv[i];
|
||||
if (item === '--profile') {
|
||||
options.profile = argv[i + 1];
|
||||
i += 1;
|
||||
} else if (item.startsWith('--profile=')) {
|
||||
options.profile = item.slice('--profile='.length);
|
||||
} else if (item === '--pod-id') {
|
||||
options.podId = argv[i + 1];
|
||||
i += 1;
|
||||
} else if (item.startsWith('--pod-id=')) {
|
||||
options.podId = item.slice('--pod-id='.length);
|
||||
} else if (item === '--workspace') {
|
||||
options.workspace = argv[i + 1];
|
||||
i += 1;
|
||||
} else if (item.startsWith('--workspace=')) {
|
||||
options.workspace = item.slice('--workspace='.length);
|
||||
} else {
|
||||
args.push(item);
|
||||
}
|
||||
}
|
||||
return { options, args };
|
||||
}
|
||||
|
||||
function selectedPodId() {
|
||||
return GLOBAL_CLI.options.podId || process.env.DEVICE_POD_ID || DEFAULT_POD_ID;
|
||||
}
|
||||
|
||||
function workspaceRoot() {
|
||||
return path.resolve(process.env.DEVICE_POD_WORKSPACE || process.cwd());
|
||||
return path.resolve(GLOBAL_CLI.options.workspace || process.env.DEVICE_POD_WORKSPACE || process.cwd());
|
||||
}
|
||||
|
||||
function devicePodDir(root = workspaceRoot()) {
|
||||
@@ -49,15 +81,19 @@ function writeJson(file, value) {
|
||||
fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
|
||||
}
|
||||
|
||||
function profilePath(root = workspaceRoot(), podId = DEFAULT_POD_ID) {
|
||||
function profilePath(root = workspaceRoot(), podId = selectedPodId()) {
|
||||
return path.join(devicePodDir(root), `${podId}.json`);
|
||||
}
|
||||
|
||||
function loadProfile() {
|
||||
const root = workspaceRoot();
|
||||
const file = process.env.DEVICE_POD_PROFILE || profilePath(root);
|
||||
const requestedProfile = GLOBAL_CLI.options.profile || process.env.DEVICE_POD_PROFILE || '';
|
||||
const file = requestedProfile ? (path.isAbsolute(requestedProfile) ? requestedProfile : path.resolve(root, requestedProfile)) : profilePath(root);
|
||||
if (!fs.existsSync(file)) throw new Error(`device-pod profile not found: ${file}`);
|
||||
const profile = readJson(file);
|
||||
const actualPodId = profile.devicePodId || profile.podId;
|
||||
const requestedPodId = GLOBAL_CLI.options.podId || process.env.DEVICE_POD_ID || '';
|
||||
if (requestedPodId && actualPodId && actualPodId !== requestedPodId) throw new Error(`device-pod profile mismatch: requested ${requestedPodId}, profile ${actualPodId}`);
|
||||
profile.__profilePath = file;
|
||||
profile.__workspaceRoot = path.resolve(profile.workspaceRoot || root);
|
||||
return profile;
|
||||
@@ -138,6 +174,10 @@ function psQuote(value) {
|
||||
return `'${String(value).replace(/'/g, "''")}'`;
|
||||
}
|
||||
|
||||
function psArray(values) {
|
||||
return `@(${values.map(psQuote).join(', ')})`;
|
||||
}
|
||||
|
||||
function parseArgs(args) {
|
||||
const out = { _: [] };
|
||||
for (let i = 0; i < args.length; i += 1) {
|
||||
@@ -537,10 +577,59 @@ async function runKeilDownload(profile, payload = {}) {
|
||||
const logFile = path.join(outDir, `download_${safeTarget(target)}_${Date.now()}.log`);
|
||||
const args = ['-f', project, '-j0', '-sg', '-o', logFile];
|
||||
if (target) args.push('-t', target);
|
||||
const result = await runCapture(tools.uv4.path, args, { cwd: path.dirname(project), timeoutMs: Number(payload.timeoutMs || 300000) });
|
||||
const timeoutMs = Number(payload.timeoutMs || 300000);
|
||||
const result = payload['capture-uart'] || payload.captureUart
|
||||
? await runKeilDownloadWithUartCapture(profile, tools.uv4.path, args, path.dirname(project), payload)
|
||||
: await runCapture(tools.uv4.path, args, { cwd: path.dirname(project), timeoutMs });
|
||||
const logText = fs.existsSync(logFile) ? fs.readFileSync(logFile, 'utf8') : '';
|
||||
const success = result.exitCode === 0 && !keilLogHasErrors(logText);
|
||||
return { success, exitCode: result.exitCode, timedOut: result.timedOut, elapsedMs: result.elapsedMs, project, target, binding, logFile, logTail: shortTail(logText), stdout: shortTail(result.stdout), stderr: shortTail(result.stderr) };
|
||||
return { success, exitCode: result.exitCode, timedOut: result.timedOut, elapsedMs: result.elapsedMs, project, target, binding, logFile, logTail: shortTail(logText), stdout: shortTail(result.stdout), stderr: shortTail(result.stderr), uartCapture: result.uartCapture };
|
||||
}
|
||||
|
||||
async function runKeilDownloadWithUartCapture(profile, uv4Path, uv4Args, cwd, payload = {}) {
|
||||
const uart = resolveUart(profile, payload['capture-uart'] || payload.captureUart, payload);
|
||||
const captureDurationMs = Number(payload['capture-duration-ms'] || payload.captureDurationMs || payload['duration-ms'] || payload.durationMs || 8000);
|
||||
const timeoutMs = Number(payload.timeoutMs || 300000);
|
||||
const logDir = path.join(stateRoot(profile.__workspaceRoot), 'serial');
|
||||
ensureDir(logDir);
|
||||
const serialLogFile = path.join(logDir, `${uart.id.replace(/\//g, '_')}.jsonl`);
|
||||
const script = [
|
||||
'$ErrorActionPreference = \'Stop\';',
|
||||
`$uv4 = ${psQuote(uv4Path)};`,
|
||||
`$uv4Args = ${psArray(uv4Args)};`,
|
||||
`$cwd = ${psQuote(cwd)};`,
|
||||
`$portName = ${psQuote(uart.port)};`,
|
||||
`$baud = ${Number(uart.baudRate || 115200)};`,
|
||||
`$captureDurationMs = ${captureDurationMs};`,
|
||||
'$port = [System.IO.Ports.SerialPort]::new($portName, $baud, [System.IO.Ports.Parity]::None, 8, [System.IO.Ports.StopBits]::One);',
|
||||
'$port.Encoding = [System.Text.UTF8Encoding]::new($false);',
|
||||
'$port.ReadTimeout = 200;',
|
||||
'$timer = [System.Diagnostics.Stopwatch]::StartNew();',
|
||||
'try {',
|
||||
' $port.Open();',
|
||||
' $port.DiscardInBuffer();',
|
||||
' Push-Location -LiteralPath $cwd;',
|
||||
' try { $uv4Output = & $uv4 @uv4Args 2>&1; $uv4ExitCode = $LASTEXITCODE } finally { Pop-Location }',
|
||||
' Start-Sleep -Milliseconds $captureDurationMs;',
|
||||
' $text = $port.ReadExisting();',
|
||||
' $bytes = [System.Text.Encoding]::UTF8.GetBytes($text);',
|
||||
' $timer.Stop();',
|
||||
' [pscustomobject]@{ exitCode = $uv4ExitCode; timedOut = $false; elapsedMs = [int]$timer.ElapsedMilliseconds; stdout = ($uv4Output | Out-String); stderr = ""; contentB64 = [Convert]::ToBase64String($bytes) } | ConvertTo-Json -Depth 5 -Compress | Write-Output;',
|
||||
'} finally { if ($port.IsOpen) { $port.Close() }; $port.Dispose() }',
|
||||
].join(' ');
|
||||
const psResult = await runPowerShell(script, Math.max(timeoutMs + captureDurationMs + 10000, 15000));
|
||||
if (psResult.exitCode !== 0) throw new Error(`keil download UART capture failed on ${uart.port}@${uart.baudRate}: ${shortTail(psResult.stderr || psResult.stdout)}`);
|
||||
const payloadJson = JSON.parse(psResult.stdout.trim());
|
||||
const content = payloadJson.contentB64 ? Buffer.from(payloadJson.contentB64, 'base64').toString('utf8') : '';
|
||||
fs.appendFileSync(serialLogFile, `${JSON.stringify({ ts: nowIso(), direction: 'download-capture-uart', uartId: uart.id, port: uart.port, baudRate: uart.baudRate, content })}\n`, 'utf8');
|
||||
return {
|
||||
exitCode: Number(payloadJson.exitCode || 0),
|
||||
timedOut: Boolean(payloadJson.timedOut),
|
||||
elapsedMs: Number(payloadJson.elapsedMs || 0),
|
||||
stdout: payloadJson.stdout || '',
|
||||
stderr: payloadJson.stderr || '',
|
||||
uartCapture: { uart, durationMs: captureDurationMs, content, bytes: Buffer.byteLength(content), logFile: serialLogFile },
|
||||
};
|
||||
}
|
||||
|
||||
async function pyocdArgs(profile, commandArgs) {
|
||||
@@ -658,16 +747,24 @@ async function serialPorts() {
|
||||
ok('io-probe.ports', { configured: profile.ioInterface?.uart || [], ports, stderr: result.stderr, exitCode: result.exitCode });
|
||||
}
|
||||
|
||||
function resolveUart(profile, uartId) {
|
||||
function resolveUart(profile, uartId, options = {}) {
|
||||
const normalized = String(uartId || 'uart/1').replace(/^\/+/, '');
|
||||
const item = (profile.ioInterface?.uart || []).find((entry) => entry.id === normalized);
|
||||
if (!item) throw new Error(`uart profile not found: ${normalized}`);
|
||||
return item;
|
||||
const resolved = { ...item };
|
||||
if (options.port) resolved.port = String(options.port);
|
||||
const baudRate = options['baud-rate'] || options.baudRate;
|
||||
if (baudRate !== undefined) {
|
||||
const parsed = Number(baudRate);
|
||||
if (!Number.isFinite(parsed) || parsed <= 0) throw new Error(`invalid UART baud rate: ${baudRate}`);
|
||||
resolved.baudRate = parsed;
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
async function serialRead(uartId, options) {
|
||||
const profile = loadProfile();
|
||||
const uart = resolveUart(profile, uartId);
|
||||
const uart = resolveUart(profile, uartId, options);
|
||||
const durationMs = Number(options['duration-ms'] || options.durationMs || 1000);
|
||||
const script = [
|
||||
'$ErrorActionPreference = \'Stop\';',
|
||||
@@ -685,13 +782,14 @@ async function serialRead(uartId, options) {
|
||||
const logDir = path.join(stateRoot(profile.__workspaceRoot), 'serial');
|
||||
ensureDir(logDir);
|
||||
const logFile = path.join(logDir, `${uart.id.replace(/\//g, '_')}.jsonl`);
|
||||
fs.appendFileSync(logFile, `${JSON.stringify({ ts: nowIso(), direction: 'read', uartId: uart.id, port: uart.port, baudRate: uart.baudRate, content })}\n`, 'utf8');
|
||||
fs.appendFileSync(logFile, `${JSON.stringify({ ts: nowIso(), direction: 'read', uartId: uart.id, port: uart.port, baudRate: uart.baudRate, exitCode: result.exitCode, content, stderr: result.stderr })}\n`, 'utf8');
|
||||
if (result.exitCode !== 0) throw new Error(`uart read failed on ${uart.port}@${uart.baudRate}: ${shortTail(result.stderr || result.stdout)}`);
|
||||
ok('io-probe.uart.read', { uart, durationMs, exitCode: result.exitCode, content, bytes: Buffer.byteLength(content), logFile, stderr: result.stderr });
|
||||
}
|
||||
|
||||
async function serialReadAfterLaunchFlash(uartId, options) {
|
||||
const profile = loadProfile();
|
||||
const uart = resolveUart(profile, uartId);
|
||||
const uart = resolveUart(profile, uartId, options);
|
||||
const tools = await toolInfo(profile);
|
||||
if (!tools.pyocd.found) throw new Error('pyOCD executable not found');
|
||||
const debug = profile.debugInterface || {};
|
||||
@@ -758,7 +856,7 @@ async function serialReadAfterLaunchFlash(uartId, options) {
|
||||
|
||||
async function serialWrite(uartId, options) {
|
||||
const profile = loadProfile();
|
||||
const uart = resolveUart(profile, uartId);
|
||||
const uart = resolveUart(profile, uartId, options);
|
||||
const message = options.hex ? Buffer.from(String(options._[0] || ''), 'hex').toString('binary') : String(options._[0] || '');
|
||||
const encoded = Buffer.from(message, options.hex ? 'binary' : 'utf8').toString('base64');
|
||||
const script = [
|
||||
@@ -771,12 +869,15 @@ async function serialWrite(uartId, options) {
|
||||
'try { $port.Open(); $port.BaseStream.Write($bytes, 0, $bytes.Length); $port.BaseStream.Flush(); [Console]::Out.Write($bytes.Length) } finally { if ($port.IsOpen) { $port.Close() }; $port.Dispose() }',
|
||||
].join(' ');
|
||||
const result = await runPowerShell(script, 10000);
|
||||
if (result.exitCode !== 0) throw new Error(`uart write failed on ${uart.port}@${uart.baudRate}: ${shortTail(result.stderr || result.stdout)}`);
|
||||
ok('io-probe.uart.write', { uart, exitCode: result.exitCode, bytesWritten: Number(result.stdout.trim() || 0), stderr: result.stderr });
|
||||
}
|
||||
|
||||
function usage() {
|
||||
ok('help', {
|
||||
usage: [
|
||||
'node tools/device-host-cli.mjs --profile .device-pod/<devicePodId>.json health',
|
||||
'node tools/device-host-cli.mjs --pod-id <devicePodId> health',
|
||||
'node tools/device-host-cli.mjs health',
|
||||
'node tools/device-host-cli.mjs workspace ls [path]',
|
||||
'node tools/device-host-cli.mjs workspace cat <path> [--limit N]',
|
||||
@@ -790,7 +891,7 @@ function usage() {
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const [group, command, ...rest] = process.argv.slice(2);
|
||||
const [group, command, ...rest] = GLOBAL_CLI.args;
|
||||
if (!group || group === 'help' || group === '--help') return usage();
|
||||
if (group === '__job') return await runJob(command);
|
||||
if (group === 'health') return await health();
|
||||
|
||||
@@ -6,9 +6,16 @@ import https from 'node:https';
|
||||
import path from 'node:path';
|
||||
|
||||
const VERSION = '0.1.0-mvp';
|
||||
const PROFILE_DIR = process.env.DEVICE_POD_PROFILE_DIR || path.resolve(process.cwd(), '.device-pod');
|
||||
const DEFAULT_TIMEOUT_MS = 30000;
|
||||
|
||||
function defaultProfileDir() {
|
||||
if (process.env.DEVICE_POD_PROFILE_DIR) return process.env.DEVICE_POD_PROFILE_DIR;
|
||||
const candidates = ['/workspace/hwlab/.device-pod', path.resolve(process.cwd(), '.device-pod'), '/app/.device-pod'];
|
||||
return candidates.find((dir) => fs.existsSync(dir)) || path.resolve(process.cwd(), '.device-pod');
|
||||
}
|
||||
|
||||
const PROFILE_DIR = defaultProfileDir();
|
||||
|
||||
function nowIso() { return new Date().toISOString(); }
|
||||
function stripSlash(value) { return String(value || '').replace(/\/+$/u, ''); }
|
||||
function sha256(text) { return createHash('sha256').update(text).digest('hex'); }
|
||||
@@ -93,6 +100,10 @@ function cmdQuote(value) {
|
||||
return `"${text.replace(/"/g, '\\"')}"`;
|
||||
}
|
||||
|
||||
function hostProfilePath(profile) {
|
||||
return profile.hostProfilePath || `.device-pod\\${profile.podId}.json`;
|
||||
}
|
||||
|
||||
const CONTROL_OPTION_KEYS = new Set([
|
||||
'approved',
|
||||
'reason',
|
||||
@@ -181,7 +192,8 @@ async function invoke(profile, command, parsed) {
|
||||
});
|
||||
const dispatch = response.body?.result?.dispatch || response.body?.result || {};
|
||||
const hostJson = parseMaybeJson(dispatch.stdout);
|
||||
const status = response.body?.error || dispatch.exitCode !== 0 || dispatch.status === 'failed' ? 'failed' : 'succeeded';
|
||||
const hostFailed = hostJson && hostJson.ok === false;
|
||||
const status = response.body?.error || dispatch.exitCode !== 0 || dispatch.status === 'failed' || hostFailed ? 'failed' : 'succeeded';
|
||||
return { requestId, operationId, traceId, httpStatus: response.status, status, dispatch, hostJson, gatewayBody: response.body };
|
||||
}
|
||||
|
||||
@@ -236,7 +248,8 @@ async function main() {
|
||||
const profile = readProfile(selector.podId || parsed.podId);
|
||||
if (selector.kind === 'health') return print({ ok: true, action: 'health', status: 'succeeded', ...profileSummary(profile), route: routeSummary(profile) });
|
||||
const built = await buildHostCommand(selector, parsed);
|
||||
const command = `${profile.hostCli} ${built.commandArgs.map(cmdQuote).join(' ')}`;
|
||||
const hostArgs = ['--profile', hostProfilePath(profile), '--pod-id', profile.podId, ...built.commandArgs];
|
||||
const command = `${profile.hostCli} ${hostArgs.map(cmdQuote).join(' ')}`;
|
||||
if (parsed.dryRun) return print({ ok: true, action: 'device-pod.plan', status: 'planned', selector, operation: built.operation, mutating: built.mutating, command, ...profileSummary(profile), route: routeSummary(profile) });
|
||||
const result = await invoke(profile, command, parsed);
|
||||
const ok = result.status === 'succeeded';
|
||||
|
||||
Reference in New Issue
Block a user