@h-rig/cli 0.0.6-alpha.9 → 0.0.6-alpha.91

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.
Files changed (116) hide show
  1. package/README.md +18 -19
  2. package/dist/bin/build-rig-binaries.d.ts +2 -0
  3. package/dist/bin/build-rig-binaries.js +22 -10
  4. package/dist/bin/rig.d.ts +72 -0
  5. package/dist/bin/rig.js +15637 -7344
  6. package/dist/config/rig-default-config.yml +5 -0
  7. package/dist/src/app/drone-ui.d.ts +34 -0
  8. package/dist/src/app/drone-ui.js +278 -0
  9. package/dist/src/commands/_async-ui.d.ts +10 -0
  10. package/dist/src/commands/_async-ui.js +121 -0
  11. package/dist/src/commands/_cli-format.d.ts +56 -0
  12. package/dist/src/commands/_cli-format.js +319 -0
  13. package/dist/src/commands/_connection-state.d.ts +54 -0
  14. package/dist/src/commands/_connection-state.js +75 -11
  15. package/dist/src/commands/_doctor-checks.d.ts +46 -0
  16. package/dist/src/commands/_doctor-checks.js +100 -267
  17. package/dist/src/commands/_help-catalog.d.ts +51 -0
  18. package/dist/src/commands/_help-catalog.js +502 -0
  19. package/dist/src/commands/_inprocess-services.d.ts +33 -0
  20. package/dist/src/commands/_inprocess-services.js +102 -0
  21. package/dist/src/commands/_json-output.d.ts +11 -0
  22. package/dist/src/commands/_json-output.js +60 -0
  23. package/dist/src/commands/_lazy-reconcile.d.ts +34 -0
  24. package/dist/src/commands/_lazy-reconcile.js +102 -0
  25. package/dist/src/commands/_parsers.d.ts +15 -0
  26. package/dist/src/commands/_parsers.js +18 -11
  27. package/dist/src/commands/_paths.d.ts +11 -0
  28. package/dist/src/commands/_paths.js +1 -1
  29. package/dist/src/commands/_pi-frontend.d.ts +35 -0
  30. package/dist/src/commands/_pi-frontend.js +64 -0
  31. package/dist/src/commands/_pi-install.d.ts +42 -0
  32. package/dist/src/commands/_pi-install.js +19 -36
  33. package/dist/src/commands/_policy.d.ts +8 -0
  34. package/dist/src/commands/_policy.js +67 -19
  35. package/dist/src/commands/_probes.d.ts +1 -0
  36. package/dist/src/commands/_run-bridge.d.ts +114 -0
  37. package/dist/src/commands/_run-bridge.js +387 -0
  38. package/dist/src/commands/_run-diagnostics.d.ts +9 -0
  39. package/dist/src/commands/_run-diagnostics.js +51 -0
  40. package/dist/src/commands/_run-driver-helpers.d.ts +26 -0
  41. package/dist/src/commands/_run-driver-helpers.js +79 -230
  42. package/dist/src/commands/_run-projection.d.ts +50 -0
  43. package/dist/src/commands/_run-projection.js +349 -0
  44. package/dist/src/commands/_run-subcommands.d.ts +3 -0
  45. package/dist/src/commands/_run-subcommands.js +31 -0
  46. package/dist/src/commands/_spinner.d.ts +25 -0
  47. package/dist/src/commands/_spinner.js +65 -0
  48. package/dist/src/commands/agent.d.ts +3 -0
  49. package/dist/src/commands/agent.js +8575 -236
  50. package/dist/src/commands/dist.d.ts +28 -0
  51. package/dist/src/commands/dist.js +44 -29
  52. package/dist/src/commands/doctor.d.ts +3 -0
  53. package/dist/src/commands/doctor.js +219 -268
  54. package/dist/src/commands/github.d.ts +3 -0
  55. package/dist/src/commands/github.js +236 -188
  56. package/dist/src/commands/inbox.d.ts +28 -0
  57. package/dist/src/commands/inbox.js +530 -111
  58. package/dist/src/commands/init.d.ts +64 -0
  59. package/dist/src/commands/init.js +912 -705
  60. package/dist/src/commands/inspect.d.ts +20 -0
  61. package/dist/src/commands/inspect.js +709 -119
  62. package/dist/src/commands/pi.d.ts +3 -0
  63. package/dist/src/commands/pi.js +177 -0
  64. package/dist/src/commands/plugin.d.ts +16 -0
  65. package/dist/src/commands/plugin.js +576 -29
  66. package/dist/src/commands/profile-and-review.d.ts +4 -0
  67. package/dist/src/commands/profile-and-review.js +112 -67
  68. package/dist/src/commands/queue.d.ts +3 -0
  69. package/dist/src/commands/queue.js +20 -20
  70. package/dist/src/commands/remote.d.ts +3 -0
  71. package/dist/src/commands/remote.js +880 -50
  72. package/dist/src/commands/repo-git-harness.d.ts +5 -0
  73. package/dist/src/commands/repo-git-harness.js +78 -28
  74. package/dist/src/commands/run.d.ts +21 -0
  75. package/dist/src/commands/run.js +17701 -661
  76. package/dist/src/commands/server.d.ts +3 -0
  77. package/dist/src/commands/server.js +166 -303
  78. package/dist/src/commands/setup.d.ts +16 -0
  79. package/dist/src/commands/setup.js +244 -299
  80. package/dist/src/commands/stats.d.ts +15 -0
  81. package/dist/src/commands/stats.js +979 -0
  82. package/dist/src/commands/task-run-driver.d.ts +94 -0
  83. package/dist/src/commands/task-run-driver.js +116 -1961
  84. package/dist/src/commands/task.d.ts +35 -0
  85. package/dist/src/commands/task.js +676 -1201
  86. package/dist/src/commands/test.d.ts +3 -0
  87. package/dist/src/commands/test.js +15 -8
  88. package/dist/src/commands/triage.d.ts +11 -0
  89. package/dist/src/commands/triage.js +227 -0
  90. package/dist/src/commands/workspace.d.ts +3 -0
  91. package/dist/src/commands/workspace.js +18 -11
  92. package/dist/src/commands.d.ts +13 -0
  93. package/dist/src/commands.js +16792 -8055
  94. package/dist/src/index.d.ts +4 -0
  95. package/dist/src/index.js +16862 -8450
  96. package/dist/src/launcher.d.ts +61 -0
  97. package/dist/src/launcher.js +81 -13
  98. package/dist/src/operator-cli.d.ts +2 -0
  99. package/dist/src/operator-cli.js +17837 -0
  100. package/dist/src/operator-entry.d.ts +1 -0
  101. package/dist/src/operator-entry.js +3 -0
  102. package/dist/src/runner.d.ts +47 -0
  103. package/dist/src/runner.js +16 -22
  104. package/dist/src/withMutedConsole.d.ts +2 -0
  105. package/package.json +21 -8
  106. package/dist/src/commands/_authority-runs.js +0 -111
  107. package/dist/src/commands/_operator-view.js +0 -340
  108. package/dist/src/commands/_preflight.js +0 -478
  109. package/dist/src/commands/_server-client.js +0 -382
  110. package/dist/src/commands/_snapshot-upload.js +0 -331
  111. package/dist/src/commands/_task-picker.js +0 -48
  112. package/dist/src/commands/browser.js +0 -890
  113. package/dist/src/commands/connect.js +0 -180
  114. package/dist/src/commands/inspector.js +0 -256
  115. package/dist/src/commands/task-report-bug.js +0 -1083
  116. package/dist/src/report-bug.js +0 -260
@@ -0,0 +1,60 @@
1
+ // @bun
2
+ // packages/cli/src/commands/_json-output.ts
3
+ import { Schema } from "effect";
4
+ import {
5
+ RigDoctorCheckOutput,
6
+ RigInboxApprovalsOutput,
7
+ RigInboxInputsOutput,
8
+ RigRunListOutput,
9
+ RigRunShowOutput,
10
+ RigServerRepairLinkOutput,
11
+ RigServerStatusOutput,
12
+ RigServerUseOutput,
13
+ RigStatsOutput,
14
+ RigTaskListOutput,
15
+ RigTaskShowOutput
16
+ } from "@rig/contracts";
17
+ var CLI_OUTPUT_SCHEMAS = {
18
+ "task list": RigTaskListOutput,
19
+ "task show": RigTaskShowOutput,
20
+ "run list": RigRunListOutput,
21
+ "run show": RigRunShowOutput,
22
+ "server use": RigServerUseOutput,
23
+ "server status": RigServerStatusOutput,
24
+ "server repair-link": RigServerRepairLinkOutput,
25
+ "inbox approvals": RigInboxApprovalsOutput,
26
+ "inbox inputs": RigInboxInputsOutput,
27
+ "doctor check": RigDoctorCheckOutput,
28
+ "stats show": RigStatsOutput
29
+ };
30
+ function isCommandOutcomeLike(value) {
31
+ if (!value || typeof value !== "object" || Array.isArray(value))
32
+ return false;
33
+ const record = value;
34
+ return typeof record.group === "string" && typeof record.command === "string";
35
+ }
36
+ function buildCliJsonEnvelope(outcome, options = {}) {
37
+ if (!isCommandOutcomeLike(outcome))
38
+ return null;
39
+ const commandKey = `${outcome.group} ${outcome.command}`;
40
+ const schema = CLI_OUTPUT_SCHEMAS[commandKey];
41
+ if (!schema)
42
+ return null;
43
+ const warn = options.warn ?? ((message) => console.error(message));
44
+ const envelope = { v: 1, command: commandKey, data: outcome.details ?? {} };
45
+ try {
46
+ Schema.decodeUnknownSync(schema)(JSON.parse(JSON.stringify(envelope)));
47
+ return envelope;
48
+ } catch (error) {
49
+ warn(`[rig] --json output for "${commandKey}" failed schema validation; printing legacy payload. ` + `(${error instanceof Error ? error.message.split(`
50
+ `)[0] : String(error)})`);
51
+ return null;
52
+ }
53
+ }
54
+ function listSchematizedCliCommands() {
55
+ return Object.keys(CLI_OUTPUT_SCHEMAS).sort();
56
+ }
57
+ export {
58
+ listSchematizedCliCommands,
59
+ buildCliJsonEnvelope
60
+ };
@@ -0,0 +1,34 @@
1
+ import { SessionManager } from "@oh-my-pi/pi-coding-agent";
2
+ import { type CollabRegistryFilter, type CollabSessionProjection } from "@oh-my-pi/pi-coding-agent/collab/api";
3
+ import { type RunJournalProjection, type RunSessionCustomEntry } from "@rig/contracts";
4
+ type SessionWriter = {
5
+ appendCustomEntry(customType: string, data: unknown): void;
6
+ };
7
+ export type CollabSessionProjectionLike = Pick<CollabSessionProjection, "collab" | "customEntries" | "source"> & {
8
+ readonly runId?: string;
9
+ readonly session: Pick<CollabSessionProjection["session"], "path" | "id">;
10
+ };
11
+ export type ProjectRunProjection = Pick<RunJournalProjection, "status">;
12
+ export type ProjectRunFromSession = (source: Pick<SessionManager, "getEntries"> | ReadonlyArray<RunSessionCustomEntry> | readonly unknown[], runId: string) => ProjectRunProjection;
13
+ export type LazyReconcileFlippedRun = {
14
+ readonly runId: string;
15
+ readonly sessionPath: string;
16
+ readonly reason: "lazy-reconcile:dead-pid";
17
+ };
18
+ export type LazyReconcileResult = {
19
+ readonly flipped: readonly LazyReconcileFlippedRun[];
20
+ readonly resumable: readonly LazyReconcileFlippedRun[];
21
+ };
22
+ type LazyReconcileDeps = {
23
+ readonly listCollabSessionProjections?: (filter: CollabRegistryFilter) => Promise<readonly CollabSessionProjectionLike[]>;
24
+ readonly projectRunFromSession?: ProjectRunFromSession;
25
+ readonly processExists?: (pid: number) => boolean;
26
+ readonly openSession?: (sessionPath: string) => Promise<SessionWriter>;
27
+ readonly nowIso?: () => string;
28
+ };
29
+ export type LazyReconcileInput = {
30
+ readonly workspaceRoot: string;
31
+ readonly identityFilter: CollabRegistryFilter;
32
+ };
33
+ export declare function runLazyReconcile(input: LazyReconcileInput, deps?: LazyReconcileDeps): Promise<LazyReconcileResult>;
34
+ export {};
@@ -0,0 +1,102 @@
1
+ // @bun
2
+ var __require = import.meta.require;
3
+
4
+ // packages/cli/src/commands/_lazy-reconcile.ts
5
+ import { SessionManager } from "@oh-my-pi/pi-coding-agent";
6
+ import {
7
+ listCollabSessionProjections as ompListCollabSessionProjections
8
+ } from "@oh-my-pi/pi-coding-agent/collab/api";
9
+ import {
10
+ CUSTOM_TYPE_FOR,
11
+ isTerminalRunStatus
12
+ } from "@rig/contracts";
13
+ var ACTIVE_LOCAL_RUN_STATUSES = new Set([
14
+ "created",
15
+ "preparing",
16
+ "running",
17
+ "validating",
18
+ "reviewing",
19
+ "closing-out"
20
+ ]);
21
+ function processExists(pid) {
22
+ if (!Number.isInteger(pid) || pid <= 0)
23
+ return false;
24
+ try {
25
+ process.kill(pid, 0);
26
+ return true;
27
+ } catch {
28
+ return false;
29
+ }
30
+ }
31
+ async function defaultProjectRunFromSession(source, runId) {
32
+ const mod = await import("@rig/runtime/control-plane/run-session-projection");
33
+ const candidate = Reflect.get(mod, "projectRunFromSession");
34
+ if (typeof candidate !== "function") {
35
+ throw new Error("@rig/runtime/control-plane/run-session-projection does not export projectRunFromSession yet; inject projectRunFromSession until RunState lands it.");
36
+ }
37
+ return candidate(source, runId);
38
+ }
39
+ async function defaultOpenSession(sessionPath) {
40
+ return await SessionManager.open(sessionPath, undefined, undefined, { suppressBreadcrumb: true });
41
+ }
42
+ function projectionRunId(item) {
43
+ if (item.runId?.trim())
44
+ return item.runId.trim();
45
+ if (item.collab?.sessionId?.trim())
46
+ return item.collab.sessionId.trim();
47
+ if (item.session.id?.trim())
48
+ return item.session.id.trim();
49
+ return null;
50
+ }
51
+ function isLocallyDiscovered(item) {
52
+ return item.source === "session-list";
53
+ }
54
+ function isActiveStatus(status) {
55
+ if (status === null)
56
+ return false;
57
+ return !isTerminalRunStatus(status);
58
+ }
59
+ function shouldFlip(item, status, exists) {
60
+ if (!isActiveStatus(status))
61
+ return false;
62
+ if (item.collab?.stale === true)
63
+ return true;
64
+ const pid = item.collab?.pid;
65
+ return isLocallyDiscovered(item) && pid !== undefined && ACTIVE_LOCAL_RUN_STATUSES.has(status) && !exists(pid);
66
+ }
67
+ function failedStatusData(status, at) {
68
+ return {
69
+ type: "status-changed",
70
+ from: status,
71
+ to: "failed",
72
+ reason: "lazy-reconcile:dead-pid",
73
+ actor: { kind: "agent" },
74
+ at
75
+ };
76
+ }
77
+ async function runLazyReconcile(input, deps = {}) {
78
+ const listProjections = deps.listCollabSessionProjections ?? ompListCollabSessionProjections;
79
+ const projectRun = deps.projectRunFromSession ?? defaultProjectRunFromSession;
80
+ const exists = deps.processExists ?? processExists;
81
+ const openSession = deps.openSession ?? defaultOpenSession;
82
+ const nowIso = deps.nowIso ?? (() => new Date().toISOString());
83
+ const projections = await listProjections(input.identityFilter);
84
+ const flipped = [];
85
+ for (const item of projections) {
86
+ const runId = projectionRunId(item);
87
+ const sessionPath = item.session.path || item.collab?.sessionPath || "";
88
+ if (!runId || !sessionPath)
89
+ continue;
90
+ const projection = await projectRun(item.customEntries, runId);
91
+ const status = projection.status;
92
+ if (status === null || !shouldFlip(item, status, exists))
93
+ continue;
94
+ const session = await openSession(sessionPath);
95
+ session.appendCustomEntry(CUSTOM_TYPE_FOR["status-changed"], failedStatusData(status, nowIso()));
96
+ flipped.push({ runId, sessionPath, reason: "lazy-reconcile:dead-pid" });
97
+ }
98
+ return { flipped, resumable: flipped };
99
+ }
100
+ export {
101
+ runLazyReconcile
102
+ };
@@ -0,0 +1,15 @@
1
+ import type { TaskSourceConfig } from "@rig/contracts";
2
+ import type { RigPluginWithRuntime } from "@rig/core";
3
+ export declare function parsePositiveInt(value: string | undefined, option: string, fallback: number): number;
4
+ export declare function parseOptionalPositiveInt(value: string | undefined, option: string): number | undefined;
5
+ export declare function parseRequiredPositiveInt(value: string | undefined, option: string): number;
6
+ export declare function parseAction(value: string | undefined): "validate" | "verify" | "pipeline";
7
+ export declare function parseIsolationMode(value: string | undefined, allowOff: true): "off" | "worktree";
8
+ export declare function parseIsolationMode(value: string | undefined, allowOff: false): "worktree";
9
+ export declare function parseInstallScope(value: string | undefined): "user" | "system";
10
+ export declare function resolveInstallDir(scope: "user" | "system", explicitPath: string | undefined): string;
11
+ export type CliRigConfig = {
12
+ plugins?: readonly RigPluginWithRuntime[];
13
+ taskSource?: TaskSourceConfig;
14
+ };
15
+ export declare function loadRigConfigOrNull(projectRoot: string): Promise<CliRigConfig | null>;
@@ -7,12 +7,19 @@ import { resolve } from "path";
7
7
 
8
8
  // packages/cli/src/runner.ts
9
9
  import { EventBus } from "@rig/runtime/control-plane/runtime/events";
10
- import { CliError } from "@rig/runtime/control-plane/errors";
10
+ import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
11
11
  import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
12
- import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
13
- import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
14
12
  import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
15
- import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
13
+
14
+ class CliError extends RuntimeCliError {
15
+ hint;
16
+ constructor(message, exitCode = 1, options = {}) {
17
+ super(message, exitCode);
18
+ if (options.hint?.trim()) {
19
+ this.hint = options.hint.trim();
20
+ }
21
+ }
22
+ }
16
23
 
17
24
  // packages/cli/src/commands/_parsers.ts
18
25
  function parsePositiveInt(value, option, fallback) {
@@ -21,7 +28,7 @@ function parsePositiveInt(value, option, fallback) {
21
28
  }
22
29
  const parsed = Number.parseInt(value, 10);
23
30
  if (!Number.isFinite(parsed) || parsed <= 0) {
24
- throw new CliError2(`Invalid ${option} value: ${value}`);
31
+ throw new CliError(`Invalid ${option} value: ${value}`, 1, { hint: `Pass a positive integer, e.g. \`${option} 10\`.` });
25
32
  }
26
33
  return parsed;
27
34
  }
@@ -31,17 +38,17 @@ function parseOptionalPositiveInt(value, option) {
31
38
  }
32
39
  const parsed = Number.parseInt(value, 10);
33
40
  if (!Number.isFinite(parsed) || parsed <= 0) {
34
- throw new CliError2(`Invalid ${option} value: ${value}`);
41
+ throw new CliError(`Invalid ${option} value: ${value}`, 1, { hint: `Pass a positive integer, e.g. \`${option} 10\`.` });
35
42
  }
36
43
  return parsed;
37
44
  }
38
45
  function parseRequiredPositiveInt(value, option) {
39
46
  if (!value) {
40
- throw new CliError2(`Missing value for ${option}.`);
47
+ throw new CliError(`Missing value for ${option}.`, 1, { hint: `Provide a value after ${option}, e.g. \`${option} 10\`.` });
41
48
  }
42
49
  const parsed = Number.parseInt(value, 10);
43
50
  if (!Number.isFinite(parsed) || parsed <= 0) {
44
- throw new CliError2(`Invalid ${option} value: ${value}`);
51
+ throw new CliError(`Invalid ${option} value: ${value}`, 1, { hint: `Pass a positive integer, e.g. \`${option} 10\`.` });
45
52
  }
46
53
  return parsed;
47
54
  }
@@ -55,7 +62,7 @@ function parseAction(value) {
55
62
  if (value === "pipeline") {
56
63
  return "pipeline";
57
64
  }
58
- throw new CliError2(`Invalid --action value: ${value}. Use validate, verify, or pipeline.`);
65
+ throw new CliError(`Invalid --action value: ${value}. Use validate, verify, or pipeline.`);
59
66
  }
60
67
  function parseIsolationMode(value, allowOff) {
61
68
  if (!value) {
@@ -67,7 +74,7 @@ function parseIsolationMode(value, allowOff) {
67
74
  if (allowOff && value === "off") {
68
75
  return value;
69
76
  }
70
- throw new CliError2(`Invalid isolation mode: ${value}. Use ${allowOff ? "off|" : ""}worktree.`);
77
+ throw new CliError(`Invalid isolation mode: ${value}. Use ${allowOff ? "off|" : ""}worktree.`);
71
78
  }
72
79
  function parseInstallScope(value) {
73
80
  if (!value || value === "user") {
@@ -76,7 +83,7 @@ function parseInstallScope(value) {
76
83
  if (value === "system") {
77
84
  return "system";
78
85
  }
79
- throw new CliError2(`Invalid --scope value: ${value}. Use user|system.`);
86
+ throw new CliError(`Invalid --scope value: ${value}. Use user|system.`);
80
87
  }
81
88
  function resolveInstallDir(scope, explicitPath) {
82
89
  if (explicitPath) {
@@ -0,0 +1,11 @@
1
+ export declare function resolveControlPlaneMonorepoRoot(projectRoot: string): string;
2
+ export declare function resolveControlPlaneTaskConfigPath(projectRoot: string): string;
3
+ export declare function resolveControlPlaneHostStateRoot(projectRoot: string): string;
4
+ export declare function resolveControlPlaneHostStateDir(projectRoot: string): string;
5
+ export declare function resolveControlPlaneHostLogsDir(projectRoot: string): string;
6
+ export declare function resolveControlPlaneHostBinDir(projectRoot: string): string;
7
+ export declare function resolveControlPlaneHostDistDir(projectRoot: string): string;
8
+ export declare function resolveControlPlaneMonorepoStateRoot(projectRoot: string): string;
9
+ export declare function resolveControlPlaneMonorepoRuntimeDir(projectRoot: string): string;
10
+ export declare function resolveControlPlaneArtifactsDir(projectRoot: string): string;
11
+ export declare function resolveControlPlaneDefinitionRoot(projectRoot: string): string;
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  // packages/cli/src/commands/_paths.ts
3
3
  import { resolve } from "path";
4
- import { resolveMonorepoRoot } from "@rig/runtime/control-plane/native/utils";
4
+ import { resolveMonorepoRoot } from "@rig/runtime/layout";
5
5
  function resolveControlPlaneMonorepoRoot(projectRoot) {
6
6
  return resolveMonorepoRoot(projectRoot);
7
7
  }
@@ -0,0 +1,35 @@
1
+ import type { RunnerContext } from "../runner";
2
+ type AttachResult = {
3
+ readonly run: Record<string, unknown>;
4
+ readonly logs: Record<string, unknown>[];
5
+ readonly timeline: Record<string, unknown>[];
6
+ readonly timelineCursor: string | null;
7
+ readonly steered: boolean;
8
+ readonly rendered: string;
9
+ readonly detached?: boolean;
10
+ };
11
+ export declare function resolveOperatorDroneCwd(runId: string, homeDir?: string): string;
12
+ /**
13
+ * Compose the operator-session environment used by the legacy interactive
14
+ * attach shim. Exported for tests that pin the env contract.
15
+ */
16
+ export declare function buildOperatorPiEnv(input: {
17
+ readonly runId: string;
18
+ readonly serverUrl: string;
19
+ readonly authToken?: string | null;
20
+ readonly serverProjectRoot?: string | null;
21
+ readonly sessionDir: string;
22
+ }): Record<string, string>;
23
+ export declare function shouldRequireOperatorTranscript(status: unknown): boolean;
24
+ export declare function missingOperatorTranscriptMessage(runId: string, status: unknown): string;
25
+ /**
26
+ * Run `body` in-process while RETURNING (instead of terminating) when it calls
27
+ * `process.exit`. Exported for unit tests that pin the guard semantics.
28
+ */
29
+ export declare function runWithProcessExitGuard(body: () => Promise<unknown>): Promise<number>;
30
+ export declare function attachRunBundledPiFrontend(context: Pick<RunnerContext, "projectRoot" | "outputMode">, input: {
31
+ readonly runId: string;
32
+ readonly steered?: boolean;
33
+ readonly returnOnQuit?: boolean;
34
+ }): Promise<AttachResult>;
35
+ export {};
@@ -0,0 +1,64 @@
1
+ // @bun
2
+ // packages/cli/src/commands/_pi-frontend.ts
3
+ import { homedir } from "os";
4
+ import { join } from "path";
5
+ import { assertPathInsideRoot, assertSafeRunId, safePathSegment } from "@rig/shared/safe-identifiers";
6
+ var TERMINAL_RUN_STATUSES = { completed: true, failed: true, stopped: true, cancelled: true, canceled: true, closed: true, merged: true, needs_attention: true, "needs-attention": true };
7
+ function resolveOperatorDroneCwd(runId, homeDir = homedir()) {
8
+ const safeRunId = assertSafeRunId(runId);
9
+ const dronesRoot = join(homeDir, ".rig", "drones");
10
+ return assertPathInsideRoot(dronesRoot, join(dronesRoot, safePathSegment(String(safeRunId).slice(0, 8), { fallback: "run", maxLength: 32 })), "operator drone cwd");
11
+ }
12
+ function buildOperatorPiEnv(input) {
13
+ return {
14
+ PI_CODING_AGENT_SESSION_DIR: input.sessionDir,
15
+ PI_SKIP_VERSION_CHECK: "1",
16
+ PI_HIDDEN_COMMANDS: "import,fork,clone,tree,new,resume,trust",
17
+ RIG_PI_OPERATOR_SESSION: "1",
18
+ RIG_RUN_ID: input.runId,
19
+ RIG_SERVER_URL: input.serverUrl,
20
+ ...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
21
+ ...input.serverProjectRoot ? { RIG_PROJECT_ROOT: input.serverProjectRoot } : {}
22
+ };
23
+ }
24
+ function shouldRequireOperatorTranscript(status) {
25
+ return typeof status === "string" && TERMINAL_RUN_STATUSES[status.trim().toLowerCase()] === true;
26
+ }
27
+ function missingOperatorTranscriptMessage(runId, status) {
28
+ const shortRun = runId.trim().slice(0, 8) || "unknown";
29
+ const statusText = typeof status === "string" && status.trim() ? status.trim() : "terminal";
30
+ return `Run ${shortRun} is ${statusText}, but no worker Pi session transcript is recorded for this run. It likely failed before Pi started or predates full-session capture. Use \`rig run show ${runId}\` and \`rig run logs ${runId}\` for the lifecycle details.`;
31
+ }
32
+ async function runWithProcessExitGuard(body) {
33
+ const realExit = process.exit;
34
+ let exitCode = 0;
35
+ let signalQuit = () => {};
36
+ const quit = new Promise((resolve) => {
37
+ signalQuit = resolve;
38
+ });
39
+ const guardedExit = (code) => {
40
+ exitCode = typeof code === "number" ? code : 0;
41
+ signalQuit();
42
+ return;
43
+ };
44
+ process.exit = guardedExit;
45
+ try {
46
+ await Promise.race([Promise.resolve().then(body), quit]);
47
+ } finally {
48
+ process.exit = realExit;
49
+ }
50
+ return exitCode;
51
+ }
52
+ async function attachRunBundledPiFrontend(context, input) {
53
+ input.steered;
54
+ input.returnOnQuit;
55
+ throw new Error(`Interactive Pi attach for run ${input.runId} was removed in the OMP-collab cutover. Run bare \`rig\` and use the OMP cockpit run detail for the live session; legacy automation can request a non-interactive text snapshot with --once or --json.`);
56
+ }
57
+ export {
58
+ shouldRequireOperatorTranscript,
59
+ runWithProcessExitGuard,
60
+ resolveOperatorDroneCwd,
61
+ missingOperatorTranscriptMessage,
62
+ buildOperatorPiEnv,
63
+ attachRunBundledPiFrontend
64
+ };
@@ -0,0 +1,42 @@
1
+ export type PiInstallCheck = {
2
+ readonly ok: boolean;
3
+ readonly label: string;
4
+ readonly detail?: string;
5
+ readonly hint?: string;
6
+ };
7
+ export type CommandRunner = (command: string[], options?: {
8
+ cwd?: string;
9
+ }) => Promise<{
10
+ exitCode: number;
11
+ stdout: string;
12
+ stderr: string;
13
+ }>;
14
+ export type PiRigInstallStatus = {
15
+ readonly pi: PiInstallCheck;
16
+ readonly piRig: PiInstallCheck;
17
+ readonly extensionPath: string;
18
+ };
19
+ export declare function resolvePiRigExtensionPath(homeDir: string): string;
20
+ export declare function resolvePiRigPackageSource(projectRoot: string, exists?: (path: string) => boolean): string;
21
+ export declare function checkPiRigInstall(input?: {
22
+ readonly homeDir?: string;
23
+ readonly commandRunner?: CommandRunner;
24
+ readonly exists?: (path: string) => boolean;
25
+ }): Promise<PiRigInstallStatus>;
26
+ export declare function ensurePiRigInstalled(input: {
27
+ readonly projectRoot: string;
28
+ readonly homeDir?: string;
29
+ readonly commandRunner?: CommandRunner;
30
+ }): Promise<PiRigInstallStatus & {
31
+ installedPath: string;
32
+ }>;
33
+ export declare function ensureRemotePiRigInstalled(input: {
34
+ readonly requestJson: (pathname: string, init?: RequestInit) => Promise<unknown>;
35
+ }): Promise<PiRigInstallStatus & {
36
+ readonly remote: true;
37
+ }>;
38
+ export declare function buildPiSetupChecks(input?: {
39
+ readonly homeDir?: string;
40
+ readonly commandRunner?: CommandRunner;
41
+ readonly exists?: (path: string) => boolean;
42
+ }): Promise<PiInstallCheck[]>;
@@ -1,12 +1,9 @@
1
1
  // @bun
2
2
  // packages/cli/src/commands/_pi-install.ts
3
- import { existsSync, readFileSync, rmSync } from "fs";
3
+ import { existsSync } from "fs";
4
4
  import { homedir } from "os";
5
5
  import { resolve } from "path";
6
- var PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
7
- var LEGACY_PI_RIG_MARKER = `// Managed by Rig. Source package: @rig/pi-rig.
8
- export { default } from '@rig/pi-rig';
9
- `;
6
+ var PI_RIG_PACKAGE_NAME = "@h-rig/pi-rig";
10
7
  async function defaultCommandRunner(command, options = {}) {
11
8
  const proc = Bun.spawn(command, { cwd: options.cwd, stdout: "pipe", stderr: "pipe" });
12
9
  const [stdout, stderr, exitCode] = await Promise.all([
@@ -55,7 +52,13 @@ async function ensurePiBinaryAvailable(input) {
55
52
  }
56
53
  return { ok: true, detail: (current.stdout || current.stderr).trim() || undefined };
57
54
  }
58
- const installCommand = process.env.RIG_PI_INSTALL_COMMAND?.trim() || "bunx @earendil-works/pi@latest install";
55
+ const installCommand = process.env.RIG_PI_INSTALL_COMMAND?.trim();
56
+ if (!installCommand) {
57
+ return {
58
+ ok: false,
59
+ error: `${(current.stderr || current.stdout).trim() || "pi --version failed"}. Set RIG_PI_INSTALL_COMMAND to a supported installer or install Pi manually.`
60
+ };
61
+ }
59
62
  const parts = splitInstallCommand(installCommand);
60
63
  if (parts.length === 0) {
61
64
  return { ok: false, error: (current.stderr || current.stdout).trim() || "pi --version failed" };
@@ -72,18 +75,6 @@ async function ensurePiBinaryAvailable(input) {
72
75
  ...next.exitCode === 0 ? {} : { error: (next.stderr || next.stdout).trim() || "pi --version failed after install" }
73
76
  };
74
77
  }
75
- function removeManagedLegacyPiRigBridge(homeDir, exists = existsSync) {
76
- const extensionPath = resolvePiRigExtensionPath(homeDir);
77
- const indexPath = resolve(extensionPath, "index.ts");
78
- if (!exists(indexPath))
79
- return;
80
- try {
81
- const content = readFileSync(indexPath, "utf8");
82
- if (content === LEGACY_PI_RIG_MARKER || content.includes("Managed by Rig. Source package: @rig/pi-rig")) {
83
- rmSync(extensionPath, { recursive: true, force: true });
84
- }
85
- } catch {}
86
- }
87
78
  async function checkPiRigInstall(input = {}) {
88
79
  const home = resolvePiHomeDir(input.homeDir);
89
80
  const extensionPath = resolvePiRigExtensionPath(home);
@@ -94,48 +85,40 @@ async function checkPiRigInstall(input = {}) {
94
85
  piRig: { ok: true, label: "pi-rig global extension", detail: extensionPath }
95
86
  };
96
87
  }
97
- const exists = input.exists ?? existsSync;
98
88
  const runner = input.commandRunner ?? defaultCommandRunner;
99
89
  const piResult = await safeRun(runner, ["pi", "--version"]);
100
90
  const piListResult = piResult.exitCode === 0 ? await safeRun(runner, ["pi", "list"]) : { exitCode: 1, stdout: "", stderr: "" };
101
- const listedPiRig = piListResult.exitCode === 0 && piListContainsPiRig(`${piListResult.stdout}
91
+ const hasPiRig = piListResult.exitCode === 0 && piListContainsPiRig(`${piListResult.stdout}
102
92
  ${piListResult.stderr}`);
103
- const legacyBridge = exists(resolve(extensionPath, "index.ts"));
104
- const hasPiRig = listedPiRig;
93
+ const hasLegacyBridgeScaffold = !hasPiRig && (input.exists ?? existsSync)(extensionPath);
105
94
  return {
106
95
  extensionPath,
107
96
  pi: {
108
97
  ok: piResult.exitCode === 0,
109
98
  label: "pi",
110
99
  detail: (piResult.stdout || piResult.stderr).trim() || undefined,
111
- hint: piResult.exitCode === 0 ? undefined : "Install Pi or run `rig init --yes` to install/update the Pi runtime."
100
+ hint: piResult.exitCode === 0 ? undefined : "Install Pi/OMP manually or set RIG_PI_INSTALL_COMMAND before verifying with bare `rig` / Cockpit \u2192 Doctor."
112
101
  },
113
102
  piRig: {
114
103
  ok: hasPiRig,
115
104
  label: "pi-rig global extension",
116
- detail: hasPiRig ? piListResult.stdout.trim() || PI_RIG_PACKAGE_NAME : legacyBridge ? `${extensionPath} (legacy bridge; reinstall required)` : undefined,
117
- hint: hasPiRig ? undefined : "Run `rig init --yes` to install/enable the global pi-rig package with `pi install`."
105
+ detail: hasPiRig ? piListResult.stdout.trim() || PI_RIG_PACKAGE_NAME : hasLegacyBridgeScaffold ? `legacy bridge scaffold at ${extensionPath}` : undefined,
106
+ hint: hasPiRig ? undefined : `Install the Rig OMP extension with \`pi install ${PI_RIG_PACKAGE_NAME}\`, then verify with bare \`rig\` / Cockpit \u2192 Doctor.`
118
107
  }
119
108
  };
120
109
  }
121
110
  async function ensurePiRigInstalled(input) {
122
111
  const home = resolvePiHomeDir(input.homeDir);
123
112
  if (process.env.RIG_TEST_FAKE_PI_INSTALL === "1") {
124
- const status2 = await checkPiRigInstall({ homeDir: home, commandRunner: input.commandRunner });
125
- return { ...status2, installedPath: status2.extensionPath };
113
+ const status = await checkPiRigInstall({ homeDir: home, commandRunner: input.commandRunner });
114
+ return { ...status, installedPath: status.extensionPath };
126
115
  }
127
116
  const runner = input.commandRunner ?? defaultCommandRunner;
128
117
  const piAvailable = await ensurePiBinaryAvailable({ runner, projectRoot: input.projectRoot });
129
- const status = piAvailable.ok ? await checkPiRigInstall({ homeDir: home, commandRunner: runner }) : {
130
- extensionPath: resolvePiRigExtensionPath(home),
131
- pi: { ok: false, label: "pi", detail: piAvailable.error, hint: "Install/update Pi with RIG_PI_INSTALL_COMMAND or install Pi manually." },
132
- piRig: { ok: false, label: "pi-rig global extension", hint: "Pi is required before pi-rig can be installed." }
133
- };
134
118
  if (!piAvailable.ok) {
135
119
  throw new Error(`Pi install/update failed: ${piAvailable.error ?? "pi unavailable"}`);
136
120
  }
137
121
  const packageSource = resolvePiRigPackageSource(input.projectRoot);
138
- removeManagedLegacyPiRigBridge(home);
139
122
  const install = await runner(["pi", "install", packageSource], { cwd: input.projectRoot });
140
123
  if (install.exitCode !== 0) {
141
124
  throw new Error(`pi-rig install failed: ${(install.stderr || install.stdout).trim() || `exit ${install.exitCode}`}`);
@@ -147,7 +130,7 @@ async function ensureRemotePiRigInstalled(input) {
147
130
  const payload = await input.requestJson("/api/pi-rig/install", {
148
131
  method: "POST",
149
132
  headers: { "content-type": "application/json" },
150
- body: JSON.stringify({ package: "@rig/pi-rig", scope: "global" })
133
+ body: JSON.stringify({ package: PI_RIG_PACKAGE_NAME, scope: "global" })
151
134
  });
152
135
  const record = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
153
136
  const piOk = record.piOk === true || record.ok === true;
@@ -160,13 +143,13 @@ async function ensureRemotePiRigInstalled(input) {
160
143
  ok: piOk,
161
144
  label: "pi",
162
145
  detail: typeof record.piVersion === "string" ? record.piVersion : undefined,
163
- hint: piOk ? undefined : "Install/update Pi on the selected remote Rig server."
146
+ hint: piOk ? undefined : "Install/update Pi on the selected remote control host, then verify with bare `rig` / Cockpit \u2192 Doctor."
164
147
  },
165
148
  piRig: {
166
149
  ok: piRigOk,
167
150
  label: "pi-rig global extension",
168
151
  detail: extensionPath,
169
- hint: piRigOk ? undefined : "Install/enable pi-rig on the selected remote Rig server."
152
+ hint: piRigOk ? undefined : `Install/enable the Rig OMP extension (${PI_RIG_PACKAGE_NAME}) on the selected remote control host, then verify with bare \`rig\` / Cockpit \u2192 Doctor.`
170
153
  }
171
154
  };
172
155
  }
@@ -0,0 +1,8 @@
1
+ import { type RunnerContext } from "../runner";
2
+ export type PolicyMode = "off" | "observe" | "enforce";
3
+ export declare function resolveEffectivePolicyMode(context: RunnerContext): PolicyMode;
4
+ export declare function appendControlledBashAudit(context: RunnerContext, mode: PolicyMode, command: string, args: string[], matchedRuleIds: string[], action?: "warn" | "blocked"): void;
5
+ export declare function enforceNativeCommandPolicy(context: RunnerContext, args: string[], options: {
6
+ commandPrefix: string;
7
+ writeAuditLog: boolean;
8
+ }): Promise<void>;