@openrig/cli 0.4.3 → 0.4.4
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/daemon/assets/guidance/openrig-start.md +6 -0
- package/daemon/assets/plugins/openrig-core/skills/mission-slice-sop/SKILL.md +49 -21
- package/daemon/assets/plugins/openrig-core/skills/openrig-user/SKILL.md +12 -12
- package/daemon/dist/build-info.d.ts +12 -0
- package/daemon/dist/build-info.d.ts.map +1 -0
- package/daemon/dist/build-info.js +11 -0
- package/daemon/dist/build-info.js.map +1 -0
- package/daemon/dist/db/migrations/048_queue_item_evidence_ref.d.ts +18 -0
- package/daemon/dist/db/migrations/048_queue_item_evidence_ref.d.ts.map +1 -0
- package/daemon/dist/db/migrations/048_queue_item_evidence_ref.js +22 -0
- package/daemon/dist/db/migrations/048_queue_item_evidence_ref.js.map +1 -0
- package/daemon/dist/domain/feed/attention-aggregator.d.ts +31 -0
- package/daemon/dist/domain/feed/attention-aggregator.d.ts.map +1 -0
- package/daemon/dist/domain/feed/attention-aggregator.js +119 -0
- package/daemon/dist/domain/feed/attention-aggregator.js.map +1 -0
- package/daemon/dist/domain/files/file-write-service.d.ts +11 -2
- package/daemon/dist/domain/files/file-write-service.d.ts.map +1 -1
- package/daemon/dist/domain/files/file-write-service.js +77 -0
- package/daemon/dist/domain/files/file-write-service.js.map +1 -1
- package/daemon/dist/domain/hosts/fanout-contract.d.ts +33 -0
- package/daemon/dist/domain/hosts/fanout-contract.d.ts.map +1 -0
- package/daemon/dist/domain/hosts/fanout-contract.js +23 -0
- package/daemon/dist/domain/hosts/fanout-contract.js.map +1 -0
- package/daemon/dist/domain/hosts/hosts-registry-reader.d.ts +49 -0
- package/daemon/dist/domain/hosts/hosts-registry-reader.d.ts.map +1 -0
- package/daemon/dist/domain/hosts/hosts-registry-reader.js +165 -0
- package/daemon/dist/domain/hosts/hosts-registry-reader.js.map +1 -0
- package/daemon/dist/domain/hosts/remote-daemon-http.d.ts +35 -0
- package/daemon/dist/domain/hosts/remote-daemon-http.d.ts.map +1 -0
- package/daemon/dist/domain/hosts/remote-daemon-http.js +105 -0
- package/daemon/dist/domain/hosts/remote-daemon-http.js.map +1 -0
- package/daemon/dist/domain/human-route-enforcer.d.ts +71 -0
- package/daemon/dist/domain/human-route-enforcer.d.ts.map +1 -0
- package/daemon/dist/domain/human-route-enforcer.js +80 -0
- package/daemon/dist/domain/human-route-enforcer.js.map +1 -0
- package/daemon/dist/domain/mission-control/audit-browse.d.ts +8 -0
- package/daemon/dist/domain/mission-control/audit-browse.d.ts.map +1 -1
- package/daemon/dist/domain/mission-control/audit-browse.js +19 -0
- package/daemon/dist/domain/mission-control/audit-browse.js.map +1 -1
- package/daemon/dist/domain/mission-control/mission-control-action-log.d.ts +1 -1
- package/daemon/dist/domain/mission-control/mission-control-action-log.d.ts.map +1 -1
- package/daemon/dist/domain/mission-control/mission-control-action-log.js +4 -0
- package/daemon/dist/domain/mission-control/mission-control-action-log.js.map +1 -1
- package/daemon/dist/domain/mission-control/mission-control-fleet-cli-capability.js +1 -1
- package/daemon/dist/domain/mission-control/mission-control-fleet-cli-capability.js.map +1 -1
- package/daemon/dist/domain/mission-control/mission-control-read-layer.js +1 -1
- package/daemon/dist/domain/mission-control/mission-control-read-layer.js.map +1 -1
- package/daemon/dist/domain/mission-control/mission-control-write-contract.d.ts +28 -0
- package/daemon/dist/domain/mission-control/mission-control-write-contract.d.ts.map +1 -1
- package/daemon/dist/domain/mission-control/mission-control-write-contract.js +125 -0
- package/daemon/dist/domain/mission-control/mission-control-write-contract.js.map +1 -1
- package/daemon/dist/domain/ps-projection.d.ts +28 -1
- package/daemon/dist/domain/ps-projection.d.ts.map +1 -1
- package/daemon/dist/domain/ps-projection.js +39 -0
- package/daemon/dist/domain/ps-projection.js.map +1 -1
- package/daemon/dist/domain/queue-repository.d.ts +25 -1
- package/daemon/dist/domain/queue-repository.d.ts.map +1 -1
- package/daemon/dist/domain/queue-repository.js +111 -7
- package/daemon/dist/domain/queue-repository.js.map +1 -1
- package/daemon/dist/domain/review/brief-spine.d.ts +19 -0
- package/daemon/dist/domain/review/brief-spine.d.ts.map +1 -0
- package/daemon/dist/domain/review/brief-spine.js +86 -0
- package/daemon/dist/domain/review/brief-spine.js.map +1 -0
- package/daemon/dist/domain/review/compose.d.ts +228 -0
- package/daemon/dist/domain/review/compose.d.ts.map +1 -0
- package/daemon/dist/domain/review/compose.js +773 -0
- package/daemon/dist/domain/review/compose.js.map +1 -0
- package/daemon/dist/domain/review/freeze.d.ts +31 -0
- package/daemon/dist/domain/review/freeze.d.ts.map +1 -0
- package/daemon/dist/domain/review/freeze.js +257 -0
- package/daemon/dist/domain/review/freeze.js.map +1 -0
- package/daemon/dist/domain/review/gather.d.ts +100 -0
- package/daemon/dist/domain/review/gather.d.ts.map +1 -0
- package/daemon/dist/domain/review/gather.js +676 -0
- package/daemon/dist/domain/review/gather.js.map +1 -0
- package/daemon/dist/domain/review/types.d.ts +258 -0
- package/daemon/dist/domain/review/types.d.ts.map +1 -0
- package/daemon/dist/domain/review/types.js +35 -0
- package/daemon/dist/domain/review/types.js.map +1 -0
- package/daemon/dist/domain/scope/scope-approve.d.ts +64 -0
- package/daemon/dist/domain/scope/scope-approve.d.ts.map +1 -0
- package/daemon/dist/domain/scope/scope-approve.js +153 -0
- package/daemon/dist/domain/scope/scope-approve.js.map +1 -0
- package/daemon/dist/domain/scope/scope-audit.d.ts +9 -1
- package/daemon/dist/domain/scope/scope-audit.d.ts.map +1 -1
- package/daemon/dist/domain/scope/scope-audit.js +173 -1
- package/daemon/dist/domain/scope/scope-audit.js.map +1 -1
- package/daemon/dist/domain/topology/multi-rig-launcher.d.ts +49 -0
- package/daemon/dist/domain/topology/multi-rig-launcher.d.ts.map +1 -0
- package/daemon/dist/domain/topology/multi-rig-launcher.js +132 -0
- package/daemon/dist/domain/topology/multi-rig-launcher.js.map +1 -0
- package/daemon/dist/domain/topology/remote-up-leaf.d.ts +29 -0
- package/daemon/dist/domain/topology/remote-up-leaf.d.ts.map +1 -0
- package/daemon/dist/domain/topology/remote-up-leaf.js +60 -0
- package/daemon/dist/domain/topology/remote-up-leaf.js.map +1 -0
- package/daemon/dist/domain/topology/topology-manifest.d.ts +34 -0
- package/daemon/dist/domain/topology/topology-manifest.d.ts.map +1 -0
- package/daemon/dist/domain/topology/topology-manifest.js +195 -0
- package/daemon/dist/domain/topology/topology-manifest.js.map +1 -0
- package/daemon/dist/domain/types.d.ts +5 -0
- package/daemon/dist/domain/types.d.ts.map +1 -1
- package/daemon/dist/domain/types.js.map +1 -1
- package/daemon/dist/domain/up-command-router.d.ts +1 -1
- package/daemon/dist/domain/up-command-router.d.ts.map +1 -1
- package/daemon/dist/domain/up-command-router.js +34 -2
- package/daemon/dist/domain/up-command-router.js.map +1 -1
- package/daemon/dist/domain/user-settings/settings-store.d.ts +17 -0
- package/daemon/dist/domain/user-settings/settings-store.d.ts.map +1 -1
- package/daemon/dist/domain/user-settings/settings-store.js +109 -0
- package/daemon/dist/domain/user-settings/settings-store.js.map +1 -1
- package/daemon/dist/domain/workspace/default-workspace-scaffold.d.ts.map +1 -1
- package/daemon/dist/domain/workspace/default-workspace-scaffold.js +51 -6
- package/daemon/dist/domain/workspace/default-workspace-scaffold.js.map +1 -1
- package/daemon/dist/domain/workspace/workspace-doctor.d.ts +11 -0
- package/daemon/dist/domain/workspace/workspace-doctor.d.ts.map +1 -1
- package/daemon/dist/domain/workspace/workspace-doctor.js +82 -0
- package/daemon/dist/domain/workspace/workspace-doctor.js.map +1 -1
- package/daemon/dist/routes/config.d.ts.map +1 -1
- package/daemon/dist/routes/config.js +15 -6
- package/daemon/dist/routes/config.js.map +1 -1
- package/daemon/dist/routes/files.d.ts.map +1 -1
- package/daemon/dist/routes/files.js +45 -1
- package/daemon/dist/routes/files.js.map +1 -1
- package/daemon/dist/routes/mission-control.d.ts.map +1 -1
- package/daemon/dist/routes/mission-control.js +67 -2
- package/daemon/dist/routes/mission-control.js.map +1 -1
- package/daemon/dist/routes/queue.d.ts +8 -2
- package/daemon/dist/routes/queue.d.ts.map +1 -1
- package/daemon/dist/routes/queue.js +44 -4
- package/daemon/dist/routes/queue.js.map +1 -1
- package/daemon/dist/routes/review.d.ts +3 -0
- package/daemon/dist/routes/review.d.ts.map +1 -0
- package/daemon/dist/routes/review.js +169 -0
- package/daemon/dist/routes/review.js.map +1 -0
- package/daemon/dist/routes/scope-approve.d.ts +3 -0
- package/daemon/dist/routes/scope-approve.d.ts.map +1 -0
- package/daemon/dist/routes/scope-approve.js +55 -0
- package/daemon/dist/routes/scope-approve.js.map +1 -0
- package/daemon/dist/routes/scope-audit.d.ts.map +1 -1
- package/daemon/dist/routes/scope-audit.js +28 -1
- package/daemon/dist/routes/scope-audit.js.map +1 -1
- package/daemon/dist/routes/slices.d.ts.map +1 -1
- package/daemon/dist/routes/slices.js +49 -9
- package/daemon/dist/routes/slices.js.map +1 -1
- package/daemon/dist/routes/terminal-ws.d.ts.map +1 -1
- package/daemon/dist/routes/terminal-ws.js +58 -21
- package/daemon/dist/routes/terminal-ws.js.map +1 -1
- package/daemon/dist/routes/up.d.ts.map +1 -1
- package/daemon/dist/routes/up.js +84 -0
- package/daemon/dist/routes/up.js.map +1 -1
- package/daemon/dist/routes/workspace.js +2 -2
- package/daemon/dist/server.d.ts +1 -0
- package/daemon/dist/server.d.ts.map +1 -1
- package/daemon/dist/server.js +14 -1
- package/daemon/dist/server.js.map +1 -1
- package/daemon/dist/startup.d.ts.map +1 -1
- package/daemon/dist/startup.js +18 -3
- package/daemon/dist/startup.js.map +1 -1
- package/daemon/docs/reference/agent-startup-guide.md +1 -1
- package/daemon/docs/reference/product-factory-vps-runbook.md +132 -0
- package/daemon/docs/reference/sdlc-conventions.md +187 -0
- package/daemon/specs/agents/conveyor/builder/agent.yaml +1 -1
- package/daemon/specs/agents/conveyor/builder/guidance/role.md +1 -0
- package/daemon/specs/agents/conveyor/lead/agent.yaml +1 -1
- package/daemon/specs/agents/conveyor/lead/guidance/role.md +1 -0
- package/daemon/specs/agents/conveyor/planner/agent.yaml +1 -1
- package/daemon/specs/agents/conveyor/planner/guidance/role.md +1 -0
- package/daemon/specs/agents/conveyor/reviewer/agent.yaml +1 -1
- package/daemon/specs/agents/conveyor/reviewer/guidance/role.md +1 -0
- package/daemon/specs/agents/design/product-designer/agent.yaml +1 -1
- package/daemon/specs/agents/design/product-designer/guidance/role.md +1 -0
- package/daemon/specs/agents/development/implementer/agent.yaml +1 -1
- package/daemon/specs/agents/development/implementer/guidance/role.md +1 -0
- package/daemon/specs/agents/development/qa/agent.yaml +1 -1
- package/daemon/specs/agents/development/qa/guidance/role.md +1 -0
- package/daemon/specs/agents/orchestration/orchestrator/agent.yaml +1 -1
- package/daemon/specs/agents/orchestration/orchestrator/guidance/role.md +1 -0
- package/daemon/specs/agents/product-management/pm/agent.yaml +1 -0
- package/daemon/specs/agents/product-management/pm/guidance/role.md +13 -0
- package/daemon/specs/agents/review/independent-reviewer/agent.yaml +1 -1
- package/daemon/specs/agents/review/independent-reviewer/guidance/role.md +1 -0
- package/daemon/specs/agents/shared/agent.yaml +2 -0
- package/daemon/specs/agents/shared/skills/core/mission-slice-sop/SKILL.md +115 -0
- package/daemon/specs/agents/shared/skills/core/openrig-operator/SKILL.md +4 -4
- package/daemon/specs/agents/shared/skills/core/openrig-user/SKILL.md +69 -23
- package/daemon/specs/agents/shared/skills/pods/orchestration-team/SKILL.md +2 -2
- package/daemon/specs/rigs/focused/pm-team/CULTURE.md +4 -0
- package/daemon/specs/rigs/launch/kernel/agents/operator/agent/guidance/role.md +1 -1
- package/daemon/specs/rigs/launch/kernel/agents/operator/agent/startup/context.md +1 -1
- package/daemon/specs/rigs/preview/product-team/CULTURE.md +4 -0
- package/dist/build-info.d.ts +8 -0
- package/dist/build-info.d.ts.map +1 -0
- package/dist/build-info.js +11 -0
- package/dist/build-info.js.map +1 -0
- package/dist/commands/config-init-workspace.d.ts.map +1 -1
- package/dist/commands/config-init-workspace.js +20 -4
- package/dist/commands/config-init-workspace.js.map +1 -1
- package/dist/commands/doctor.js +2 -2
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/expand.js +1 -1
- package/dist/commands/expand.js.map +1 -1
- package/dist/commands/file.d.ts +7 -0
- package/dist/commands/file.d.ts.map +1 -0
- package/dist/commands/file.js +77 -0
- package/dist/commands/file.js.map +1 -0
- package/dist/commands/host.d.ts +34 -0
- package/dist/commands/host.d.ts.map +1 -0
- package/dist/commands/host.js +448 -0
- package/dist/commands/host.js.map +1 -0
- package/dist/commands/proof.d.ts +54 -0
- package/dist/commands/proof.d.ts.map +1 -0
- package/dist/commands/proof.js +311 -0
- package/dist/commands/proof.js.map +1 -0
- package/dist/commands/ps.d.ts +41 -7
- package/dist/commands/ps.d.ts.map +1 -1
- package/dist/commands/ps.js +343 -131
- package/dist/commands/ps.js.map +1 -1
- package/dist/commands/queue.d.ts.map +1 -1
- package/dist/commands/queue.js +76 -0
- package/dist/commands/queue.js.map +1 -1
- package/dist/commands/scope.d.ts.map +1 -1
- package/dist/commands/scope.js +140 -3
- package/dist/commands/scope.js.map +1 -1
- package/dist/commands/send.js +13 -4
- package/dist/commands/send.js.map +1 -1
- package/dist/commands/up.d.ts +2 -0
- package/dist/commands/up.d.ts.map +1 -1
- package/dist/commands/up.js +69 -2
- package/dist/commands/up.js.map +1 -1
- package/dist/commands/workspace.js +6 -6
- package/dist/commands/workspace.js.map +1 -1
- package/dist/config-store.d.ts +4 -0
- package/dist/config-store.d.ts.map +1 -1
- package/dist/config-store.js +68 -0
- package/dist/config-store.js.map +1 -1
- package/dist/cross-host-executor.d.ts +1 -0
- package/dist/cross-host-executor.d.ts.map +1 -1
- package/dist/cross-host-executor.js +4 -1
- package/dist/cross-host-executor.js.map +1 -1
- package/dist/daemon-lifecycle.d.ts.map +1 -1
- package/dist/daemon-lifecycle.js +4 -1
- package/dist/daemon-lifecycle.js.map +1 -1
- package/dist/host-registry.d.ts +9 -0
- package/dist/host-registry.d.ts.map +1 -1
- package/dist/host-registry.js +32 -2
- package/dist/host-registry.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/file-transfer.d.ts +89 -0
- package/dist/lib/file-transfer.d.ts.map +1 -0
- package/dist/lib/file-transfer.js +300 -0
- package/dist/lib/file-transfer.js.map +1 -0
- package/dist/lib/hosts/fanout-contract.d.ts +33 -0
- package/dist/lib/hosts/fanout-contract.d.ts.map +1 -0
- package/dist/lib/hosts/fanout-contract.js +23 -0
- package/dist/lib/hosts/fanout-contract.js.map +1 -0
- package/dist/lib/scope/scope-audit.d.ts +9 -1
- package/dist/lib/scope/scope-audit.d.ts.map +1 -1
- package/dist/lib/scope/scope-audit.js +173 -1
- package/dist/lib/scope/scope-audit.js.map +1 -1
- package/dist/lib/scope/templates.d.ts +1 -0
- package/dist/lib/scope/templates.d.ts.map +1 -1
- package/dist/lib/scope/templates.js +9 -0
- package/dist/lib/scope/templates.js.map +1 -1
- package/dist/lib/scope-templates/backlog-deprecation.md +16 -0
- package/dist/lib/scope-templates/backlog-tech-debt.md +16 -0
- package/dist/lib/scope-templates/bug-fix.md +16 -0
- package/dist/lib/scope-templates/implementation-prd.md +33 -0
- package/dist/lib/scope-templates/mission-placeholder.md +1 -1
- package/dist/lib/scope-templates/mission-progress.md +1 -1
- package/dist/lib/scope-templates/mission-release.md +1 -1
- package/dist/lib/scope-templates/placeholder.md +11 -7
- package/dist/lib/scope-templates/proof.md +5 -1
- package/dist/lib/scope-templates/release-feature.md +14 -6
- package/dist/lib/scope-templates/research.md +16 -0
- package/dist/lib/scope-templates/slice-progress.md +1 -1
- package/dist/version.d.ts +5 -0
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +11 -1
- package/dist/version.js.map +1 -1
- package/package.json +1 -1
- package/ui/dist/assets/index-D69ZhNIr.js +598 -0
- package/ui/dist/assets/index-DJYun-8d.css +32 -0
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/index-DYZniQcc.js +0 -597
- package/ui/dist/assets/index-trcb4Yf_.css +0 -32
package/dist/commands/ps.js
CHANGED
|
@@ -55,6 +55,7 @@ const NUMERIC_OPERATORS = [">=", "<=", ">", "<", "="];
|
|
|
55
55
|
// rig-level only; `rigName` is the alias and exists at both levels (per the
|
|
56
56
|
// closed `rig ps trust and scale-safety` slice at openrig 0a9fb43).
|
|
57
57
|
const ALLOWED_RIG_FIELDS = new Set([
|
|
58
|
+
"attentionCount",
|
|
58
59
|
"rigId",
|
|
59
60
|
"name",
|
|
60
61
|
"rigName",
|
|
@@ -196,6 +197,45 @@ function parseFields(input, allowed, level) {
|
|
|
196
197
|
}
|
|
197
198
|
return fields;
|
|
198
199
|
}
|
|
200
|
+
function parsePsControls(opts) {
|
|
201
|
+
let effectiveFilter = opts.filter;
|
|
202
|
+
if (opts.active) {
|
|
203
|
+
if (effectiveFilter) {
|
|
204
|
+
return {
|
|
205
|
+
error: `--active and --filter cannot be combined. ` +
|
|
206
|
+
`--active is sugar for --filter agentActivity.state=running. ` +
|
|
207
|
+
`Pick one form, or compose by upgrading to --filter directly.`,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
effectiveFilter = "agentActivity.state=running";
|
|
211
|
+
}
|
|
212
|
+
let parsedFilter = null;
|
|
213
|
+
if (effectiveFilter) {
|
|
214
|
+
const result = parseFilter(effectiveFilter);
|
|
215
|
+
if ("error" in result)
|
|
216
|
+
return result;
|
|
217
|
+
parsedFilter = result;
|
|
218
|
+
}
|
|
219
|
+
const limit = opts.limit !== undefined ? Number(opts.limit) : null;
|
|
220
|
+
if (limit !== null && (!Number.isInteger(limit) || limit < 0)) {
|
|
221
|
+
return { error: `--limit must be a non-negative integer; got '${opts.limit}'` };
|
|
222
|
+
}
|
|
223
|
+
let fields = null;
|
|
224
|
+
if (opts.fields !== undefined) {
|
|
225
|
+
const fieldsLevel = opts.nodes ? "nodes" : "rig";
|
|
226
|
+
const fieldsAllowed = fieldsLevel === "nodes" ? ALLOWED_NODE_FIELDS : ALLOWED_RIG_FIELDS;
|
|
227
|
+
const fieldsResult = parseFields(opts.fields, fieldsAllowed, fieldsLevel);
|
|
228
|
+
if ("error" in fieldsResult)
|
|
229
|
+
return fieldsResult;
|
|
230
|
+
fields = fieldsResult;
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
parsedFilter,
|
|
234
|
+
limit,
|
|
235
|
+
fields,
|
|
236
|
+
useEnvelope: parsedFilter !== null || limit !== null || fields !== null || opts.summary === true,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
199
239
|
function applyRigFilter(entries, filter) {
|
|
200
240
|
return entries.filter((e) => {
|
|
201
241
|
if (filter.key === "status")
|
|
@@ -258,6 +298,65 @@ function applyNodeFilter(entries, filter) {
|
|
|
258
298
|
// OPR.0.3.3.19 - build the /api/ps path, opting archived rigs back in when
|
|
259
299
|
// --include-archived is set. Default (no flag) leaves the daemon's
|
|
260
300
|
// archived-excluding default in force.
|
|
301
|
+
function fanOutNodesError() {
|
|
302
|
+
return [
|
|
303
|
+
"rig ps --all-hosts/--hosts --nodes: per-node fan-out requires the FULL explicit ladder.",
|
|
304
|
+
" rig ps --all-hosts --nodes -A (fleet nodes per host, projected rows)",
|
|
305
|
+
" rig ps --all-hosts --nodes -A --full (complete per-node records — the last rung)",
|
|
306
|
+
"One rig's seats on one host: rig ps --host <id> --nodes --rig <name>.",
|
|
307
|
+
].join("\n");
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* OPR.0.4.4.21 FR-2/FR-3 — THE centralized disclosure-ladder validator.
|
|
311
|
+
* Runs BEFORE the local/HTTP-host/SSH/fan-out dispatch split so every path
|
|
312
|
+
* obeys one grammar (qa1 plan-review: remote mode bypassed the session-rig
|
|
313
|
+
* injection and fanned wide). Principle (arch-ratified): IMPLICIT SCOPE
|
|
314
|
+
* DEFAULTS DON'T CROSS HOST BOUNDARIES — the session-rig default has no
|
|
315
|
+
* stable referent on a remote host (a same-named rig there would silently
|
|
316
|
+
* misresolve), so remote per-node views are explicit-or-error.
|
|
317
|
+
* Returns the teaching error text, or null when the invocation is valid.
|
|
318
|
+
*/
|
|
319
|
+
export function validatePsLadder(opts, callerRig) {
|
|
320
|
+
const isFanOut = !!(opts.allHosts || opts.hosts);
|
|
321
|
+
const isSingleRemote = !!opts.host;
|
|
322
|
+
// FR-3: -A/--all-rigs has exactly ONE meaning — the --nodes fleet widener.
|
|
323
|
+
if (opts.allRigs && !opts.nodes) {
|
|
324
|
+
return [
|
|
325
|
+
"rig ps -A: '-A/--all-rigs' now has exactly one meaning — the --nodes fleet widener.",
|
|
326
|
+
"The consolidated all-rigs view IS the default: just run 'rig ps'.",
|
|
327
|
+
"Fleet nodes: rig ps --nodes -A (add --full for complete per-node records).",
|
|
328
|
+
"Archived history: rig ps --include-archived.",
|
|
329
|
+
].join("\n");
|
|
330
|
+
}
|
|
331
|
+
// FR-5: multi-host fan-out is rollup-only UNLESS the full explicit
|
|
332
|
+
// ladder is requested — `--nodes -A` per host (`--full` for complete
|
|
333
|
+
// records), exactly as the PRD writes it. Anything less explicit under
|
|
334
|
+
// --nodes errors; never silent rig-tier data under a --nodes flag.
|
|
335
|
+
if (opts.nodes && isFanOut && !opts.allRigs) {
|
|
336
|
+
return fanOutNodesError();
|
|
337
|
+
}
|
|
338
|
+
// FR-2: --nodes always names its scope — session default locally,
|
|
339
|
+
// explicit --rig / -A everywhere else. Never an implicit fan-out.
|
|
340
|
+
if (opts.nodes && !opts.rig && !opts.allRigs) {
|
|
341
|
+
if (isFanOut) {
|
|
342
|
+
return fanOutNodesError();
|
|
343
|
+
}
|
|
344
|
+
if (isSingleRemote) {
|
|
345
|
+
return [
|
|
346
|
+
`rig ps --host ${opts.host} --nodes: no target — the local session's rig is not a remote scope`,
|
|
347
|
+
"(implicit scope defaults don't cross host boundaries; a same-named remote rig would silently misresolve).",
|
|
348
|
+
`Name one: rig ps --host ${opts.host} --nodes --rig <name>, or that host's fleet explicitly: rig ps --host ${opts.host} --nodes -A.`,
|
|
349
|
+
].join("\n");
|
|
350
|
+
}
|
|
351
|
+
if (!callerRig) {
|
|
352
|
+
return [
|
|
353
|
+
"rig ps --nodes: no target — outside a managed session there is no current rig to default to.",
|
|
354
|
+
"Name one: rig ps --nodes --rig <name>, or go fleet-wide explicitly: rig ps --nodes -A.",
|
|
355
|
+
].join("\n");
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
261
360
|
function psApiPath(opts) {
|
|
262
361
|
return opts.includeArchived ? "/api/ps?includeArchived=true" : "/api/ps";
|
|
263
362
|
}
|
|
@@ -269,6 +368,12 @@ function selectFields(entries, fields) {
|
|
|
269
368
|
return out;
|
|
270
369
|
});
|
|
271
370
|
}
|
|
371
|
+
function selectFanOutFields(entries, fields) {
|
|
372
|
+
return entries.map((e) => {
|
|
373
|
+
const [selected] = selectFields([e], fields);
|
|
374
|
+
return { ...selected, hostId: e.hostId };
|
|
375
|
+
});
|
|
376
|
+
}
|
|
272
377
|
function needsAttention(node) {
|
|
273
378
|
return node.lifecycleState === "attention_required"
|
|
274
379
|
|| node.startupStatus === "attention_required"
|
|
@@ -373,22 +478,39 @@ function abbrevNodeLifecycle(state) {
|
|
|
373
478
|
return "—";
|
|
374
479
|
}
|
|
375
480
|
/**
|
|
376
|
-
* `rig ps` —
|
|
481
|
+
* `rig ps` — the consolidated fleet map + explicit disclosure ladder.
|
|
377
482
|
*
|
|
378
|
-
*
|
|
379
|
-
*
|
|
380
|
-
*
|
|
381
|
-
*
|
|
382
|
-
*
|
|
383
|
-
*
|
|
483
|
+
* OPR.0.4.4.21: the default is ALL active rigs, one compact O(rigs) row each
|
|
484
|
+
* (the current-rig-only default is retired). The token-safety invariant: no
|
|
485
|
+
* invocation returns per-node detail across all rigs unless explicitly
|
|
486
|
+
* flagged (--nodes -A), and even then rows are projected unless --full.
|
|
487
|
+
* Default `rig ps --json` keeps the bare-array shape for back-compat; the
|
|
488
|
+
* truncation/envelope shape only applies when at least one of
|
|
489
|
+
* `--limit`/`--summary`/`--fields`/`--filter` is specified.
|
|
384
490
|
*/
|
|
385
491
|
export function psCommand(depsOverride) {
|
|
386
492
|
const cmd = new Command("ps")
|
|
387
493
|
.description("List rigs and their status")
|
|
388
494
|
.addHelpText("after", `
|
|
389
|
-
Default:
|
|
390
|
-
|
|
391
|
-
|
|
495
|
+
Default (OPR.0.4.4.21): ALL active rigs, ONE compact row each — O(rigs), never
|
|
496
|
+
a fleet node fan-out — plus the host rollup line ("N rigs · M seats · K need
|
|
497
|
+
attention"), the archived/stopped count line, and the drill-ladder footer.
|
|
498
|
+
STATED contract: default --json is a bare array of ALL non-archived rigs
|
|
499
|
+
INCLUDING stopped ones (existing keys preserved; additive attentionCount);
|
|
500
|
+
only the HUMAN table folds stopped rigs into the count line.
|
|
501
|
+
|
|
502
|
+
The disclosure ladder (each heavier view is an explicit step):
|
|
503
|
+
rig ps the consolidated map (this default)
|
|
504
|
+
rig ps --rig <name> one rig's detail
|
|
505
|
+
rig ps --nodes per-node, CURRENT rig (session default, local only)
|
|
506
|
+
rig ps --nodes --rig <name> per-node, named rig
|
|
507
|
+
rig ps --nodes -A fleet nodes, projected rows
|
|
508
|
+
rig ps --nodes -A --full complete per-node records (the only full fan-out)
|
|
509
|
+
|
|
510
|
+
-A/--all-rigs has exactly ONE meaning: the --nodes fleet widener. Bare -A
|
|
511
|
+
errors (all-rigs IS the default; archived history stays behind
|
|
512
|
+
--include-archived). The session-rig default never crosses host boundaries:
|
|
513
|
+
remote --nodes requires an explicit --rig or -A.
|
|
392
514
|
|
|
393
515
|
Compact defaults: 'rig ps --nodes' shows a compact summary per node
|
|
394
516
|
(rig, session, lifecycle, activity state, reason when attention, queue counts,
|
|
@@ -402,11 +524,10 @@ null even with --full; fetch the full recovery guidance + currentUsage from the
|
|
|
402
524
|
single-node detail (/api/rigs/:rigId/nodes/:logicalId) or 'rig whoami'.
|
|
403
525
|
|
|
404
526
|
Examples:
|
|
405
|
-
rig ps
|
|
406
|
-
rig ps
|
|
407
|
-
rig ps --full
|
|
408
|
-
rig ps --json
|
|
409
|
-
rig ps -A --json All rigs, compact JSON
|
|
527
|
+
rig ps All active rigs, one row each + rollup
|
|
528
|
+
rig ps --rig <name> One rig's detail
|
|
529
|
+
rig ps --full All rigs, no table truncation
|
|
530
|
+
rig ps --json All non-archived rigs, bare JSON array
|
|
410
531
|
rig ps --json --limit 20 Bounded JSON envelope
|
|
411
532
|
rig ps --json --summary Aggregate-only JSON
|
|
412
533
|
rig ps --json --fields rigName,status,lifecycleState
|
|
@@ -426,12 +547,16 @@ Examples:
|
|
|
426
547
|
rig ps --nodes --running Same as --active
|
|
427
548
|
rig ps --nodes --filter agentActivity.state=running
|
|
428
549
|
Same as --active (the explicit form)
|
|
429
|
-
rig ps --host vm-
|
|
550
|
+
rig ps --host vm-1 --nodes --rig <name> --json Remote host per-node (explicit target required)
|
|
551
|
+
rig ps --all-hosts Per-host O(rigs) rollups (AggregatedPayload JSON)
|
|
430
552
|
rig ps --include-archived Include archived rigs (marked with *); hidden by default
|
|
431
553
|
|
|
432
|
-
--rig <name>
|
|
433
|
-
--session <name> filters within the effective rig scope; use -A if the
|
|
434
|
-
session is in a different rig.
|
|
554
|
+
--rig <name> scopes to one rig. -A/--all-rigs is ONLY the --nodes fleet widener.
|
|
555
|
+
--session <name> filters within the effective rig scope; use --nodes -A if the
|
|
556
|
+
target session is in a different rig.
|
|
557
|
+
Multi-host fan-out (--all-hosts/--hosts) is rollup-only by default; the full
|
|
558
|
+
explicit ladder (--all-hosts --nodes -A, --full for complete records) fans out
|
|
559
|
+
per-node with hostId-stamped projected rows.
|
|
435
560
|
|
|
436
561
|
--active/--running narrow to agentActivity.state=running. Cannot combine with --filter.
|
|
437
562
|
|
|
@@ -439,7 +564,8 @@ session is in a different rig.
|
|
|
439
564
|
contextUsage.percent, contextUsage.state. Other keys are rejected.
|
|
440
565
|
|
|
441
566
|
--fields accepts (rig-level): rigId, name, rigName, nodeCount, runningCount,
|
|
442
|
-
activeCount, hasWorkCount, status, lifecycleState, uptime,
|
|
567
|
+
activeCount, hasWorkCount, attentionCount, status, lifecycleState, uptime,
|
|
568
|
+
latestSnapshot.
|
|
443
569
|
--fields accepts (node-level, with --nodes): rigId, rigName, logicalId, podId,
|
|
444
570
|
podNamespace, canonicalSessionName, nodeKind, runtime, sessionStatus,
|
|
445
571
|
startupStatus, restoreOutcome, lifecycleState, tmuxAttachCommand,
|
|
@@ -478,15 +604,49 @@ Exit codes:
|
|
|
478
604
|
if (opts.running)
|
|
479
605
|
opts.active = true;
|
|
480
606
|
const isRemote = !!(opts.host || opts.allHosts || opts.hosts);
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
607
|
+
// OPR.0.4.4.21 FR-1: the rig tier is consolidated ALL-ACTIVE-RIGS by
|
|
608
|
+
// default (the current-rig-only default is RETIRED — it hid running
|
|
609
|
+
// rigs from the operator's field of view). The session-rig default
|
|
610
|
+
// now applies ONLY to the node tier (FR-2's scoped --nodes), and
|
|
611
|
+
// ONLY locally (the ladder validator enforces explicit-or-error on
|
|
612
|
+
// every remote path).
|
|
613
|
+
const sessionName = readOpenRigEnv("OPENRIG_SESSION_NAME", "RIGGED_SESSION_NAME");
|
|
614
|
+
const callerRig = sessionName ? extractRigName(sessionName) : undefined;
|
|
615
|
+
const ladderError = validatePsLadder(opts, callerRig);
|
|
616
|
+
if (ladderError) {
|
|
617
|
+
console.error(ladderError);
|
|
618
|
+
process.exitCode = 1;
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
if (opts.nodes && !opts.allRigs && !opts.rig && !isRemote && callerRig) {
|
|
622
|
+
opts.rig = callerRig;
|
|
486
623
|
}
|
|
487
624
|
const deps = getDepsF();
|
|
625
|
+
// OPR.0.4.4.21 rev1-r2 fixback: the shared shaping controls
|
|
626
|
+
// (--active/--filter/--limit/--fields/--summary) parse + validate
|
|
627
|
+
// BEFORE any dispatch — local, single-host, or fan-out — so every
|
|
628
|
+
// path honors the same composition contract and rejections stay
|
|
629
|
+
// pre-HTTP on remote paths too.
|
|
630
|
+
// Parse shared composition controls once up front so local, single-host,
|
|
631
|
+
// and fan-out paths reject malformed --filter/--limit/--fields before
|
|
632
|
+
// any HTTP call and apply the same shaping semantics.
|
|
633
|
+
const controls = parsePsControls(opts);
|
|
634
|
+
if ("error" in controls) {
|
|
635
|
+
console.error(controls.error);
|
|
636
|
+
process.exitCode = 1;
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
const { parsedFilter, limit, fields, useEnvelope } = controls;
|
|
488
640
|
if (opts.allHosts || opts.hosts) {
|
|
489
|
-
|
|
641
|
+
// AggregatedPayload is the closed fan-out contract (items + hosts).
|
|
642
|
+
// Do not add an ad-hoc summary member here; teach the per-host form.
|
|
643
|
+
if (opts.summary) {
|
|
644
|
+
console.error("rig ps --all-hosts/--hosts --summary: summary does not compose with the merged fan-out payload.\n" +
|
|
645
|
+
"Summarize one host: rig ps --host <id> --summary; or drop --summary for the merged AggregatedPayload.");
|
|
646
|
+
process.exitCode = 1;
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
await runFanOutPs(opts, deps, { parsedFilter, limit, fields });
|
|
490
650
|
return;
|
|
491
651
|
}
|
|
492
652
|
if (opts.host) {
|
|
@@ -499,73 +659,31 @@ Exit codes:
|
|
|
499
659
|
process.exitCode = 1;
|
|
500
660
|
return;
|
|
501
661
|
}
|
|
502
|
-
// Parse filter once up front so unknown keys/malformed filters surface
|
|
503
|
-
// as exit-1 errors before any HTTP call.
|
|
504
|
-
//
|
|
505
|
-
// PL-019 item 1: --active is sugar for --filter agentActivity.state=running.
|
|
506
|
-
// Combining --active with --filter is rejected (composition is
|
|
507
|
-
// ambiguous — pick one explicit form). Parity is verified end-to-end
|
|
508
|
-
// by the focused test: `--active` and the explicit filter must yield
|
|
509
|
-
// identical output on the same fixture.
|
|
510
|
-
let effectiveFilter = opts.filter;
|
|
511
|
-
if (opts.active) {
|
|
512
|
-
if (effectiveFilter) {
|
|
513
|
-
console.error(`--active and --filter cannot be combined. ` +
|
|
514
|
-
`--active is sugar for --filter agentActivity.state=running. ` +
|
|
515
|
-
`Pick one form, or compose by upgrading to --filter directly.`);
|
|
516
|
-
process.exitCode = 1;
|
|
517
|
-
return;
|
|
518
|
-
}
|
|
519
|
-
effectiveFilter = "agentActivity.state=running";
|
|
520
|
-
}
|
|
521
|
-
let parsedFilter = null;
|
|
522
|
-
if (effectiveFilter) {
|
|
523
|
-
const result = parseFilter(effectiveFilter);
|
|
524
|
-
if ("error" in result) {
|
|
525
|
-
console.error(result.error);
|
|
526
|
-
process.exitCode = 1;
|
|
527
|
-
return;
|
|
528
|
-
}
|
|
529
|
-
parsedFilter = result;
|
|
530
|
-
}
|
|
531
|
-
const limit = opts.limit !== undefined ? Number(opts.limit) : null;
|
|
532
|
-
if (limit !== null && (!Number.isInteger(limit) || limit < 0)) {
|
|
533
|
-
console.error(`--limit must be a non-negative integer; got '${opts.limit}'`);
|
|
534
|
-
process.exitCode = 1;
|
|
535
|
-
return;
|
|
536
|
-
}
|
|
537
|
-
// C9a: validate --fields against the per-level allow-list before any HTTP
|
|
538
|
-
// call, mirroring the --filter validation above. Level is determined by
|
|
539
|
-
// --nodes; both branches downstream use the same `fields` variable, so
|
|
540
|
-
// one up-front validation covers both paths.
|
|
541
|
-
let fields = null;
|
|
542
|
-
if (opts.fields !== undefined) {
|
|
543
|
-
const fieldsLevel = opts.nodes ? "nodes" : "rig";
|
|
544
|
-
const fieldsAllowed = fieldsLevel === "nodes" ? ALLOWED_NODE_FIELDS : ALLOWED_RIG_FIELDS;
|
|
545
|
-
const fieldsResult = parseFields(opts.fields, fieldsAllowed, fieldsLevel);
|
|
546
|
-
if ("error" in fieldsResult) {
|
|
547
|
-
console.error(fieldsResult.error);
|
|
548
|
-
process.exitCode = 1;
|
|
549
|
-
return;
|
|
550
|
-
}
|
|
551
|
-
fields = fieldsResult;
|
|
552
|
-
}
|
|
553
|
-
const useEnvelope = parsedFilter !== null || limit !== null || fields !== null || opts.summary === true;
|
|
554
662
|
const client = deps.clientFactory(getDaemonUrl(status));
|
|
555
663
|
if (opts.nodes) {
|
|
556
664
|
await handleNodes(client, opts, parsedFilter, limit, fields, useEnvelope);
|
|
557
665
|
return;
|
|
558
666
|
}
|
|
559
|
-
|
|
667
|
+
// OPR.0.4.4.21 FR-1: ONE O(rigs) fetch including archived so the
|
|
668
|
+
// count line can be computed; visibility is split client-side below
|
|
669
|
+
// (JSON keeps today's default of excluding archived — parity).
|
|
670
|
+
const res = await client.get("/api/ps?includeArchived=true");
|
|
560
671
|
if (res.status >= 400) {
|
|
561
672
|
console.error(`Failed to fetch rig list from daemon (HTTP ${res.status}). Check daemon status with: rig status`);
|
|
562
673
|
process.exitCode = 2;
|
|
563
674
|
return;
|
|
564
675
|
}
|
|
565
676
|
const all = res.data;
|
|
677
|
+
// OPR.0.4.4.21 — archived visibility parity: the daemon call above
|
|
678
|
+
// always includes archived (for the count line); without
|
|
679
|
+
// --include-archived they are dropped from BOTH renders here, exactly
|
|
680
|
+
// as the daemon default used to do. archivedCount feeds the FR-1
|
|
681
|
+
// count line only.
|
|
682
|
+
const archivedCount = all.filter((e) => e.isArchived === true).length;
|
|
683
|
+
const visible = opts.includeArchived ? all : all.filter((e) => e.isArchived !== true);
|
|
566
684
|
const rigScoped = opts.rig
|
|
567
|
-
?
|
|
568
|
-
:
|
|
685
|
+
? visible.filter((e) => (e.rigName ?? e.name) === opts.rig)
|
|
686
|
+
: visible;
|
|
569
687
|
// Apply CLI-side filter (Amendment A: prefer CLI shaping).
|
|
570
688
|
const filtered = parsedFilter ? applyRigFilter(rigScoped, parsedFilter) : rigScoped;
|
|
571
689
|
// Summary mode short-circuits per-entry output.
|
|
@@ -608,15 +726,32 @@ Exit codes:
|
|
|
608
726
|
if (parsedFilter) {
|
|
609
727
|
console.log(`No rigs match --filter ${parsedFilter.key}=${parsedFilter.value}`);
|
|
610
728
|
}
|
|
729
|
+
else if (archivedCount > 0 && !opts.includeArchived) {
|
|
730
|
+
// Proven-empty with the history pointer, never a bare "No rigs"
|
|
731
|
+
// while archived history exists.
|
|
732
|
+
console.log(`No active rigs · ${archivedCount} archived (rig ps --include-archived)`);
|
|
733
|
+
}
|
|
611
734
|
else {
|
|
612
735
|
console.log("No rigs");
|
|
613
736
|
}
|
|
614
737
|
return;
|
|
615
738
|
}
|
|
739
|
+
// OPR.0.4.4.21 FR-1 (human table ONLY — JSON keeps ALL non-archived
|
|
740
|
+
// entries including stopped; this scope split is a STATED contract
|
|
741
|
+
// sentence in the help text): on the BARE default (no filter, no
|
|
742
|
+
// --rig, no --include-archived) stopped rigs fold into the count
|
|
743
|
+
// line — history is what the default drops, not field of view.
|
|
744
|
+
const bareDefault = !parsedFilter && !opts.rig && !opts.includeArchived;
|
|
745
|
+
const tableRows = bareDefault ? limited.filter((e) => e.status !== "stopped") : limited;
|
|
746
|
+
const stoppedCount = bareDefault ? limited.length - tableRows.length : 0;
|
|
747
|
+
// FR-1 display element 1: the host rollup line.
|
|
748
|
+
const rollupSeats = tableRows.reduce((n, e) => n + e.nodeCount, 0);
|
|
749
|
+
const rollupAttention = tableRows.reduce((n, e) => n + (e.attentionCount ?? 0), 0);
|
|
750
|
+
console.log(`${tableRows.length} rig${tableRows.length === 1 ? "" : "s"} · ${rollupSeats} seat${rollupSeats === 1 ? "" : "s"} · ${rollupAttention} need${rollupAttention === 1 ? "s" : ""} attention`);
|
|
616
751
|
// Human output: apply default truncation budget unless --full.
|
|
617
|
-
const humanList = (opts.full || limit !== null) ?
|
|
618
|
-
const humanTruncated = !opts.full && limit === null &&
|
|
619
|
-
const header = padRigRow("RIG", "NODES", "RUNNING", "ACTIVE", "WORK", "STATUS", "LIFECYCLE", "UPTIME", "SNAPSHOT");
|
|
752
|
+
const humanList = (opts.full || limit !== null) ? tableRows : tableRows.slice(0, HUMAN_RIG_BUDGET);
|
|
753
|
+
const humanTruncated = !opts.full && limit === null && tableRows.length > HUMAN_RIG_BUDGET;
|
|
754
|
+
const header = padRigRow("RIG", "NODES", "RUNNING", "ACTIVE", "WORK", "ATTN", "STATUS", "LIFECYCLE", "UPTIME", "SNAPSHOT");
|
|
620
755
|
console.log(header);
|
|
621
756
|
let anyArchivedShown = false;
|
|
622
757
|
for (const e of humanList) {
|
|
@@ -627,11 +762,27 @@ Exit codes:
|
|
|
627
762
|
anyArchivedShown = true;
|
|
628
763
|
console.log(padRigRow(e.isArchived ? `${e.rigName ?? e.name} *` : (e.rigName ?? e.name), String(e.nodeCount), String(e.runningCount),
|
|
629
764
|
// Slice 15 — "—" when daemon predates the field; honest absence.
|
|
630
|
-
e.activeCount !== undefined ? String(e.activeCount) : "—", e.hasWorkCount !== undefined ? String(e.hasWorkCount) : "—",
|
|
765
|
+
e.activeCount !== undefined ? String(e.activeCount) : "—", e.hasWorkCount !== undefined ? String(e.hasWorkCount) : "—",
|
|
766
|
+
// OPR.0.4.4.21 — the founder's field-of-view anchor: where is
|
|
767
|
+
// something that might concern me. "—" = daemon predates the field.
|
|
768
|
+
e.attentionCount !== undefined ? (e.attentionCount > 0 ? `▲${e.attentionCount}` : "0") : "—", e.status, abbrevRigLifecycle(e.lifecycleState), e.uptime ?? "—", e.latestSnapshot ?? "—"));
|
|
631
769
|
}
|
|
632
770
|
if (anyArchivedShown) {
|
|
633
771
|
console.log("* = archived (hidden from the default view; shown via --include-archived). Reverse with: rig unarchive <rig>");
|
|
634
772
|
}
|
|
773
|
+
// FR-1 display element 2: history as ONE count line, never rows.
|
|
774
|
+
if (stoppedCount > 0 || (!opts.includeArchived && archivedCount > 0)) {
|
|
775
|
+
const parts = [];
|
|
776
|
+
if (stoppedCount > 0)
|
|
777
|
+
parts.push(`${stoppedCount} stopped (rig ps --filter status=stopped)`);
|
|
778
|
+
if (!opts.includeArchived && archivedCount > 0)
|
|
779
|
+
parts.push(`${archivedCount} archived (rig ps --include-archived)`);
|
|
780
|
+
console.log(`not shown: ${parts.join(" · ")}`);
|
|
781
|
+
}
|
|
782
|
+
// FR-1 display element 3: the affordance footer — the drill ladder.
|
|
783
|
+
if (bareDefault) {
|
|
784
|
+
console.log("drill: rig ps --rig <name> (one rig) · rig ps --nodes --rig <name> (its seats) · --full (everything)");
|
|
785
|
+
}
|
|
635
786
|
if (humanTruncated) {
|
|
636
787
|
const remaining = filtered.length - HUMAN_RIG_BUDGET;
|
|
637
788
|
console.log(`... and ${remaining} more rig${remaining === 1 ? "" : "s"} (truncated at ${HUMAN_RIG_BUDGET}).`);
|
|
@@ -666,7 +817,12 @@ async function handleNodes(client, opts, parsedFilter, limit, fields, useEnvelop
|
|
|
666
817
|
console.error(`Warning: failed to fetch nodes for rig "${rig.rigName ?? rig.name}" (HTTP ${nodesRes.status}). List rigs with: rig ps`);
|
|
667
818
|
continue;
|
|
668
819
|
}
|
|
669
|
-
|
|
820
|
+
const parentRigName = rig.rigName ?? rig.name;
|
|
821
|
+
allNodes.push(...nodesRes.data.map((n) => ({
|
|
822
|
+
...n,
|
|
823
|
+
rigId: n.rigId ?? rig.rigId,
|
|
824
|
+
rigName: n.rigName ?? parentRigName,
|
|
825
|
+
})));
|
|
670
826
|
}
|
|
671
827
|
let narrowed = allNodes;
|
|
672
828
|
if (opts.rig)
|
|
@@ -769,7 +925,7 @@ function truncate(s, max) {
|
|
|
769
925
|
function fitCell(value, width) {
|
|
770
926
|
return truncate(value, width).padEnd(width);
|
|
771
927
|
}
|
|
772
|
-
function padRigRow(rig, nodes, running, active, work, status, lifecycle, uptime, snapshot) {
|
|
928
|
+
function padRigRow(rig, nodes, running, active, work, attn, status, lifecycle, uptime, snapshot) {
|
|
773
929
|
return [
|
|
774
930
|
fitCell(rig, 24),
|
|
775
931
|
fitCell(nodes, 7),
|
|
@@ -780,6 +936,8 @@ function padRigRow(rig, nodes, running, active, work, status, lifecycle, uptime,
|
|
|
780
936
|
// operators see which dimension differs at a glance.
|
|
781
937
|
fitCell(active, 8),
|
|
782
938
|
fitCell(work, 6),
|
|
939
|
+
// OPR.0.4.4.21 — ATTN: seats needing attention (the field-of-view anchor).
|
|
940
|
+
fitCell(attn, 6),
|
|
783
941
|
fitCell(status, 10),
|
|
784
942
|
fitCell(lifecycle, 11),
|
|
785
943
|
fitCell(uptime, 11),
|
|
@@ -878,7 +1036,10 @@ async function runCrossHostPs(hostId, opts, deps) {
|
|
|
878
1036
|
// OPR.0.4.0.34: forward the breadth flag so `--host h -A` keeps all-rigs
|
|
879
1037
|
// breadth across the hop (the current-rig default is local-only and never
|
|
880
1038
|
// applied to a remote call, but an explicit -A must still reach the remote).
|
|
881
|
-
|
|
1039
|
+
// OPR.0.4.4.21 FR-3: -A is legal only alongside --nodes (the validator
|
|
1040
|
+
// already rejected the bare form before any dispatch; this guard keeps
|
|
1041
|
+
// the reconstructed remote argv obeying the same grammar).
|
|
1042
|
+
if (opts.allRigs && opts.nodes)
|
|
882
1043
|
argv.push("--all-rigs");
|
|
883
1044
|
if (opts.limit !== undefined)
|
|
884
1045
|
argv.push("--limit", opts.limit);
|
|
@@ -932,46 +1093,15 @@ async function runHttpPs(host, opts, deps) {
|
|
|
932
1093
|
process.exitCode = 1;
|
|
933
1094
|
return;
|
|
934
1095
|
}
|
|
935
|
-
|
|
936
|
-
if (
|
|
937
|
-
|
|
938
|
-
console.error("--active and --filter cannot be combined.");
|
|
939
|
-
process.exitCode = 1;
|
|
940
|
-
return;
|
|
941
|
-
}
|
|
942
|
-
effectiveFilter = "agentActivity.state=running";
|
|
943
|
-
}
|
|
944
|
-
let parsedFilter = null;
|
|
945
|
-
if (effectiveFilter) {
|
|
946
|
-
const result = parseFilter(effectiveFilter);
|
|
947
|
-
if ("error" in result) {
|
|
948
|
-
console.error(result.error);
|
|
949
|
-
process.exitCode = 1;
|
|
950
|
-
return;
|
|
951
|
-
}
|
|
952
|
-
parsedFilter = result;
|
|
953
|
-
}
|
|
954
|
-
const limit = opts.limit !== undefined ? Number(opts.limit) : null;
|
|
955
|
-
if (limit !== null && (!Number.isInteger(limit) || limit < 0)) {
|
|
956
|
-
console.error(`--limit must be a non-negative integer; got '${opts.limit}'`);
|
|
1096
|
+
const controls = parsePsControls(opts);
|
|
1097
|
+
if ("error" in controls) {
|
|
1098
|
+
console.error(controls.error);
|
|
957
1099
|
process.exitCode = 1;
|
|
958
1100
|
return;
|
|
959
1101
|
}
|
|
960
|
-
|
|
961
|
-
if (opts.fields !== undefined) {
|
|
962
|
-
const fieldsLevel = opts.nodes ? "nodes" : "rig";
|
|
963
|
-
const fieldsAllowed = fieldsLevel === "nodes" ? ALLOWED_NODE_FIELDS : ALLOWED_RIG_FIELDS;
|
|
964
|
-
const fieldsResult = parseFields(opts.fields, fieldsAllowed, fieldsLevel);
|
|
965
|
-
if ("error" in fieldsResult) {
|
|
966
|
-
console.error(fieldsResult.error);
|
|
967
|
-
process.exitCode = 1;
|
|
968
|
-
return;
|
|
969
|
-
}
|
|
970
|
-
fields = fieldsResult;
|
|
971
|
-
}
|
|
1102
|
+
const { parsedFilter, limit, fields, useEnvelope } = controls;
|
|
972
1103
|
const client = deps.clientFactory(host.url);
|
|
973
1104
|
const headers = buildRemoteHeaders(bearerResult.token);
|
|
974
|
-
const useEnvelope = parsedFilter !== null || limit !== null || fields !== null || opts.summary === true;
|
|
975
1105
|
try {
|
|
976
1106
|
if (opts.nodes) {
|
|
977
1107
|
await handleNodes(client, opts, parsedFilter, limit, fields, useEnvelope, headers);
|
|
@@ -1020,7 +1150,22 @@ async function runHttpPs(host, opts, deps) {
|
|
|
1020
1150
|
process.exitCode = 1;
|
|
1021
1151
|
}
|
|
1022
1152
|
}
|
|
1023
|
-
|
|
1153
|
+
/** OPR.0.4.4.21 FR-5 — adapter to the intra-P4 shared contract
|
|
1154
|
+
* (fanout-contract.ts, slice 15 first-lander at 0ecd329b; the closed
|
|
1155
|
+
* status enum is THE contract, failedStep rides as additive detail). */
|
|
1156
|
+
function toPerHostStatus(r) {
|
|
1157
|
+
const status = r.ok ? "ok"
|
|
1158
|
+
: r.failedStep === "unsupported-transport" ? "unsupported-transport"
|
|
1159
|
+
: r.failedStep === "permission-gate" ? "auth-failed"
|
|
1160
|
+
: "unreachable";
|
|
1161
|
+
const out = { hostId: r.host, status };
|
|
1162
|
+
if (r.error)
|
|
1163
|
+
out.error = r.error;
|
|
1164
|
+
if (!r.ok && r.failedStep !== "unsupported-transport")
|
|
1165
|
+
out.failedStep = r.failedStep;
|
|
1166
|
+
return out;
|
|
1167
|
+
}
|
|
1168
|
+
async function runFanOutPs(opts, deps, shaping) {
|
|
1024
1169
|
const loader = deps.hostRegistryLoader ?? loadHostRegistry;
|
|
1025
1170
|
const registry = loader();
|
|
1026
1171
|
if (!registry.ok) {
|
|
@@ -1040,14 +1185,20 @@ async function runFanOutPs(opts, deps) {
|
|
|
1040
1185
|
}
|
|
1041
1186
|
}
|
|
1042
1187
|
else {
|
|
1043
|
-
|
|
1188
|
+
// OPR.0.4.4.21 fixback (qa1 F1): target EVERY declared host — non-HTTP
|
|
1189
|
+
// hosts must appear in hosts[] as unsupported-transport (R15-2), never
|
|
1190
|
+
// be silently absent. The per-host leg classifies transport.
|
|
1191
|
+
targetIds = allHosts.map((h) => h.id);
|
|
1044
1192
|
}
|
|
1045
1193
|
const results = await Promise.all(targetIds.map(async (id) => {
|
|
1046
1194
|
const host = allHosts.find((h) => h.id === id);
|
|
1047
1195
|
if (!host)
|
|
1048
1196
|
return { host: id, ok: false, failedStep: "remote-daemon-unreachable", error: `unknown host ${id}` };
|
|
1049
1197
|
if (host.transport !== "http") {
|
|
1050
|
-
|
|
1198
|
+
// R15-2 (shared contract): an SSH-declared host is a STRUCTURED
|
|
1199
|
+
// unsupported-transport status — never prose-only, never silently
|
|
1200
|
+
// thinner output.
|
|
1201
|
+
return { host: id, ok: false, failedStep: "unsupported-transport", error: `host ${id} uses transport ${host.transport}; HTTP fan-out requires transport: http` };
|
|
1051
1202
|
}
|
|
1052
1203
|
const httpHost = host;
|
|
1053
1204
|
const bearerResult = resolveRemoteBearer(httpHost);
|
|
@@ -1055,16 +1206,35 @@ async function runFanOutPs(opts, deps) {
|
|
|
1055
1206
|
return { host: id, ok: false, failedStep: bearerResult.failedStep, error: bearerResult.error };
|
|
1056
1207
|
}
|
|
1057
1208
|
const client = deps.clientFactory(httpHost.url);
|
|
1209
|
+
const headers = buildRemoteHeaders(bearerResult.token);
|
|
1058
1210
|
try {
|
|
1059
1211
|
const psQuery = opts.includeArchived ? "?includeArchived=true" : "";
|
|
1060
|
-
const res = await client.get(`/api/ps${psQuery}`, {
|
|
1061
|
-
headers: buildRemoteHeaders(bearerResult.token),
|
|
1062
|
-
});
|
|
1212
|
+
const res = await client.get(`/api/ps${psQuery}`, { headers });
|
|
1063
1213
|
const failedStep = classifyHttpFailedStep(res.status);
|
|
1064
1214
|
if (failedStep !== "none") {
|
|
1065
1215
|
return { host: id, ok: false, failedStep, error: `HTTP ${res.status}` };
|
|
1066
1216
|
}
|
|
1067
|
-
|
|
1217
|
+
if (!opts.nodes) {
|
|
1218
|
+
return { host: id, ok: true, failedStep: "none", data: res.data };
|
|
1219
|
+
}
|
|
1220
|
+
// OPR.0.4.4.21 FR-5 — the full explicit ladder (--nodes -A, --full
|
|
1221
|
+
// for complete records): per-host node fan-out, projected unless
|
|
1222
|
+
// --full (the invariant's last rung; validated as -A-only upstream).
|
|
1223
|
+
const rigs = Array.isArray(res.data) ? res.data : [];
|
|
1224
|
+
const nodes = [];
|
|
1225
|
+
for (const rig of rigs) {
|
|
1226
|
+
const nodesRes = await client.get(`/api/rigs/${encodeURIComponent(rig.rigId)}/nodes`, { headers });
|
|
1227
|
+
if (nodesRes.status >= 400) {
|
|
1228
|
+
return { host: id, ok: false, failedStep: classifyHttpFailedStep(nodesRes.status), error: `HTTP ${nodesRes.status} fetching nodes for rig ${rig.rigName ?? rig.name}` };
|
|
1229
|
+
}
|
|
1230
|
+
const parentRigName = rig.rigName ?? rig.name;
|
|
1231
|
+
nodes.push(...nodesRes.data.map((n) => ({
|
|
1232
|
+
...n,
|
|
1233
|
+
rigId: n.rigId ?? rig.rigId,
|
|
1234
|
+
rigName: n.rigName ?? parentRigName,
|
|
1235
|
+
})));
|
|
1236
|
+
}
|
|
1237
|
+
return { host: id, ok: true, failedStep: "none", data: nodes };
|
|
1068
1238
|
}
|
|
1069
1239
|
catch (err) {
|
|
1070
1240
|
return { host: id, ok: false, failedStep: classifyHttpError(err), error: err.message };
|
|
@@ -1072,7 +1242,49 @@ async function runFanOutPs(opts, deps) {
|
|
|
1072
1242
|
}));
|
|
1073
1243
|
const hasFailure = results.some((r) => !r.ok);
|
|
1074
1244
|
if (opts.json) {
|
|
1075
|
-
|
|
1245
|
+
// OPR.0.4.4.21 FR-5 — the intra-P4 shared payload (ONE contract with
|
|
1246
|
+
// slice 15): items = each host's rows stamped with their origin hostId
|
|
1247
|
+
// (flat-mergeable; origin never positional), hosts = the per-host
|
|
1248
|
+
// structured status array. EVERY targeted host appears in hosts[] —
|
|
1249
|
+
// ok or not (no silent thinning).
|
|
1250
|
+
const rawItems = results.flatMap((r) => r.ok && Array.isArray(r.data)
|
|
1251
|
+
? r.data.map((row) => ({ ...row, hostId: r.host }))
|
|
1252
|
+
: []);
|
|
1253
|
+
const hostStatuses = results.map(toPerHostStatus);
|
|
1254
|
+
let narrowed = rawItems;
|
|
1255
|
+
if (opts.rig)
|
|
1256
|
+
narrowed = narrowed.filter((e) => (e.rigName ?? e.name) === opts.rig);
|
|
1257
|
+
if (opts.nodes && opts.session)
|
|
1258
|
+
narrowed = narrowed.filter((n) => n.canonicalSessionName === opts.session);
|
|
1259
|
+
const filtered = shaping.parsedFilter
|
|
1260
|
+
? opts.nodes
|
|
1261
|
+
? applyNodeFilter(narrowed, shaping.parsedFilter)
|
|
1262
|
+
: applyRigFilter(narrowed, shaping.parsedFilter)
|
|
1263
|
+
: narrowed;
|
|
1264
|
+
let items;
|
|
1265
|
+
if (opts.summary) {
|
|
1266
|
+
items = [
|
|
1267
|
+
opts.nodes
|
|
1268
|
+
? summarizeNodes(filtered)
|
|
1269
|
+
: summarizeRigs(filtered),
|
|
1270
|
+
];
|
|
1271
|
+
}
|
|
1272
|
+
else {
|
|
1273
|
+
const limited = shaping.limit !== null ? filtered.slice(0, shaping.limit) : filtered;
|
|
1274
|
+
items = shaping.fields
|
|
1275
|
+
? selectFanOutFields(limited, shaping.fields)
|
|
1276
|
+
: opts.nodes && !opts.full
|
|
1277
|
+
? compactNodeProjection(limited).map((row, i) => ({
|
|
1278
|
+
...row,
|
|
1279
|
+
hostId: limited[i]?.hostId,
|
|
1280
|
+
}))
|
|
1281
|
+
: limited;
|
|
1282
|
+
}
|
|
1283
|
+
const payload = {
|
|
1284
|
+
items,
|
|
1285
|
+
hosts: hostStatuses,
|
|
1286
|
+
};
|
|
1287
|
+
console.log(JSON.stringify(payload));
|
|
1076
1288
|
}
|
|
1077
1289
|
else {
|
|
1078
1290
|
for (const r of results) {
|