@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
@@ -1,12 +1,19 @@
1
1
  // @bun
2
2
  // packages/cli/src/runner.ts
3
3
  import { EventBus } from "@rig/runtime/control-plane/runtime/events";
4
- import { CliError } from "@rig/runtime/control-plane/errors";
4
+ import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
5
5
  import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
6
- import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
7
- import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
8
6
  import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
9
- import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
7
+
8
+ class CliError extends RuntimeCliError {
9
+ hint;
10
+ constructor(message, exitCode = 1, options = {}) {
11
+ super(message, exitCode);
12
+ if (options.hint?.trim()) {
13
+ this.hint = options.hint.trim();
14
+ }
15
+ }
16
+ }
10
17
  function takeFlag(args, flag) {
11
18
  const rest = [];
12
19
  let value = false;
@@ -27,7 +34,7 @@ function takeOption(args, option) {
27
34
  if (current === option) {
28
35
  const next = args[index + 1];
29
36
  if (!next || next.startsWith("-")) {
30
- throw new CliError(`Missing value for ${option}`);
37
+ throw new CliError(`Missing value for ${option}`, 1, { hint: `Provide a value after ${option}, e.g. \`${option} <value>\`.` });
31
38
  }
32
39
  value = next;
33
40
  index += 1;
@@ -53,20 +60,843 @@ Usage: ${usage}`);
53
60
  return taskId;
54
61
  }
55
62
 
56
- // packages/cli/src/commands/remote.ts
57
- import {
58
- doctorManagedRemoteEndpoints,
59
- listManagedRemoteEndpoints,
60
- matchesEventFilter,
61
- migrateManagedRemoteEndpoints,
62
- RemoteCliError,
63
- RemoteWsClient,
64
- removeManagedRemoteEndpoint,
65
- resolveRemoteEndpoint,
66
- summarizeMessage,
67
- updateManagedRemoteEndpointInAuthority,
68
- upsertManagedRemoteEndpoint
69
- } from "@rig/runtime/control-plane/remote";
63
+ // packages/runtime/src/control-plane/remote.ts
64
+ import { randomUUID } from "crypto";
65
+ import { chmodSync, existsSync as existsSync2, mkdirSync, readFileSync, writeFileSync } from "fs";
66
+ import { homedir } from "os";
67
+ import { dirname as dirname2, join, resolve as resolve2 } from "path";
68
+ import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
69
+
70
+ // packages/runtime/src/control-plane/server-paths.ts
71
+ import { existsSync } from "fs";
72
+ import { dirname, resolve, relative } from "path";
73
+ import { assertPathInsideRoot, assertSafeRunId, safePathSegment } from "@rig/shared/safe-identifiers";
74
+ function normalizeOptionalString(value) {
75
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
76
+ }
77
+ function resolveAuthorityPaths(projectRoot) {
78
+ const normalizedRoot = resolve(projectRoot);
79
+ const stateRoot = resolveAuthorityStateRoot(normalizedRoot);
80
+ const stateDir = resolveAuthorityStateDir(normalizedRoot);
81
+ return {
82
+ projectRoot: normalizedRoot,
83
+ harnessRoot: resolve(normalizedRoot, "rig"),
84
+ runsDir: resolve(stateRoot, "runs"),
85
+ remoteDir: resolve(stateRoot, "remote"),
86
+ stateDir,
87
+ remoteEndpointsPath: resolve(stateRoot, "remote", "endpoints.toml"),
88
+ remoteSecretsPath: resolve(stateDir, "remote-secrets.toml")
89
+ };
90
+ }
91
+ function resolveAuthorityStateRoot(projectRoot) {
92
+ const normalizedRoot = resolve(projectRoot);
93
+ const taskWorkspace = normalizeOptionalString(process.env.RIG_TASK_WORKSPACE);
94
+ if (taskWorkspace) {
95
+ return resolve(taskWorkspace, ".rig");
96
+ }
97
+ const stateDir = normalizeOptionalString(process.env.RIG_STATE_DIR);
98
+ if (stateDir) {
99
+ return dirname(resolve(stateDir));
100
+ }
101
+ const logsDir = normalizeOptionalString(process.env.RIG_LOGS_DIR);
102
+ if (logsDir) {
103
+ return dirname(resolve(logsDir));
104
+ }
105
+ const sessionFile = normalizeOptionalString(process.env.RIG_SESSION_FILE);
106
+ if (sessionFile) {
107
+ return dirname(dirname(resolve(sessionFile)));
108
+ }
109
+ const projectStateRoot = resolve(normalizedRoot, ".rig");
110
+ if (existsSync(projectStateRoot)) {
111
+ return projectStateRoot;
112
+ }
113
+ return resolve(normalizedRoot, ".rig");
114
+ }
115
+ function resolveAuthorityStateDir(projectRoot) {
116
+ const explicit = normalizeOptionalString(process.env.RIG_STATE_DIR);
117
+ if (explicit) {
118
+ return resolve(explicit);
119
+ }
120
+ return resolve(resolveAuthorityStateRoot(projectRoot), "state");
121
+ }
122
+
123
+ // packages/runtime/src/control-plane/remote.ts
124
+ var DEFAULT_REMOTE_PORT = 7890;
125
+ var DEFAULT_TIMEOUTS = {
126
+ connectMs: 5000,
127
+ authMs: 5000,
128
+ requestMs: 30000,
129
+ subscriptionMs: 1e4
130
+ };
131
+ var REMOTES_CONFIG_PATH = join(homedir(), ".config", "rig", "remotes.toml");
132
+
133
+ class RemoteCliError extends Error {
134
+ code;
135
+ exitCode;
136
+ details;
137
+ constructor(code, message, exitCode = 1, details) {
138
+ super(message);
139
+ this.name = "RemoteCliError";
140
+ this.code = code;
141
+ this.exitCode = exitCode;
142
+ this.details = details;
143
+ }
144
+ }
145
+ function loadRemotesConfig(configPath = REMOTES_CONFIG_PATH) {
146
+ if (!existsSync2(configPath)) {
147
+ return { version: 1, remotes: {} };
148
+ }
149
+ try {
150
+ const content = readFileSync(configPath, "utf-8");
151
+ if (!content.trim()) {
152
+ return { version: 1, remotes: {} };
153
+ }
154
+ const parsed = parseToml(content);
155
+ return {
156
+ version: parsed.version ?? 1,
157
+ remotes: parsed.remotes ?? {}
158
+ };
159
+ } catch (error) {
160
+ throw new RemoteCliError("RIG_REMOTE_CONFIG_PARSE_ERROR", `Failed to parse remotes config at ${configPath}: ${error instanceof Error ? error.message : String(error)}`, 2, { configPath });
161
+ }
162
+ }
163
+ function saveRemotesConfig(config, configPath = REMOTES_CONFIG_PATH) {
164
+ mkdirSync(dirname2(configPath), { recursive: true });
165
+ writeFileSync(configPath, `${stringifyToml(config).trimEnd()}
166
+ `, "utf-8");
167
+ try {
168
+ chmodSync(configPath, 384);
169
+ } catch {}
170
+ }
171
+ function readTomlFile(path, fallback) {
172
+ if (!existsSync2(path))
173
+ return fallback;
174
+ const raw = readFileSync(path, "utf8");
175
+ if (!raw.trim())
176
+ return fallback;
177
+ return parseToml(raw);
178
+ }
179
+ function writeTomlFile(path, value) {
180
+ mkdirSync(dirname2(path), { recursive: true });
181
+ writeFileSync(path, `${stringifyToml(value).trimEnd()}
182
+ `, "utf8");
183
+ }
184
+ function normalizeStringArray(value) {
185
+ if (!Array.isArray(value))
186
+ return [];
187
+ return value.map((entry) => normalizeString(entry)).filter((entry) => entry !== "");
188
+ }
189
+ function normalizePort(value) {
190
+ const port = typeof value === "number" ? value : Number(value);
191
+ return Number.isInteger(port) && port > 0 && port <= 65535 ? port : DEFAULT_REMOTE_PORT;
192
+ }
193
+ function loadRemoteEndpointsToml(projectRoot) {
194
+ const paths = resolveAuthorityPaths(projectRoot);
195
+ const parsed = readTomlFile(paths.remoteEndpointsPath, { version: 1, endpoints: {} });
196
+ return {
197
+ version: parsed.version ?? 1,
198
+ endpoints: parsed.endpoints ?? {}
199
+ };
200
+ }
201
+ function loadRemoteSecretsToml(projectRoot) {
202
+ const paths = resolveAuthorityPaths(projectRoot);
203
+ const parsed = readTomlFile(paths.remoteSecretsPath, { version: 1, secrets: {} });
204
+ return {
205
+ version: parsed.version ?? 1,
206
+ secrets: parsed.secrets ?? {}
207
+ };
208
+ }
209
+ function saveRemoteEndpointsToml(projectRoot, payload) {
210
+ writeTomlFile(resolveAuthorityPaths(projectRoot).remoteEndpointsPath, payload);
211
+ }
212
+ function saveRemoteSecretsToml(projectRoot, payload) {
213
+ const paths = resolveAuthorityPaths(projectRoot);
214
+ writeTomlFile(paths.remoteSecretsPath, payload);
215
+ try {
216
+ chmodSync(paths.remoteSecretsPath, 384);
217
+ } catch {}
218
+ }
219
+ function listAuthorityRemoteEndpoints(projectRoot) {
220
+ const endpoints = loadRemoteEndpointsToml(projectRoot).endpoints ?? {};
221
+ const secrets = loadRemoteSecretsToml(projectRoot).secrets ?? {};
222
+ return Object.values(endpoints).map((entry) => {
223
+ const token = secrets[entry.secret_ref] ?? "";
224
+ return {
225
+ id: entry.id,
226
+ alias: entry.alias,
227
+ host: entry.host,
228
+ port: normalizePort(entry.port),
229
+ token,
230
+ autoConnect: Boolean(entry.auto_connect),
231
+ addedAt: entry.created_at,
232
+ updatedAt: entry.updated_at,
233
+ lastConnectedAt: normalizeString(entry.last_connected_at) || null,
234
+ labels: normalizeStringArray(entry.labels),
235
+ capabilities: normalizeStringArray(entry.capabilities),
236
+ transport: normalizeString(entry.transport) || "websocket",
237
+ secretRef: entry.secret_ref
238
+ };
239
+ }).sort((left, right) => left.alias.localeCompare(right.alias));
240
+ }
241
+ function nextRemoteEndpointRecord(input) {
242
+ const timestamp = new Date().toISOString();
243
+ const secretRef = input.existing?.secret_ref ?? randomUUID();
244
+ return {
245
+ record: {
246
+ id: input.endpointId ?? input.existing?.id ?? randomUUID(),
247
+ alias: input.alias,
248
+ host: input.host,
249
+ port: normalizePort(input.port),
250
+ transport: normalizeString(input.transport) || normalizeString(input.existing?.transport) || "websocket",
251
+ auto_connect: input.autoConnect ?? input.existing?.auto_connect ?? false,
252
+ labels: input.labels ?? normalizeStringArray(input.existing?.labels),
253
+ capabilities: input.capabilities ?? normalizeStringArray(input.existing?.capabilities),
254
+ secret_ref: secretRef,
255
+ created_at: input.existing?.created_at ?? timestamp,
256
+ updated_at: timestamp,
257
+ last_connected_at: input.existing?.last_connected_at ?? null
258
+ },
259
+ secretRef
260
+ };
261
+ }
262
+ function upsertAuthorityRemoteEndpoint(projectRoot, input) {
263
+ const endpointsToml = loadRemoteEndpointsToml(projectRoot);
264
+ const secretsToml = loadRemoteSecretsToml(projectRoot);
265
+ const existing = Object.values(endpointsToml.endpoints ?? {}).find((entry) => entry.alias === input.alias) ?? null;
266
+ const { record, secretRef } = nextRemoteEndpointRecord({
267
+ endpointId: input.endpointId,
268
+ existing,
269
+ alias: input.alias,
270
+ host: input.host,
271
+ port: input.port,
272
+ token: input.token,
273
+ autoConnect: input.autoConnect,
274
+ transport: input.transport,
275
+ labels: input.labels,
276
+ capabilities: input.capabilities
277
+ });
278
+ endpointsToml.endpoints = {
279
+ ...endpointsToml.endpoints ?? {},
280
+ [record.id]: record
281
+ };
282
+ secretsToml.secrets = {
283
+ ...secretsToml.secrets ?? {},
284
+ [secretRef]: input.token
285
+ };
286
+ saveRemoteEndpointsToml(projectRoot, endpointsToml);
287
+ saveRemoteSecretsToml(projectRoot, secretsToml);
288
+ return listAuthorityRemoteEndpoints(projectRoot).find((entry) => entry.id === record.id);
289
+ }
290
+ function updateAuthorityRemoteEndpoint(projectRoot, input) {
291
+ const endpointsToml = loadRemoteEndpointsToml(projectRoot);
292
+ const secretsToml = loadRemoteSecretsToml(projectRoot);
293
+ const current = Object.values(endpointsToml.endpoints ?? {}).find((entry) => input.endpointId && entry.id === input.endpointId || input.alias && entry.alias === input.alias);
294
+ if (!current)
295
+ return null;
296
+ const record = {
297
+ ...current,
298
+ alias: normalizeString(input.alias) || current.alias,
299
+ host: normalizeString(input.host) || current.host,
300
+ port: input.port !== undefined ? normalizePort(input.port) : current.port,
301
+ auto_connect: input.autoConnect ?? current.auto_connect,
302
+ transport: normalizeString(input.transport) || current.transport,
303
+ labels: input.labels ?? normalizeStringArray(current.labels),
304
+ capabilities: input.capabilities ?? normalizeStringArray(current.capabilities),
305
+ updated_at: new Date().toISOString()
306
+ };
307
+ endpointsToml.endpoints = {
308
+ ...endpointsToml.endpoints ?? {},
309
+ [record.id]: record
310
+ };
311
+ if (input.token !== undefined) {
312
+ secretsToml.secrets = {
313
+ ...secretsToml.secrets ?? {},
314
+ [record.secret_ref]: input.token
315
+ };
316
+ }
317
+ saveRemoteEndpointsToml(projectRoot, endpointsToml);
318
+ saveRemoteSecretsToml(projectRoot, secretsToml);
319
+ return listAuthorityRemoteEndpoints(projectRoot).find((entry) => entry.id === record.id) ?? null;
320
+ }
321
+ function removeAuthorityRemoteEndpoint(projectRoot, endpointIdOrAlias) {
322
+ const endpointsToml = loadRemoteEndpointsToml(projectRoot);
323
+ const secretsToml = loadRemoteSecretsToml(projectRoot);
324
+ const entry = Object.values(endpointsToml.endpoints ?? {}).find((candidate) => candidate.id === endpointIdOrAlias || candidate.alias === endpointIdOrAlias);
325
+ if (!entry)
326
+ return false;
327
+ const nextEndpoints = { ...endpointsToml.endpoints ?? {} };
328
+ delete nextEndpoints[entry.id];
329
+ const nextSecrets = { ...secretsToml.secrets ?? {} };
330
+ delete nextSecrets[entry.secret_ref];
331
+ saveRemoteEndpointsToml(projectRoot, { version: endpointsToml.version ?? 1, endpoints: nextEndpoints });
332
+ saveRemoteSecretsToml(projectRoot, { version: secretsToml.version ?? 1, secrets: nextSecrets });
333
+ return true;
334
+ }
335
+ function markAuthorityRemoteEndpointConnected(projectRoot, endpointId, connectedAt = new Date().toISOString()) {
336
+ const endpointsToml = loadRemoteEndpointsToml(projectRoot);
337
+ const entry = endpointsToml.endpoints?.[endpointId];
338
+ if (!entry)
339
+ return;
340
+ endpointsToml.endpoints = {
341
+ ...endpointsToml.endpoints ?? {},
342
+ [endpointId]: {
343
+ ...entry,
344
+ last_connected_at: connectedAt,
345
+ updated_at: connectedAt
346
+ }
347
+ };
348
+ saveRemoteEndpointsToml(projectRoot, endpointsToml);
349
+ }
350
+ function importLegacyRemoteEndpoints(projectRoot, legacyPath = REMOTES_CONFIG_PATH) {
351
+ if (!existsSync2(legacyPath)) {
352
+ return { imported: 0, skipped: 0, sourcePath: legacyPath };
353
+ }
354
+ const parsed = readTomlFile(legacyPath, { version: 1, remotes: {} });
355
+ let imported = 0;
356
+ let skipped = 0;
357
+ for (const [alias, entry] of Object.entries(parsed.remotes ?? {})) {
358
+ const host = normalizeString(entry.host);
359
+ const token = normalizeString(entry.token);
360
+ if (!host || !token) {
361
+ skipped += 1;
362
+ continue;
363
+ }
364
+ upsertAuthorityRemoteEndpoint(projectRoot, {
365
+ alias,
366
+ host,
367
+ port: normalizePort(entry.port),
368
+ token
369
+ });
370
+ imported += 1;
371
+ }
372
+ return { imported, skipped, sourcePath: legacyPath };
373
+ }
374
+ function doctorAuthorityRemoteEndpoints(projectRoot) {
375
+ const paths = resolveAuthorityPaths(projectRoot);
376
+ const endpoints = loadRemoteEndpointsToml(projectRoot).endpoints ?? {};
377
+ const secrets = loadRemoteSecretsToml(projectRoot).secrets ?? {};
378
+ const missingSecrets = Object.values(endpoints).filter((entry) => !normalizeString(secrets[entry.secret_ref])).map((entry) => entry.alias);
379
+ const warnings = [];
380
+ if (!existsSync2(paths.remoteEndpointsPath)) {
381
+ warnings.push("Remote endpoint manifest is missing.");
382
+ }
383
+ if (!existsSync2(paths.remoteSecretsPath)) {
384
+ warnings.push("Remote secret store is missing.");
385
+ }
386
+ return {
387
+ manifestPath: paths.remoteEndpointsPath,
388
+ secretsPath: paths.remoteSecretsPath,
389
+ endpointCount: Object.keys(endpoints).length,
390
+ missingSecrets,
391
+ warnings
392
+ };
393
+ }
394
+ function listManagedRemoteEndpoints(configPath = REMOTES_CONFIG_PATH, projectRoot) {
395
+ if (projectRoot) {
396
+ return listAuthorityRemoteEndpoints(projectRoot).map((entry) => ({
397
+ id: entry.id,
398
+ alias: entry.alias,
399
+ host: entry.host,
400
+ port: entry.port,
401
+ token: entry.token,
402
+ addedAt: entry.addedAt,
403
+ lastConnected: entry.lastConnectedAt
404
+ }));
405
+ }
406
+ const config = loadRemotesConfig(configPath);
407
+ return Object.entries(config.remotes ?? {}).map(([alias, entry]) => ({
408
+ id: alias,
409
+ alias,
410
+ host: normalizeString(entry.host) || "",
411
+ port: Number.isFinite(entry.port) ? Number(entry.port) : DEFAULT_REMOTE_PORT,
412
+ token: normalizeString(entry.token) || "",
413
+ addedAt: normalizeString(entry.addedAt) || null,
414
+ lastConnected: normalizeString(entry.lastConnected) || null
415
+ })).sort((left, right) => left.alias.localeCompare(right.alias));
416
+ }
417
+ function upsertManagedRemoteEndpoint(input, configPath = REMOTES_CONFIG_PATH, projectRoot) {
418
+ if (projectRoot) {
419
+ const saved = upsertAuthorityRemoteEndpoint(projectRoot, {
420
+ ...input,
421
+ token: input.token ?? ""
422
+ });
423
+ return {
424
+ id: saved.id,
425
+ alias: saved.alias,
426
+ host: saved.host,
427
+ port: saved.port,
428
+ token: saved.token,
429
+ addedAt: saved.addedAt,
430
+ lastConnected: saved.lastConnectedAt
431
+ };
432
+ }
433
+ const config = loadRemotesConfig(configPath);
434
+ const existing = config.remotes?.[input.alias];
435
+ const nextEntry = {
436
+ host: input.host,
437
+ port: input.port,
438
+ token: input.token,
439
+ addedAt: normalizeString(existing?.addedAt) || new Date().toISOString(),
440
+ ...normalizeString(existing?.lastConnected) ? { lastConnected: normalizeString(existing?.lastConnected) } : {}
441
+ };
442
+ const nextConfig = {
443
+ version: config.version ?? 1,
444
+ remotes: {
445
+ ...config.remotes ?? {},
446
+ [input.alias]: nextEntry
447
+ }
448
+ };
449
+ saveRemotesConfig(nextConfig, configPath);
450
+ return {
451
+ id: input.alias,
452
+ alias: input.alias,
453
+ host: input.host,
454
+ port: input.port,
455
+ token: input.token ?? "",
456
+ addedAt: nextEntry.addedAt ?? null,
457
+ lastConnected: nextEntry.lastConnected ?? null
458
+ };
459
+ }
460
+ function removeManagedRemoteEndpoint(alias, configPath = REMOTES_CONFIG_PATH, projectRoot) {
461
+ if (projectRoot) {
462
+ return removeAuthorityRemoteEndpoint(projectRoot, alias);
463
+ }
464
+ const config = loadRemotesConfig(configPath);
465
+ if (!config.remotes?.[alias]) {
466
+ return false;
467
+ }
468
+ const nextRemotes = { ...config.remotes ?? {} };
469
+ delete nextRemotes[alias];
470
+ saveRemotesConfig({
471
+ version: config.version ?? 1,
472
+ remotes: nextRemotes
473
+ }, configPath);
474
+ return true;
475
+ }
476
+ function resolveRemoteEndpoint(options) {
477
+ const env = options.env ?? process.env;
478
+ const envHost = normalizeString(env.RIG_REMOTE_HOST);
479
+ const envToken = normalizeString(env.RIG_REMOTE_TOKEN);
480
+ const envPort = parsePort(normalizeString(env.RIG_REMOTE_PORT), "RIG_REMOTE_PORT");
481
+ const envAlias = normalizeString(env.RIG_REMOTE_ALIAS);
482
+ const explicitHost = normalizeString(options.host);
483
+ const explicitToken = normalizeString(options.token);
484
+ const explicitPort = parsePort(normalizeString(options.port), "--port");
485
+ const explicitProvided = Boolean(explicitHost || explicitToken || explicitPort !== null);
486
+ const requestedAlias = normalizeString(options.remoteAlias) || envAlias;
487
+ let endpoint = {
488
+ source: "env",
489
+ host: envHost || "",
490
+ port: envPort ?? DEFAULT_REMOTE_PORT,
491
+ token: envToken || "",
492
+ configPath: REMOTES_CONFIG_PATH
493
+ };
494
+ if (requestedAlias) {
495
+ if (options.projectRoot) {
496
+ const remote = listAuthorityRemoteEndpoints(options.projectRoot).find((entry) => entry.alias === requestedAlias);
497
+ if (!remote) {
498
+ const configPath = resolveAuthorityPaths(options.projectRoot).remoteEndpointsPath;
499
+ throw new RemoteCliError("RIG_REMOTE_ALIAS_NOT_FOUND", `Remote alias '${requestedAlias}' was not found in ${configPath}.`, 2, { alias: requestedAlias, configPath });
500
+ }
501
+ endpoint = {
502
+ source: "alias",
503
+ alias: requestedAlias,
504
+ host: remote.host,
505
+ port: remote.port,
506
+ token: remote.token,
507
+ configPath: resolveAuthorityPaths(options.projectRoot).remoteEndpointsPath
508
+ };
509
+ } else {
510
+ const config = loadRemotesConfig();
511
+ const remote = config.remotes?.[requestedAlias];
512
+ if (!remote) {
513
+ throw new RemoteCliError("RIG_REMOTE_ALIAS_NOT_FOUND", `Remote alias '${requestedAlias}' was not found in ${REMOTES_CONFIG_PATH}.`, 2, { alias: requestedAlias, configPath: REMOTES_CONFIG_PATH });
514
+ }
515
+ endpoint = {
516
+ source: "alias",
517
+ alias: requestedAlias,
518
+ host: normalizeString(remote.host) || "",
519
+ port: Number.isFinite(remote.port) ? Number(remote.port) : DEFAULT_REMOTE_PORT,
520
+ token: normalizeString(remote.token) || "",
521
+ configPath: REMOTES_CONFIG_PATH
522
+ };
523
+ }
524
+ }
525
+ if (explicitProvided) {
526
+ endpoint = {
527
+ ...endpoint,
528
+ source: "flags",
529
+ host: explicitHost || endpoint.host,
530
+ port: explicitPort ?? endpoint.port,
531
+ token: explicitToken || endpoint.token
532
+ };
533
+ }
534
+ if (!endpoint.host) {
535
+ throw new RemoteCliError("RIG_REMOTE_HOST_REQUIRED", "Remote host is required. Pass --host, --remote <alias>, or set RIG_REMOTE_HOST.", 2);
536
+ }
537
+ if (!Number.isFinite(endpoint.port) || endpoint.port <= 0 || endpoint.port > 65535) {
538
+ throw new RemoteCliError("RIG_REMOTE_INVALID_PORT", `Invalid remote port: ${endpoint.port}.`, 2);
539
+ }
540
+ return endpoint;
541
+ }
542
+
543
+ class RemoteWsClient {
544
+ endpoint;
545
+ ws = null;
546
+ connected = false;
547
+ pending = new Map;
548
+ onEvent;
549
+ requestTimeoutMs;
550
+ connectTimeoutMs;
551
+ authTimeoutMs;
552
+ subscriptionTimeoutMs;
553
+ connectionToken = null;
554
+ connectionTokenExpiresAt = null;
555
+ tokenRefreshTimer = null;
556
+ constructor(endpoint, options = {}) {
557
+ this.endpoint = endpoint;
558
+ this.onEvent = options.onEvent;
559
+ this.connectTimeoutMs = options.connectTimeoutMs ?? DEFAULT_TIMEOUTS.connectMs;
560
+ this.authTimeoutMs = options.authTimeoutMs ?? DEFAULT_TIMEOUTS.authMs;
561
+ this.requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_TIMEOUTS.requestMs;
562
+ this.subscriptionTimeoutMs = options.subscriptionTimeoutMs ?? DEFAULT_TIMEOUTS.subscriptionMs;
563
+ }
564
+ setEventHandler(handler) {
565
+ this.onEvent = handler;
566
+ }
567
+ async connect() {
568
+ if (this.connected) {
569
+ return;
570
+ }
571
+ const url = `ws://${this.endpoint.host}:${this.endpoint.port}`;
572
+ await new Promise((resolve3, reject) => {
573
+ let authResolved = false;
574
+ let connectTimer = setTimeout(() => {
575
+ connectTimer = null;
576
+ reject(new RemoteCliError("RIG_REMOTE_CONNECT_TIMEOUT", `Timed out connecting to ${url} after ${this.connectTimeoutMs}ms.`, 3, { host: this.endpoint.host, port: this.endpoint.port, timeoutMs: this.connectTimeoutMs }));
577
+ }, this.connectTimeoutMs);
578
+ const ws = new WebSocket(url);
579
+ this.ws = ws;
580
+ ws.onopen = () => {
581
+ const authMessage = {
582
+ type: "auth",
583
+ id: crypto.randomUUID(),
584
+ timestamp: new Date().toISOString(),
585
+ token: this.connectionToken || this.endpoint.token,
586
+ tokenType: this.connectionToken ? "connection" : "server"
587
+ };
588
+ let authTimer = setTimeout(() => {
589
+ authTimer = null;
590
+ reject(new RemoteCliError("RIG_REMOTE_AUTH_TIMEOUT", `Timed out waiting for auth response from ${url} after ${this.authTimeoutMs}ms.`, 3, { host: this.endpoint.host, port: this.endpoint.port, timeoutMs: this.authTimeoutMs }));
591
+ }, this.authTimeoutMs);
592
+ ws.send(JSON.stringify(authMessage));
593
+ const originalOnMessage = ws.onmessage;
594
+ ws.onmessage = (event) => {
595
+ const message = parseIncomingMessage(event.data);
596
+ if (!message) {
597
+ return;
598
+ }
599
+ if (message.type === "auth_response" && !authResolved) {
600
+ authResolved = true;
601
+ if (connectTimer) {
602
+ clearTimeout(connectTimer);
603
+ connectTimer = null;
604
+ }
605
+ if (authTimer) {
606
+ clearTimeout(authTimer);
607
+ authTimer = null;
608
+ }
609
+ const auth = message;
610
+ if (!auth.success) {
611
+ reject(new RemoteCliError("RIG_REMOTE_AUTH_FAILED", auth.error || "Remote authentication failed.", 3, { host: this.endpoint.host, port: this.endpoint.port }));
612
+ return;
613
+ }
614
+ if (auth.connectionToken && auth.connectionTokenExpiresAt) {
615
+ this.connectionToken = auth.connectionToken;
616
+ this.connectionTokenExpiresAt = auth.connectionTokenExpiresAt;
617
+ this.scheduleTokenRefresh();
618
+ }
619
+ if (this.endpoint.configPath.endsWith("endpoints.toml")) {
620
+ const projectRoot = dirname2(dirname2(dirname2(this.endpoint.configPath)));
621
+ try {
622
+ markAuthorityRemoteEndpointConnected(projectRoot, this.endpoint.alias ? listAuthorityRemoteEndpoints(projectRoot).find((entry) => entry.alias === this.endpoint.alias)?.id ?? "" : "");
623
+ } catch {}
624
+ }
625
+ this.connected = true;
626
+ ws.onmessage = (event2) => {
627
+ const next = parseIncomingMessage(event2.data);
628
+ if (!next) {
629
+ return;
630
+ }
631
+ this.routeMessage(next);
632
+ };
633
+ resolve3();
634
+ return;
635
+ }
636
+ if (typeof originalOnMessage === "function") {
637
+ originalOnMessage.call(ws, event);
638
+ }
639
+ };
640
+ };
641
+ ws.onerror = () => {
642
+ if (connectTimer) {
643
+ clearTimeout(connectTimer);
644
+ connectTimer = null;
645
+ }
646
+ if (!authResolved) {
647
+ reject(new RemoteCliError("RIG_REMOTE_CONNECT_FAILED", `Failed to connect to ${url}.`, 3, { host: this.endpoint.host, port: this.endpoint.port }));
648
+ }
649
+ };
650
+ ws.onclose = () => {
651
+ this.connected = false;
652
+ this.clearPending("Connection closed");
653
+ };
654
+ });
655
+ }
656
+ disconnect() {
657
+ this.connected = false;
658
+ this.clearTokenRefreshTimer();
659
+ this.clearPending("Connection closed");
660
+ if (this.ws) {
661
+ try {
662
+ this.ws.close();
663
+ } catch {}
664
+ this.ws = null;
665
+ }
666
+ }
667
+ async subscribe(eventTypes = []) {
668
+ const message = { type: "subscribe" };
669
+ if (eventTypes.length > 0) {
670
+ message.eventTypes = eventTypes;
671
+ }
672
+ const response = await this.request(message, this.subscriptionTimeoutMs);
673
+ return expectType(response, "operation_result");
674
+ }
675
+ async unsubscribe() {
676
+ return expectType(await this.request({ type: "unsubscribe" }), "operation_result");
677
+ }
678
+ async getState() {
679
+ return expectType(await this.request({ type: "get_state" }), "state_response");
680
+ }
681
+ async getTasks() {
682
+ return expectType(await this.request({ type: "get_tasks" }), "tasks_response");
683
+ }
684
+ async pause() {
685
+ return expectType(await this.request({ type: "pause" }), "operation_result");
686
+ }
687
+ async resume() {
688
+ return expectType(await this.request({ type: "resume" }), "operation_result");
689
+ }
690
+ async interrupt() {
691
+ return expectType(await this.request({ type: "interrupt" }), "operation_result");
692
+ }
693
+ async continueExecution() {
694
+ return expectType(await this.request({ type: "continue" }), "operation_result");
695
+ }
696
+ async refreshTasks() {
697
+ return expectType(await this.request({ type: "refresh_tasks" }), "operation_result");
698
+ }
699
+ async addIterations(count) {
700
+ return expectType(await this.request({ type: "add_iterations", count }), "operation_result");
701
+ }
702
+ async removeIterations(count) {
703
+ return expectType(await this.request({ type: "remove_iterations", count }), "operation_result");
704
+ }
705
+ async getPromptPreview(taskId) {
706
+ return expectType(await this.request({ type: "get_prompt_preview", taskId }), "prompt_preview_response");
707
+ }
708
+ async getIterationOutput(taskId) {
709
+ return expectType(await this.request({ type: "get_iteration_output", taskId }), "iteration_output_response");
710
+ }
711
+ async startOrchestration(options) {
712
+ return expectType(await this.request({
713
+ type: "orchestrate:start",
714
+ maxWorkers: options.maxWorkers,
715
+ maxIterations: options.maxIterations,
716
+ directMerge: options.directMerge
717
+ }), "orchestrate:start_response");
718
+ }
719
+ async pauseOrchestration(orchestrationId) {
720
+ return expectType(await this.request({ type: "orchestrate:pause", orchestrationId }), "operation_result");
721
+ }
722
+ async resumeOrchestration(orchestrationId) {
723
+ return expectType(await this.request({ type: "orchestrate:resume", orchestrationId }), "operation_result");
724
+ }
725
+ async stopOrchestration(orchestrationId) {
726
+ return expectType(await this.request({ type: "orchestrate:stop", orchestrationId }), "operation_result");
727
+ }
728
+ async getOrchestrationState(orchestrationId) {
729
+ return expectType(await this.request({ type: "orchestrate:get_state", orchestrationId }), "orchestrate:state_response");
730
+ }
731
+ async ping() {
732
+ return this.request({ type: "ping" });
733
+ }
734
+ async request(payload, timeoutMs = this.requestTimeoutMs) {
735
+ if (!this.connected || !this.ws) {
736
+ throw new RemoteCliError("RIG_REMOTE_NOT_CONNECTED", "Remote client is not connected.", 3);
737
+ }
738
+ const id = crypto.randomUUID();
739
+ const message = {
740
+ ...payload,
741
+ id,
742
+ timestamp: new Date().toISOString()
743
+ };
744
+ return await new Promise((resolve3, reject) => {
745
+ const timeout = setTimeout(() => {
746
+ this.pending.delete(id);
747
+ reject(new RemoteCliError("RIG_REMOTE_REQUEST_TIMEOUT", `Timed out waiting for response to '${String(payload.type)}' after ${timeoutMs}ms.`, 3, { requestType: payload.type, requestId: id, timeoutMs }));
748
+ }, timeoutMs);
749
+ this.pending.set(id, { resolve: resolve3, reject, timeout });
750
+ this.ws?.send(JSON.stringify(message));
751
+ });
752
+ }
753
+ routeMessage(message) {
754
+ if (message.id && this.pending.has(message.id)) {
755
+ const pending = this.pending.get(message.id);
756
+ if (pending) {
757
+ clearTimeout(pending.timeout);
758
+ this.pending.delete(message.id);
759
+ pending.resolve(message);
760
+ }
761
+ return;
762
+ }
763
+ if (message.type === "token_refresh_response") {
764
+ const token = normalizeString(message.connectionToken);
765
+ const expiresAt = normalizeString(message.connectionTokenExpiresAt);
766
+ if (token && expiresAt) {
767
+ this.connectionToken = token;
768
+ this.connectionTokenExpiresAt = expiresAt;
769
+ this.scheduleTokenRefresh();
770
+ }
771
+ }
772
+ this.onEvent?.({ receivedAt: new Date().toISOString(), message });
773
+ }
774
+ scheduleTokenRefresh() {
775
+ this.clearTokenRefreshTimer();
776
+ if (!this.connectionToken || !this.connectionTokenExpiresAt || !this.ws || !this.connected) {
777
+ return;
778
+ }
779
+ const expiresAt = new Date(this.connectionTokenExpiresAt).getTime();
780
+ if (!Number.isFinite(expiresAt)) {
781
+ return;
782
+ }
783
+ const refreshAt = expiresAt - 60 * 60 * 1000;
784
+ const delay = Math.max(0, refreshAt - Date.now());
785
+ this.tokenRefreshTimer = setTimeout(() => {
786
+ if (!this.connected || !this.ws || !this.connectionToken) {
787
+ return;
788
+ }
789
+ const refreshMessage = {
790
+ type: "token_refresh",
791
+ id: crypto.randomUUID(),
792
+ timestamp: new Date().toISOString(),
793
+ connectionToken: this.connectionToken
794
+ };
795
+ this.ws.send(JSON.stringify(refreshMessage));
796
+ }, delay);
797
+ }
798
+ clearTokenRefreshTimer() {
799
+ if (this.tokenRefreshTimer) {
800
+ clearTimeout(this.tokenRefreshTimer);
801
+ this.tokenRefreshTimer = null;
802
+ }
803
+ }
804
+ clearPending(reason) {
805
+ for (const [requestId, pending] of this.pending.entries()) {
806
+ clearTimeout(pending.timeout);
807
+ pending.reject(new RemoteCliError("RIG_REMOTE_REQUEST_CANCELLED", `${reason} (request ${requestId}).`, 3, { requestId }));
808
+ this.pending.delete(requestId);
809
+ }
810
+ }
811
+ }
812
+ function matchesEventFilter(message, expectedType) {
813
+ if (!expectedType) {
814
+ return true;
815
+ }
816
+ if (message.type === expectedType) {
817
+ return true;
818
+ }
819
+ if (message.type === "engine_event") {
820
+ const eventType = normalizeString(message.event?.type);
821
+ return eventType === expectedType;
822
+ }
823
+ if (message.type === "parallel_event") {
824
+ const eventType = normalizeString(message.event?.type);
825
+ return eventType === expectedType;
826
+ }
827
+ return false;
828
+ }
829
+ function summarizeMessage(message) {
830
+ if (message.type === "pong") {
831
+ return "pong";
832
+ }
833
+ if (message.type === "engine_event") {
834
+ const eventType = normalizeString(message.event?.type);
835
+ return eventType ? `engine_event:${eventType}` : "engine_event";
836
+ }
837
+ if (message.type === "parallel_event") {
838
+ const eventType = normalizeString(message.event?.type);
839
+ const orchestrationId = normalizeString(message.orchestrationId);
840
+ return orchestrationId ? `parallel_event:${eventType || "unknown"}:${orchestrationId}` : `parallel_event:${eventType || "unknown"}`;
841
+ }
842
+ return message.type;
843
+ }
844
+ function migrateManagedRemoteEndpoints(projectRoot, legacyPath = REMOTES_CONFIG_PATH) {
845
+ return importLegacyRemoteEndpoints(projectRoot, legacyPath);
846
+ }
847
+ function doctorManagedRemoteEndpoints(projectRoot) {
848
+ return doctorAuthorityRemoteEndpoints(projectRoot);
849
+ }
850
+ function updateManagedRemoteEndpointInAuthority(projectRoot, input) {
851
+ const updated = updateAuthorityRemoteEndpoint(projectRoot, input);
852
+ if (!updated)
853
+ return null;
854
+ return {
855
+ id: updated.id,
856
+ alias: updated.alias,
857
+ host: updated.host,
858
+ port: updated.port,
859
+ token: updated.token,
860
+ addedAt: updated.addedAt,
861
+ lastConnected: updated.lastConnectedAt
862
+ };
863
+ }
864
+ function normalizeString(value) {
865
+ if (!value) {
866
+ return "";
867
+ }
868
+ return value.trim();
869
+ }
870
+ function parsePort(value, label) {
871
+ if (!value) {
872
+ return null;
873
+ }
874
+ const parsed = Number.parseInt(value, 10);
875
+ if (!Number.isFinite(parsed) || parsed <= 0 || parsed > 65535) {
876
+ throw new RemoteCliError("RIG_REMOTE_INVALID_PORT", `Invalid port for ${label}: ${value}.`, 2, { label, value });
877
+ }
878
+ return parsed;
879
+ }
880
+ function parseIncomingMessage(raw) {
881
+ if (typeof raw !== "string") {
882
+ return null;
883
+ }
884
+ try {
885
+ const parsed = JSON.parse(raw);
886
+ if (!parsed || typeof parsed !== "object" || typeof parsed.type !== "string") {
887
+ return null;
888
+ }
889
+ return parsed;
890
+ } catch {
891
+ return null;
892
+ }
893
+ }
894
+ function expectType(message, expectedType) {
895
+ if (message.type !== expectedType) {
896
+ throw new RemoteCliError("RIG_REMOTE_UNEXPECTED_RESPONSE", `Unexpected response type: ${message.type}. Expected ${expectedType}.`, 3, { expectedType, actualType: message.type, message });
897
+ }
898
+ return message;
899
+ }
70
900
 
71
901
  // packages/cli/src/commands/_parsers.ts
72
902
  function parseOptionalPositiveInt(value, option) {
@@ -75,17 +905,17 @@ function parseOptionalPositiveInt(value, option) {
75
905
  }
76
906
  const parsed = Number.parseInt(value, 10);
77
907
  if (!Number.isFinite(parsed) || parsed <= 0) {
78
- throw new CliError2(`Invalid ${option} value: ${value}`);
908
+ throw new CliError(`Invalid ${option} value: ${value}`, 1, { hint: `Pass a positive integer, e.g. \`${option} 10\`.` });
79
909
  }
80
910
  return parsed;
81
911
  }
82
912
  function parseRequiredPositiveInt(value, option) {
83
913
  if (!value) {
84
- throw new CliError2(`Missing value for ${option}.`);
914
+ throw new CliError(`Missing value for ${option}.`, 1, { hint: `Provide a value after ${option}, e.g. \`${option} 10\`.` });
85
915
  }
86
916
  const parsed = Number.parseInt(value, 10);
87
917
  if (!Number.isFinite(parsed) || parsed <= 0) {
88
- throw new CliError2(`Invalid ${option} value: ${value}`);
918
+ throw new CliError(`Invalid ${option} value: ${value}`, 1, { hint: `Pass a positive integer, e.g. \`${option} 10\`.` });
89
919
  }
90
920
  return parsed;
91
921
  }
@@ -134,7 +964,7 @@ async function executeRemote(context, args) {
134
964
  const [subcommand = "list", ...subRest] = rest;
135
965
  switch (subcommand) {
136
966
  case "list": {
137
- requireNoExtraArgs(subRest, "bun run rig remote endpoint list");
967
+ requireNoExtraArgs(subRest, "rig remote endpoint list");
138
968
  const endpoints = listManagedRemoteEndpoints(undefined, context.projectRoot);
139
969
  const redactedEndpoints = endpoints.map((endpoint2) => redactRemoteEndpoint(endpoint2));
140
970
  if (context.outputMode === "text") {
@@ -158,9 +988,9 @@ async function executeRemote(context, args) {
158
988
  pending2 = port.rest;
159
989
  const token = takeOption(pending2, "--token");
160
990
  pending2 = token.rest;
161
- requireNoExtraArgs(pending2, "bun run rig remote endpoint add --alias <a> --host <h> --port <n> --token <t>");
991
+ requireNoExtraArgs(pending2, "rig remote endpoint add --alias <a> --host <h> --port <n> --token <t>");
162
992
  if (!alias.value || !host.value || !token.value || !port.value) {
163
- throw new CliError2("remote endpoint add requires --alias, --host, --port, and --token.");
993
+ throw new CliError("remote endpoint add requires --alias, --host, --port, and --token.", 1, { hint: "Re-run as `rig remote endpoint add --alias <a> --host <h> --port <n> --token <t>`." });
164
994
  }
165
995
  const created = upsertManagedRemoteEndpoint({
166
996
  alias: alias.value,
@@ -185,9 +1015,9 @@ async function executeRemote(context, args) {
185
1015
  pending2 = port.rest;
186
1016
  const token = takeOption(pending2, "--token");
187
1017
  pending2 = token.rest;
188
- requireNoExtraArgs(pending2, "bun run rig remote endpoint update --id <id> [--alias <a>] [--host <h>] [--port <n>] [--token <t>]");
1018
+ requireNoExtraArgs(pending2, "rig remote endpoint update --id <id> [--alias <a>] [--host <h>] [--port <n>] [--token <t>]");
189
1019
  if (!endpointId.value && !alias.value) {
190
- throw new CliError2("remote endpoint update requires --id <id> or --alias <a>.");
1020
+ throw new CliError("remote endpoint update requires --id <id> or --alias <a>.", 1, { hint: "List endpoints with `rig remote endpoint list`, then pass --id or --alias." });
191
1021
  }
192
1022
  const updated = updateManagedRemoteEndpointInAuthority(context.projectRoot, {
193
1023
  endpointId: endpointId.value || undefined,
@@ -197,7 +1027,7 @@ async function executeRemote(context, args) {
197
1027
  token: token.value || undefined
198
1028
  });
199
1029
  if (!updated) {
200
- throw new CliError2("Remote endpoint not found.", 2);
1030
+ throw new CliError("Remote endpoint not found.", 2, { hint: "Run `rig remote endpoint list` to see configured endpoints." });
201
1031
  }
202
1032
  if (context.outputMode === "text") {
203
1033
  console.log(`Updated remote endpoint ${updated.alias} -> ${updated.host}:${updated.port}`);
@@ -208,13 +1038,13 @@ async function executeRemote(context, args) {
208
1038
  let pending2 = subRest;
209
1039
  const alias = takeOption(pending2, "--alias");
210
1040
  pending2 = alias.rest;
211
- requireNoExtraArgs(pending2, "bun run rig remote endpoint remove --alias <a>");
1041
+ requireNoExtraArgs(pending2, "rig remote endpoint remove --alias <a>");
212
1042
  if (!alias.value) {
213
- throw new CliError2("remote endpoint remove requires --alias.");
1043
+ throw new CliError("remote endpoint remove requires --alias.", 1, { hint: "Run `rig remote endpoint list` to find the alias, then `rig remote endpoint remove --alias <a>`." });
214
1044
  }
215
1045
  const removed = removeManagedRemoteEndpoint(alias.value, undefined, context.projectRoot);
216
1046
  if (!removed) {
217
- throw new CliError2(`Remote endpoint alias not found: ${alias.value}`, 2);
1047
+ throw new CliError(`Remote endpoint alias not found: ${alias.value}`, 2, { hint: "Run `rig remote endpoint list` to see configured endpoint aliases." });
218
1048
  }
219
1049
  if (context.outputMode === "text") {
220
1050
  console.log(`Removed remote endpoint ${alias.value}`);
@@ -225,9 +1055,9 @@ async function executeRemote(context, args) {
225
1055
  let pending2 = subRest;
226
1056
  const alias = takeOption(pending2, "--alias");
227
1057
  pending2 = alias.rest;
228
- requireNoExtraArgs(pending2, "bun run rig remote endpoint test --alias <a>");
1058
+ requireNoExtraArgs(pending2, "rig remote endpoint test --alias <a>");
229
1059
  if (!alias.value) {
230
- throw new CliError2("remote endpoint test requires --alias.");
1060
+ throw new CliError("remote endpoint test requires --alias.", 1, { hint: "Run `rig remote endpoint list` to find the alias, then `rig remote endpoint test --alias <a>`." });
231
1061
  }
232
1062
  const endpoint2 = resolveRemoteEndpoint({ remoteAlias: alias.value, projectRoot: context.projectRoot });
233
1063
  const client = new RemoteWsClient(endpoint2);
@@ -256,7 +1086,7 @@ async function executeRemote(context, args) {
256
1086
  }
257
1087
  }
258
1088
  case "migrate": {
259
- requireNoExtraArgs(subRest, "bun run rig remote endpoint migrate");
1089
+ requireNoExtraArgs(subRest, "rig remote endpoint migrate");
260
1090
  const result = migrateManagedRemoteEndpoints(context.projectRoot);
261
1091
  if (context.outputMode === "text") {
262
1092
  console.log(`Imported ${result.imported} endpoint(s) from ${result.sourcePath}${result.skipped > 0 ? `, skipped ${result.skipped}` : ""}.`);
@@ -264,7 +1094,7 @@ async function executeRemote(context, args) {
264
1094
  return { ok: true, group: "remote", command: "endpoint migrate", details: result };
265
1095
  }
266
1096
  case "doctor": {
267
- requireNoExtraArgs(subRest, "bun run rig remote endpoint doctor");
1097
+ requireNoExtraArgs(subRest, "rig remote endpoint doctor");
268
1098
  const result = doctorManagedRemoteEndpoints(context.projectRoot);
269
1099
  if (context.outputMode === "text") {
270
1100
  console.log(JSON.stringify(result, null, 2));
@@ -272,7 +1102,7 @@ async function executeRemote(context, args) {
272
1102
  return { ok: true, group: "remote", command: "endpoint doctor", details: result };
273
1103
  }
274
1104
  default:
275
- throw new CliError2(`Unknown remote endpoint command: ${subcommand}`);
1105
+ throw new CliError(`Unknown remote endpoint command: ${subcommand}`, 1, { hint: "Run `rig remote --help` for endpoint commands (list|add|update|remove|test)." });
276
1106
  }
277
1107
  }
278
1108
  let pending = rest;
@@ -317,17 +1147,17 @@ async function executeRemote(context, args) {
317
1147
  try {
318
1148
  switch (command) {
319
1149
  case "test": {
320
- requireNoExtraArgs(pending, "bun run rig remote test [--remote <alias>] [--host <host>] [--port <n>] [--token <token>]");
1150
+ requireNoExtraArgs(pending, "rig remote test [--remote <alias>] [--host <host>] [--port <n>] [--token <token>]");
321
1151
  const response = await withClient((client) => client.ping());
322
1152
  return { ok: true, group: "remote", command, details: toDetails(response) };
323
1153
  }
324
1154
  case "status": {
325
- requireNoExtraArgs(pending, "bun run rig remote status [--remote <alias>] [--host <host>] [--port <n>] [--token <token>]");
1155
+ requireNoExtraArgs(pending, "rig remote status [--remote <alias>] [--host <host>] [--port <n>] [--token <token>]");
326
1156
  const response = await withClient((client) => client.getState());
327
1157
  return { ok: true, group: "remote", command, details: toDetails(response) };
328
1158
  }
329
1159
  case "tasks": {
330
- requireNoExtraArgs(pending, "bun run rig remote tasks [--remote <alias>] [--host <host>] [--port <n>] [--token <token>]");
1160
+ requireNoExtraArgs(pending, "rig remote tasks [--remote <alias>] [--host <host>] [--port <n>] [--token <token>]");
331
1161
  const response = await withClient((client) => client.getTasks());
332
1162
  return { ok: true, group: "remote", command, details: toDetails(response) };
333
1163
  }
@@ -337,11 +1167,11 @@ async function executeRemote(context, args) {
337
1167
  watchPending = secondsResult.rest;
338
1168
  const eventResult = takeOption(watchPending, "--event");
339
1169
  watchPending = eventResult.rest;
340
- requireNoExtraArgs(watchPending, "bun run rig remote watch [--seconds <n>] [--event <type>] [--remote <alias>] [--host <host>] [--port <n>] [--token <token>]");
1170
+ requireNoExtraArgs(watchPending, "rig remote watch [--seconds <n>] [--event <type>] [--remote <alias>] [--host <host>] [--port <n>] [--token <token>]");
341
1171
  const seconds = parseOptionalPositiveInt(secondsResult.value, "--seconds");
342
1172
  const eventFilter = eventResult.value || undefined;
343
1173
  if (context.outputMode === "json" && !seconds) {
344
- throw new CliError2("--json remote watch requires --seconds <n> to produce bounded output.", 2);
1174
+ throw new CliError("--json remote watch requires --seconds <n> to produce bounded output.", 2, { hint: "Re-run as `rig remote watch --seconds 10 --json`." });
345
1175
  }
346
1176
  const captured = [];
347
1177
  let dropped = 0;
@@ -406,7 +1236,7 @@ async function executeRemote(context, args) {
406
1236
  case "stop":
407
1237
  case "continue":
408
1238
  case "refresh": {
409
- requireNoExtraArgs(pending, `bun run rig remote ${command} [--remote <alias>] [--host <host>] [--port <n>] [--token <token>]`);
1239
+ requireNoExtraArgs(pending, `rig remote ${command} [--remote <alias>] [--host <host>] [--port <n>] [--token <token>]`);
410
1240
  const response = await withClient(async (client) => {
411
1241
  switch (command) {
412
1242
  case "pause":
@@ -429,7 +1259,7 @@ async function executeRemote(context, args) {
429
1259
  let countPending = pending;
430
1260
  const countResult = takeOption(countPending, "--count");
431
1261
  countPending = countResult.rest;
432
- requireNoExtraArgs(countPending, `bun run rig remote ${command} --count <n> [--remote <alias>] [--host <host>] [--port <n>] [--token <token>]`);
1262
+ requireNoExtraArgs(countPending, `rig remote ${command} --count <n> [--remote <alias>] [--host <host>] [--port <n>] [--token <token>]`);
433
1263
  const count = parseRequiredPositiveInt(countResult.value, "--count");
434
1264
  const response = await withClient(async (client) => command === "add-iterations" ? await client.addIterations(count) : await client.removeIterations(count));
435
1265
  assertRemoteOperationSuccess(command, response);
@@ -440,8 +1270,8 @@ async function executeRemote(context, args) {
440
1270
  let taskPending = pending;
441
1271
  const taskResult = takeOption(taskPending, "--task");
442
1272
  taskPending = taskResult.rest;
443
- requireNoExtraArgs(taskPending, `bun run rig remote ${command} --task <id> [--remote <alias>] [--host <host>] [--port <n>] [--token <token>]`);
444
- const taskId = requireTask(taskResult.value, `bun run rig remote ${command} --task <id>`);
1273
+ requireNoExtraArgs(taskPending, `rig remote ${command} --task <id> [--remote <alias>] [--host <host>] [--port <n>] [--token <token>]`);
1274
+ const taskId = requireTask(taskResult.value, `rig remote ${command} --task <id>`);
445
1275
  const response = await withClient(async (client) => command === "prompt-preview" ? await client.getPromptPreview(taskId) : await client.getIterationOutput(taskId));
446
1276
  assertRemoteOperationSuccess(command, response);
447
1277
  return { ok: true, group: "remote", command, details: toDetails(response) };
@@ -454,7 +1284,7 @@ async function executeRemote(context, args) {
454
1284
  orchestrationPending = maxIterationsResult.rest;
455
1285
  const directMergeResult = takeFlag(orchestrationPending, "--direct-merge");
456
1286
  orchestrationPending = directMergeResult.rest;
457
- requireNoExtraArgs(orchestrationPending, "bun run rig remote orchestrate-start [--max-workers <n>] [--max-iterations <n>] [--direct-merge]");
1287
+ requireNoExtraArgs(orchestrationPending, "rig remote orchestrate-start [--max-workers <n>] [--max-iterations <n>] [--direct-merge]");
458
1288
  const response = await withClient((client) => client.startOrchestration({
459
1289
  maxWorkers: parseOptionalPositiveInt(maxWorkersResult.value, "--max-workers"),
460
1290
  maxIterations: parseOptionalPositiveInt(maxIterationsResult.value, "--max-iterations"),
@@ -475,8 +1305,8 @@ async function executeRemote(context, args) {
475
1305
  let orchestrationPending = pending;
476
1306
  const idResult = takeOption(orchestrationPending, "--id");
477
1307
  orchestrationPending = idResult.rest;
478
- requireNoExtraArgs(orchestrationPending, `bun run rig remote ${command} --id <orch-id> [--remote <alias>] [--host <host>] [--port <n>] [--token <token>]`);
479
- const orchestrationId = requireTask(idResult.value, `bun run rig remote ${command} --id <orch-id> [--remote <alias>] [--host <host>] [--port <n>] [--token <token>]`);
1308
+ requireNoExtraArgs(orchestrationPending, `rig remote ${command} --id <orch-id> [--remote <alias>] [--host <host>] [--port <n>] [--token <token>]`);
1309
+ const orchestrationId = requireTask(idResult.value, `rig remote ${command} --id <orch-id> [--remote <alias>] [--host <host>] [--port <n>] [--token <token>]`);
480
1310
  const response = await withClient(async (client) => {
481
1311
  switch (command) {
482
1312
  case "orchestrate-pause":
@@ -493,11 +1323,11 @@ async function executeRemote(context, args) {
493
1323
  return { ok: true, group: "remote", command, details: toDetails(response) };
494
1324
  }
495
1325
  default:
496
- throw new CliError2(`Unknown remote command: ${command}`);
1326
+ throw new CliError(`Unknown remote command: ${command}`, 1, { hint: "Run `rig remote --help` to list remote commands." });
497
1327
  }
498
1328
  } catch (error) {
499
1329
  if (error instanceof RemoteCliError) {
500
- throw new CliError2(`[${error.code}] ${error.message}`, error.exitCode);
1330
+ throw new CliError(`[${error.code}] ${error.message}`, error.exitCode);
501
1331
  }
502
1332
  throw error;
503
1333
  }