@h-rig/core 0.0.6-alpha.157 → 0.0.6-alpha.158

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 (89) hide show
  1. package/dist/src/agent-role-registry.d.ts +4 -0
  2. package/dist/src/agent-role-registry.js +27 -0
  3. package/dist/src/authority-paths.d.ts +15 -0
  4. package/dist/src/authority-paths.js +80 -0
  5. package/dist/src/baked-secrets.d.ts +6 -0
  6. package/dist/src/baked-secrets.js +121 -0
  7. package/dist/src/build-time-config.d.ts +12 -0
  8. package/dist/src/build-time-config.js +25 -0
  9. package/dist/src/build-time-config.macro.d.ts +1 -0
  10. package/dist/src/capability-loaders.d.ts +51 -0
  11. package/dist/src/{task-io.js → capability-loaders.js} +237 -195
  12. package/dist/src/capability.d.ts +79 -0
  13. package/dist/src/capability.js +63 -0
  14. package/dist/src/checkout-root.d.ts +1 -0
  15. package/dist/src/checkout-root.js +30 -0
  16. package/dist/src/config-env.d.ts +4 -0
  17. package/dist/src/config-env.js +23 -0
  18. package/dist/src/config.d.ts +1 -1
  19. package/dist/src/config.js +7 -4
  20. package/dist/src/declarative-config.d.ts +14 -0
  21. package/dist/src/declarative-config.js +82 -0
  22. package/dist/src/define-config.d.ts +2 -1
  23. package/dist/src/define-config.js +7 -4
  24. package/dist/src/embedded-plugins.d.ts +59 -0
  25. package/dist/src/embedded-plugins.js +22 -0
  26. package/dist/src/exec.d.ts +13 -0
  27. package/dist/src/exec.js +101 -0
  28. package/dist/src/harness-paths.d.ts +18 -0
  29. package/dist/src/harness-paths.js +141 -0
  30. package/dist/src/hook-materializer.d.ts +72 -0
  31. package/dist/src/hook-materializer.js +274 -0
  32. package/dist/src/hook-runner.d.ts +48 -0
  33. package/dist/src/hook-runner.js +752 -0
  34. package/dist/src/hook-runtime.d.ts +11 -0
  35. package/dist/src/hook-runtime.js +307 -0
  36. package/dist/src/index.d.ts +3 -1
  37. package/dist/src/index.js +119 -5
  38. package/dist/src/json-files.d.ts +9 -0
  39. package/dist/src/json-files.js +125 -0
  40. package/dist/src/kernel-entrypoint.d.ts +22 -0
  41. package/dist/src/kernel-entrypoint.js +537 -0
  42. package/dist/src/layout.d.ts +10 -0
  43. package/dist/src/layout.js +144 -0
  44. package/dist/src/load-config.js +149 -50
  45. package/dist/src/placement.d.ts +50 -0
  46. package/dist/src/placement.js +990 -0
  47. package/dist/src/plugin-host-context.d.ts +66 -0
  48. package/dist/src/plugin-host-context.js +1270 -0
  49. package/dist/src/plugin-host-registries.d.ts +31 -0
  50. package/dist/src/plugin-host-registries.js +79 -0
  51. package/dist/src/plugin-host.d.ts +6 -1
  52. package/dist/src/plugin-host.js +51 -0
  53. package/dist/src/plugin-runtime.d.ts +45 -6
  54. package/dist/src/profile-ops.d.ts +9 -0
  55. package/dist/src/profile-ops.js +252 -0
  56. package/dist/src/project-plugins.d.ts +9 -12
  57. package/dist/src/project-plugins.js +248 -61
  58. package/dist/src/remote-config.d.ts +183 -0
  59. package/dist/src/remote-config.js +568 -0
  60. package/dist/src/root-resolver.d.ts +5 -0
  61. package/dist/src/root-resolver.js +69 -0
  62. package/dist/src/run-provisioning.d.ts +58 -0
  63. package/dist/src/run-provisioning.js +128 -0
  64. package/dist/src/runtime-context.d.ts +20 -0
  65. package/dist/src/runtime-context.js +257 -0
  66. package/dist/src/runtime-events.d.ts +44 -0
  67. package/dist/src/runtime-events.js +212 -0
  68. package/dist/src/runtime-overlay.d.ts +11 -0
  69. package/dist/src/runtime-overlay.js +71 -0
  70. package/dist/src/runtime-paths.d.ts +21 -0
  71. package/dist/src/runtime-paths.js +181 -0
  72. package/dist/src/runtime-provisioning-env.d.ts +5 -0
  73. package/dist/src/runtime-provisioning-env.js +217 -0
  74. package/dist/src/runtime-runner-context.d.ts +12 -0
  75. package/dist/src/runtime-runner-context.js +1 -0
  76. package/dist/src/safe-identifiers.d.ts +44 -0
  77. package/dist/src/safe-identifiers.js +96 -0
  78. package/dist/src/scope-rules.d.ts +4 -0
  79. package/dist/src/scope-rules.js +21 -0
  80. package/dist/src/server-paths.d.ts +26 -0
  81. package/dist/src/server-paths.js +308 -0
  82. package/dist/src/setup-version.d.ts +3 -0
  83. package/dist/src/setup-version.js +14 -0
  84. package/dist/src/task-record-reader.d.ts +3 -0
  85. package/dist/src/task-record-reader.js +9 -0
  86. package/dist/src/validator-registry.d.ts +27 -0
  87. package/dist/src/validator-registry.js +64 -0
  88. package/package.json +136 -6
  89. package/dist/src/task-io.d.ts +0 -54
@@ -0,0 +1,568 @@
1
+ // @bun
2
+ // packages/core/src/remote-config.ts
3
+ import { randomUUID } from "crypto";
4
+ import { chmodSync, existsSync as existsSync2, mkdirSync, mkdtempSync, readFileSync, writeFileSync } from "fs";
5
+ import { homedir, tmpdir } from "os";
6
+ import { dirname as dirname2, join, resolve as resolve2 } from "path";
7
+ import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
8
+
9
+ // packages/core/src/authority-paths.ts
10
+ import { existsSync } from "fs";
11
+ import { dirname, resolve } from "path";
12
+ function normalizeOptionalString(value) {
13
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
14
+ }
15
+ function resolveAuthorityPaths(projectRoot) {
16
+ const normalizedRoot = resolve(projectRoot);
17
+ const stateRoot = resolveAuthorityStateRoot(normalizedRoot);
18
+ const stateDir = resolveAuthorityStateDir(normalizedRoot);
19
+ return {
20
+ projectRoot: normalizedRoot,
21
+ harnessRoot: resolve(normalizedRoot, "rig"),
22
+ runsDir: resolve(stateRoot, "runs"),
23
+ remoteDir: resolve(stateRoot, "remote"),
24
+ stateDir,
25
+ remoteEndpointsPath: resolve(stateRoot, "remote", "endpoints.toml"),
26
+ remoteSecretsPath: resolve(stateDir, "remote-secrets.toml")
27
+ };
28
+ }
29
+ function resolveAuthorityStateRoot(projectRoot) {
30
+ const normalizedRoot = resolve(projectRoot);
31
+ const taskWorkspace = normalizeOptionalString(process.env.RIG_TASK_WORKSPACE);
32
+ if (taskWorkspace) {
33
+ return resolve(taskWorkspace, ".rig");
34
+ }
35
+ const stateDir = normalizeOptionalString(process.env.RIG_STATE_DIR);
36
+ if (stateDir) {
37
+ return dirname(resolve(stateDir));
38
+ }
39
+ const logsDir = normalizeOptionalString(process.env.RIG_LOGS_DIR);
40
+ if (logsDir) {
41
+ return dirname(resolve(logsDir));
42
+ }
43
+ const sessionFile = normalizeOptionalString(process.env.RIG_SESSION_FILE);
44
+ if (sessionFile) {
45
+ return dirname(dirname(resolve(sessionFile)));
46
+ }
47
+ const projectStateRoot = resolve(normalizedRoot, ".rig");
48
+ if (existsSync(projectStateRoot)) {
49
+ return projectStateRoot;
50
+ }
51
+ return resolve(normalizedRoot, ".rig");
52
+ }
53
+ function resolveAuthorityStateDir(projectRoot) {
54
+ const explicit = normalizeOptionalString(process.env.RIG_STATE_DIR);
55
+ if (explicit) {
56
+ return resolve(explicit);
57
+ }
58
+ return resolve(resolveAuthorityStateRoot(projectRoot), "state");
59
+ }
60
+
61
+ // packages/core/src/remote-config.ts
62
+ var DEFAULT_REMOTE_PORT = 7890;
63
+ var BACKBONE_DEFAULTS_KEY = Symbol.for("rig.remote-config.backbone-defaults");
64
+ function backboneDefaultsState() {
65
+ const global = globalThis;
66
+ return global[BACKBONE_DEFAULTS_KEY] ??= {};
67
+ }
68
+ function registerBackboneDefaults(defaults) {
69
+ const relayUrl = normalizeString(defaults.relayUrl);
70
+ const registryBaseUrl = normalizeString(defaults.registryBaseUrl);
71
+ const sshTarget = normalizeString(defaults.sshTarget);
72
+ const state = backboneDefaultsState();
73
+ if (relayUrl)
74
+ state.relayUrl = relayUrl;
75
+ if (registryBaseUrl)
76
+ state.registryBaseUrl = registryBaseUrl;
77
+ if (sshTarget)
78
+ state.sshTarget = sshTarget;
79
+ }
80
+ function resolveRelayUrl(env = process.env) {
81
+ return normalizeString(env.RIG_COLLAB_RELAY) || normalizeString(env.RIG_SPIKE_RELAY) || backboneDefaultsState().relayUrl || "";
82
+ }
83
+ function resolveSshTarget(env = process.env) {
84
+ return normalizeString(env.RIG_SSH_TARGET) || backboneDefaultsState().sshTarget || "";
85
+ }
86
+ var REMOTES_CONFIG_PATH = join(homedir(), ".config", "rig", "remotes.toml");
87
+
88
+ class RemoteCliError extends Error {
89
+ code;
90
+ exitCode;
91
+ details;
92
+ constructor(code, message, exitCode = 1, details) {
93
+ super(message);
94
+ this.name = "RemoteCliError";
95
+ this.code = code;
96
+ this.exitCode = exitCode;
97
+ this.details = details;
98
+ }
99
+ }
100
+ function loadRemotesConfig(configPath = REMOTES_CONFIG_PATH) {
101
+ if (!existsSync2(configPath)) {
102
+ return { version: 1, remotes: {} };
103
+ }
104
+ try {
105
+ const content = readFileSync(configPath, "utf-8");
106
+ if (!content.trim()) {
107
+ return { version: 1, remotes: {} };
108
+ }
109
+ const parsed = parseToml(content);
110
+ return {
111
+ version: parsed.version ?? 1,
112
+ remotes: parsed.remotes ?? {}
113
+ };
114
+ } catch (error) {
115
+ throw new RemoteCliError("RIG_REMOTE_CONFIG_PARSE_ERROR", `Failed to parse remotes config at ${configPath}: ${error instanceof Error ? error.message : String(error)}`, 2, { configPath });
116
+ }
117
+ }
118
+ function saveRemotesConfig(config, configPath = REMOTES_CONFIG_PATH) {
119
+ mkdirSync(dirname2(configPath), { recursive: true });
120
+ writeFileSync(configPath, `${stringifyToml(config).trimEnd()}
121
+ `, "utf-8");
122
+ try {
123
+ chmodSync(configPath, 384);
124
+ } catch {}
125
+ }
126
+ function readTomlFile(path, fallback) {
127
+ if (!existsSync2(path))
128
+ return fallback;
129
+ const raw = readFileSync(path, "utf8");
130
+ if (!raw.trim())
131
+ return fallback;
132
+ return parseToml(raw);
133
+ }
134
+ function writeTomlFile(path, value) {
135
+ mkdirSync(dirname2(path), { recursive: true });
136
+ writeFileSync(path, `${stringifyToml(value).trimEnd()}
137
+ `, "utf8");
138
+ }
139
+ function normalizeStringArray(value) {
140
+ if (!Array.isArray(value))
141
+ return [];
142
+ return value.map((entry) => normalizeString(entry)).filter((entry) => entry !== "");
143
+ }
144
+ function normalizePort(value) {
145
+ const port = typeof value === "number" ? value : Number(value);
146
+ return Number.isInteger(port) && port > 0 && port <= 65535 ? port : DEFAULT_REMOTE_PORT;
147
+ }
148
+ function normalizeRegistryBaseUrl(raw) {
149
+ const trimmed = raw.replace(/\/+$/, "");
150
+ if (!trimmed)
151
+ return trimmed;
152
+ return /\/registry$/.test(trimmed) ? trimmed : `${trimmed}/registry`;
153
+ }
154
+ function loadRemoteEndpointsToml(projectRoot) {
155
+ const paths = resolveAuthorityPaths(projectRoot);
156
+ const parsed = readTomlFile(paths.remoteEndpointsPath, { version: 1, endpoints: {} });
157
+ return {
158
+ version: parsed.version ?? 1,
159
+ endpoints: parsed.endpoints ?? {}
160
+ };
161
+ }
162
+ function loadRemoteSecretsToml(projectRoot) {
163
+ const paths = resolveAuthorityPaths(projectRoot);
164
+ const parsed = readTomlFile(paths.remoteSecretsPath, { version: 1, secrets: {} });
165
+ return {
166
+ version: parsed.version ?? 1,
167
+ secrets: parsed.secrets ?? {}
168
+ };
169
+ }
170
+ function readJsonRecordFile(path) {
171
+ try {
172
+ if (!existsSync2(path))
173
+ return {};
174
+ const raw = readFileSync(path, "utf8");
175
+ if (!raw.trim())
176
+ return {};
177
+ const parsed = JSON.parse(raw);
178
+ return parsed && typeof parsed === "object" ? parsed : {};
179
+ } catch {
180
+ return {};
181
+ }
182
+ }
183
+ function coerceString(value) {
184
+ return typeof value === "string" ? value.trim() : "";
185
+ }
186
+ function resolveOwnerNamespaceKey(projectRoot, env = process.env) {
187
+ const fromEnv = normalizeString(env.RIG_GITHUB_NAMESPACE_KEY);
188
+ if (fromEnv)
189
+ return fromEnv;
190
+ const stateDir = resolveAuthorityPaths(projectRoot).stateDir;
191
+ const auth = readJsonRecordFile(resolve2(stateDir, "github-auth.json"));
192
+ const namespaceKey = coerceString(auth.userNamespaceKey);
193
+ if (namespaceKey)
194
+ return namespaceKey;
195
+ const userId = coerceString(auth.userId);
196
+ return userId ? `gh:${userId}` : undefined;
197
+ }
198
+ function forceLocalTransport(env) {
199
+ return env.RIG_FORCE_LOCAL === "1" || env.RIG_FORCE_LOCAL === "true";
200
+ }
201
+ function resolveDispatchTransportPlacement(projectRoot, env = process.env) {
202
+ if (forceLocalTransport(env)) {
203
+ return { kind: "local", alias: "local", sshTarget: null, selected: null };
204
+ }
205
+ const selected = resolveSelectedRemote(projectRoot, env);
206
+ const sshTarget = selected?.sshTarget ?? resolveSshTarget(env);
207
+ if (!sshTarget) {
208
+ return { kind: "local", alias: "local", sshTarget: null, selected: null };
209
+ }
210
+ return { kind: "remote", alias: selected?.alias ?? sshTarget, sshTarget, selected };
211
+ }
212
+ function resolveSelectedRemote(projectRoot, env = process.env) {
213
+ const stateDir = resolveAuthorityPaths(projectRoot).stateDir;
214
+ const conn = readJsonRecordFile(resolve2(stateDir, "connection.json"));
215
+ const selected = normalizeString(env.RIG_REMOTE_ALIAS) || coerceString(conn.selected);
216
+ if (!selected || selected === "local")
217
+ return null;
218
+ const endpoints = Object.values(loadRemoteEndpointsToml(projectRoot).endpoints ?? {});
219
+ const match = endpoints.find((entry) => normalizeString(entry.alias) === selected);
220
+ if (!match) {
221
+ if (selected === "remote") {
222
+ const sshTarget = resolveSshTarget(env);
223
+ if (!sshTarget)
224
+ return null;
225
+ return {
226
+ alias: "remote",
227
+ host: sshTarget,
228
+ port: 22,
229
+ sshTarget,
230
+ checkout: coerceString(conn.serverProjectRoot) || null,
231
+ registryBaseUrl: null,
232
+ secretRef: null
233
+ };
234
+ }
235
+ return null;
236
+ }
237
+ const host = normalizeString(match.host) || selected;
238
+ const explicitRegistryUrl = normalizeString(env.RIG_REGISTRY_URL) || normalizeString(env.REGISTRY_URL);
239
+ const registryBaseUrl = explicitRegistryUrl ? normalizeRegistryBaseUrl(explicitRegistryUrl) : null;
240
+ return {
241
+ alias: selected,
242
+ host,
243
+ port: normalizePort(match.port),
244
+ sshTarget: host || normalizeString(match.alias) || coerceString(conn.serverProjectRootAlias),
245
+ checkout: coerceString(conn.serverProjectRoot) || null,
246
+ registryBaseUrl,
247
+ secretRef: normalizeString(match.secret_ref) || null
248
+ };
249
+ }
250
+ function resolveRegistryBaseUrl(projectRoot, env = process.env) {
251
+ return normalizeRegistryBaseUrl(normalizeString(env.RIG_REGISTRY_URL) || normalizeString(env.REGISTRY_URL) || resolveSelectedRemote(projectRoot, env)?.registryBaseUrl || backboneDefaultsState().registryBaseUrl || "");
252
+ }
253
+ var rigOmpConfigOverlayPath = null;
254
+ function resolveRigOmpConfigOverlayPath(projectRoot = process.cwd()) {
255
+ if (rigOmpConfigOverlayPath && existsSync2(rigOmpConfigOverlayPath))
256
+ return rigOmpConfigOverlayPath;
257
+ const overlay = [
258
+ `collab.relayUrl: ${resolveRelayUrl()}`,
259
+ `registry.url: ${resolveRegistryBaseUrl(projectRoot)}`,
260
+ "# Cockpit owns the screen: skip OMP's welcome/startup so the drone board renders cleanly.",
261
+ "startup.quiet: true",
262
+ ""
263
+ ].join(`
264
+ `);
265
+ const base = tmpdir();
266
+ mkdirSync(base, { recursive: true });
267
+ const dir = mkdtempSync(join(base, "rig-config-"));
268
+ const file = join(dir, "rig-default-config.yml");
269
+ writeFileSync(file, overlay);
270
+ rigOmpConfigOverlayPath = file;
271
+ return file;
272
+ }
273
+ function saveRemoteEndpointsToml(projectRoot, payload) {
274
+ writeTomlFile(resolveAuthorityPaths(projectRoot).remoteEndpointsPath, payload);
275
+ }
276
+ function saveRemoteSecretsToml(projectRoot, payload) {
277
+ const paths = resolveAuthorityPaths(projectRoot);
278
+ writeTomlFile(paths.remoteSecretsPath, payload);
279
+ try {
280
+ chmodSync(paths.remoteSecretsPath, 384);
281
+ } catch {}
282
+ }
283
+ function listAuthorityRemoteEndpoints(projectRoot) {
284
+ const endpoints = loadRemoteEndpointsToml(projectRoot).endpoints ?? {};
285
+ const secrets = loadRemoteSecretsToml(projectRoot).secrets ?? {};
286
+ return Object.values(endpoints).map((entry) => {
287
+ const token = secrets[entry.secret_ref] ?? "";
288
+ return {
289
+ id: entry.id,
290
+ alias: entry.alias,
291
+ host: entry.host,
292
+ port: normalizePort(entry.port),
293
+ token,
294
+ autoConnect: Boolean(entry.auto_connect),
295
+ addedAt: entry.created_at,
296
+ updatedAt: entry.updated_at,
297
+ lastConnectedAt: normalizeString(entry.last_connected_at) || null,
298
+ labels: normalizeStringArray(entry.labels),
299
+ capabilities: normalizeStringArray(entry.capabilities),
300
+ transport: normalizeString(entry.transport) || "websocket",
301
+ secretRef: entry.secret_ref
302
+ };
303
+ }).sort((left, right) => left.alias.localeCompare(right.alias));
304
+ }
305
+ function nextRemoteEndpointRecord(input) {
306
+ const timestamp = new Date().toISOString();
307
+ const secretRef = input.existing?.secret_ref ?? randomUUID();
308
+ return {
309
+ record: {
310
+ id: input.endpointId ?? input.existing?.id ?? randomUUID(),
311
+ alias: input.alias,
312
+ host: input.host,
313
+ port: normalizePort(input.port),
314
+ transport: normalizeString(input.transport) || normalizeString(input.existing?.transport) || "websocket",
315
+ auto_connect: input.autoConnect ?? input.existing?.auto_connect ?? false,
316
+ labels: input.labels ?? normalizeStringArray(input.existing?.labels),
317
+ capabilities: input.capabilities ?? normalizeStringArray(input.existing?.capabilities),
318
+ secret_ref: secretRef,
319
+ created_at: input.existing?.created_at ?? timestamp,
320
+ updated_at: timestamp,
321
+ last_connected_at: input.existing?.last_connected_at ?? null
322
+ },
323
+ secretRef
324
+ };
325
+ }
326
+ function upsertAuthorityRemoteEndpoint(projectRoot, input) {
327
+ const endpointsToml = loadRemoteEndpointsToml(projectRoot);
328
+ const secretsToml = loadRemoteSecretsToml(projectRoot);
329
+ const existing = Object.values(endpointsToml.endpoints ?? {}).find((entry) => entry.alias === input.alias) ?? null;
330
+ const { record, secretRef } = nextRemoteEndpointRecord({
331
+ existing,
332
+ alias: input.alias,
333
+ host: input.host,
334
+ port: input.port,
335
+ token: input.token,
336
+ ...input.endpointId !== undefined ? { endpointId: input.endpointId } : {},
337
+ ...input.autoConnect !== undefined ? { autoConnect: input.autoConnect } : {},
338
+ ...input.transport !== undefined ? { transport: input.transport } : {},
339
+ ...input.labels !== undefined ? { labels: input.labels } : {},
340
+ ...input.capabilities !== undefined ? { capabilities: input.capabilities } : {}
341
+ });
342
+ endpointsToml.endpoints = {
343
+ ...endpointsToml.endpoints ?? {},
344
+ [record.id]: record
345
+ };
346
+ secretsToml.secrets = {
347
+ ...secretsToml.secrets ?? {},
348
+ [secretRef]: input.token
349
+ };
350
+ saveRemoteEndpointsToml(projectRoot, endpointsToml);
351
+ saveRemoteSecretsToml(projectRoot, secretsToml);
352
+ return listAuthorityRemoteEndpoints(projectRoot).find((entry) => entry.id === record.id);
353
+ }
354
+ function updateAuthorityRemoteEndpoint(projectRoot, input) {
355
+ const endpointsToml = loadRemoteEndpointsToml(projectRoot);
356
+ const secretsToml = loadRemoteSecretsToml(projectRoot);
357
+ const current = Object.values(endpointsToml.endpoints ?? {}).find((entry) => input.endpointId && entry.id === input.endpointId || input.alias && entry.alias === input.alias);
358
+ if (!current)
359
+ return null;
360
+ const record = {
361
+ ...current,
362
+ alias: normalizeString(input.alias) || current.alias,
363
+ host: normalizeString(input.host) || current.host,
364
+ port: input.port !== undefined ? normalizePort(input.port) : current.port,
365
+ auto_connect: input.autoConnect ?? current.auto_connect,
366
+ transport: normalizeString(input.transport) || current.transport,
367
+ labels: input.labels ?? normalizeStringArray(current.labels),
368
+ capabilities: input.capabilities ?? normalizeStringArray(current.capabilities),
369
+ updated_at: new Date().toISOString()
370
+ };
371
+ endpointsToml.endpoints = {
372
+ ...endpointsToml.endpoints ?? {},
373
+ [record.id]: record
374
+ };
375
+ if (input.token !== undefined) {
376
+ secretsToml.secrets = {
377
+ ...secretsToml.secrets ?? {},
378
+ [record.secret_ref]: input.token
379
+ };
380
+ }
381
+ saveRemoteEndpointsToml(projectRoot, endpointsToml);
382
+ saveRemoteSecretsToml(projectRoot, secretsToml);
383
+ return listAuthorityRemoteEndpoints(projectRoot).find((entry) => entry.id === record.id) ?? null;
384
+ }
385
+ function removeAuthorityRemoteEndpoint(projectRoot, endpointIdOrAlias) {
386
+ const endpointsToml = loadRemoteEndpointsToml(projectRoot);
387
+ const secretsToml = loadRemoteSecretsToml(projectRoot);
388
+ const entry = Object.values(endpointsToml.endpoints ?? {}).find((candidate) => candidate.id === endpointIdOrAlias || candidate.alias === endpointIdOrAlias);
389
+ if (!entry)
390
+ return false;
391
+ const nextEndpoints = { ...endpointsToml.endpoints ?? {} };
392
+ delete nextEndpoints[entry.id];
393
+ const nextSecrets = { ...secretsToml.secrets ?? {} };
394
+ delete nextSecrets[entry.secret_ref];
395
+ saveRemoteEndpointsToml(projectRoot, { version: endpointsToml.version ?? 1, endpoints: nextEndpoints });
396
+ saveRemoteSecretsToml(projectRoot, { version: secretsToml.version ?? 1, secrets: nextSecrets });
397
+ return true;
398
+ }
399
+ function markAuthorityRemoteEndpointConnected(projectRoot, endpointId, connectedAt = new Date().toISOString()) {
400
+ const endpointsToml = loadRemoteEndpointsToml(projectRoot);
401
+ const entry = endpointsToml.endpoints?.[endpointId];
402
+ if (!entry)
403
+ return;
404
+ endpointsToml.endpoints = {
405
+ ...endpointsToml.endpoints ?? {},
406
+ [endpointId]: {
407
+ ...entry,
408
+ last_connected_at: connectedAt,
409
+ updated_at: connectedAt
410
+ }
411
+ };
412
+ saveRemoteEndpointsToml(projectRoot, endpointsToml);
413
+ }
414
+ function importLegacyRemoteEndpoints(projectRoot, legacyPath = REMOTES_CONFIG_PATH) {
415
+ if (!existsSync2(legacyPath)) {
416
+ return { imported: 0, skipped: 0, sourcePath: legacyPath };
417
+ }
418
+ const parsed = readTomlFile(legacyPath, { version: 1, remotes: {} });
419
+ let imported = 0;
420
+ let skipped = 0;
421
+ for (const [alias, entry] of Object.entries(parsed.remotes ?? {})) {
422
+ const host = normalizeString(entry.host);
423
+ const token = normalizeString(entry.token);
424
+ if (!host || !token) {
425
+ skipped += 1;
426
+ continue;
427
+ }
428
+ upsertAuthorityRemoteEndpoint(projectRoot, {
429
+ alias,
430
+ host,
431
+ port: normalizePort(entry.port),
432
+ token
433
+ });
434
+ imported += 1;
435
+ }
436
+ return { imported, skipped, sourcePath: legacyPath };
437
+ }
438
+ function doctorAuthorityRemoteEndpoints(projectRoot) {
439
+ const paths = resolveAuthorityPaths(projectRoot);
440
+ const endpoints = loadRemoteEndpointsToml(projectRoot).endpoints ?? {};
441
+ const secrets = loadRemoteSecretsToml(projectRoot).secrets ?? {};
442
+ const missingSecrets = Object.values(endpoints).filter((entry) => !normalizeString(secrets[entry.secret_ref])).map((entry) => entry.alias);
443
+ const warnings = [];
444
+ if (!existsSync2(paths.remoteEndpointsPath)) {
445
+ warnings.push("Remote endpoint manifest is missing.");
446
+ }
447
+ if (!existsSync2(paths.remoteSecretsPath)) {
448
+ warnings.push("Remote secret store is missing.");
449
+ }
450
+ return {
451
+ manifestPath: paths.remoteEndpointsPath,
452
+ secretsPath: paths.remoteSecretsPath,
453
+ endpointCount: Object.keys(endpoints).length,
454
+ missingSecrets,
455
+ warnings
456
+ };
457
+ }
458
+ function listManagedRemoteEndpoints(configPath = REMOTES_CONFIG_PATH, projectRoot) {
459
+ if (projectRoot) {
460
+ return listAuthorityRemoteEndpoints(projectRoot).map((entry) => ({
461
+ id: entry.id,
462
+ alias: entry.alias,
463
+ host: entry.host,
464
+ port: entry.port,
465
+ token: entry.token,
466
+ addedAt: entry.addedAt,
467
+ lastConnected: entry.lastConnectedAt
468
+ }));
469
+ }
470
+ const config = loadRemotesConfig(configPath);
471
+ return Object.entries(config.remotes ?? {}).map(([alias, entry]) => ({
472
+ id: alias,
473
+ alias,
474
+ host: normalizeString(entry.host) || "",
475
+ port: Number.isFinite(entry.port) ? Number(entry.port) : DEFAULT_REMOTE_PORT,
476
+ token: normalizeString(entry.token) || "",
477
+ addedAt: normalizeString(entry.addedAt) || null,
478
+ lastConnected: normalizeString(entry.lastConnected) || null
479
+ })).sort((left, right) => left.alias.localeCompare(right.alias));
480
+ }
481
+ function upsertManagedRemoteEndpoint(input, configPath = REMOTES_CONFIG_PATH, projectRoot) {
482
+ if (projectRoot) {
483
+ const saved = upsertAuthorityRemoteEndpoint(projectRoot, {
484
+ ...input,
485
+ token: input.token ?? ""
486
+ });
487
+ return {
488
+ id: saved.id,
489
+ alias: saved.alias,
490
+ host: saved.host,
491
+ port: saved.port,
492
+ token: saved.token,
493
+ addedAt: saved.addedAt,
494
+ lastConnected: saved.lastConnectedAt
495
+ };
496
+ }
497
+ const config = loadRemotesConfig(configPath);
498
+ const existing = config.remotes?.[input.alias];
499
+ const nextEntry = {
500
+ host: input.host,
501
+ port: input.port,
502
+ ...input.token !== undefined ? { token: input.token } : {},
503
+ addedAt: normalizeString(existing?.addedAt) || new Date().toISOString(),
504
+ ...normalizeString(existing?.lastConnected) ? { lastConnected: normalizeString(existing?.lastConnected) } : {}
505
+ };
506
+ const nextConfig = {
507
+ version: config.version ?? 1,
508
+ remotes: {
509
+ ...config.remotes ?? {},
510
+ [input.alias]: nextEntry
511
+ }
512
+ };
513
+ saveRemotesConfig(nextConfig, configPath);
514
+ return {
515
+ id: input.alias,
516
+ alias: input.alias,
517
+ host: input.host,
518
+ port: input.port,
519
+ token: input.token ?? "",
520
+ addedAt: nextEntry.addedAt ?? null,
521
+ lastConnected: nextEntry.lastConnected ?? null
522
+ };
523
+ }
524
+ function removeManagedRemoteEndpoint(alias, configPath = REMOTES_CONFIG_PATH, projectRoot) {
525
+ if (projectRoot) {
526
+ return removeAuthorityRemoteEndpoint(projectRoot, alias);
527
+ }
528
+ const config = loadRemotesConfig(configPath);
529
+ if (!config.remotes?.[alias]) {
530
+ return false;
531
+ }
532
+ const nextRemotes = { ...config.remotes ?? {} };
533
+ delete nextRemotes[alias];
534
+ saveRemotesConfig({
535
+ version: config.version ?? 1,
536
+ remotes: nextRemotes
537
+ }, configPath);
538
+ return true;
539
+ }
540
+ function normalizeString(value) {
541
+ if (!value) {
542
+ return "";
543
+ }
544
+ return value.trim();
545
+ }
546
+ export {
547
+ upsertManagedRemoteEndpoint,
548
+ updateAuthorityRemoteEndpoint,
549
+ resolveSshTarget,
550
+ resolveSelectedRemote,
551
+ resolveRigOmpConfigOverlayPath,
552
+ resolveRelayUrl,
553
+ resolveRegistryBaseUrl,
554
+ resolveOwnerNamespaceKey,
555
+ resolveDispatchTransportPlacement,
556
+ removeManagedRemoteEndpoint,
557
+ registerBackboneDefaults,
558
+ normalizeString,
559
+ markAuthorityRemoteEndpointConnected,
560
+ loadRemotesConfig,
561
+ listManagedRemoteEndpoints,
562
+ listAuthorityRemoteEndpoints,
563
+ importLegacyRemoteEndpoints,
564
+ doctorAuthorityRemoteEndpoints,
565
+ RemoteCliError,
566
+ REMOTES_CONFIG_PATH,
567
+ DEFAULT_REMOTE_PORT
568
+ };
@@ -0,0 +1,5 @@
1
+ export declare function resolveProjectRoot(options?: {
2
+ cwd?: string;
3
+ projectRootEnv?: string;
4
+ fallbackFromDir?: string;
5
+ }): string;
@@ -0,0 +1,69 @@
1
+ // @bun
2
+ // packages/core/src/root-resolver.ts
3
+ import { existsSync, readFileSync } from "fs";
4
+ import { dirname, parse, resolve } from "path";
5
+
6
+ // packages/core/src/layout.ts
7
+ import {
8
+ RIG_ARTIFACTS_DIRNAME,
9
+ RIG_DEFINITION_DIRNAME,
10
+ RIG_STATE_DIRNAME
11
+ } from "@rig/contracts";
12
+
13
+ // packages/core/src/root-resolver.ts
14
+ function hasProjectMarker(candidate) {
15
+ return existsSync(resolve(candidate, RIG_DEFINITION_DIRNAME)) || existsSync(resolve(candidate, RIG_STATE_DIRNAME));
16
+ }
17
+ function resolveProjectRoot(options) {
18
+ const cwd = options?.cwd || process.cwd();
19
+ const envRoot = options?.projectRootEnv || process.env.PROJECT_RIG_ROOT || "";
20
+ const fallbackFromDir = options?.fallbackFromDir || cwd;
21
+ if (envRoot && hasProjectMarker(envRoot)) {
22
+ return envRoot;
23
+ }
24
+ const walked = walkUpForRoot(cwd);
25
+ if (walked) {
26
+ return walked;
27
+ }
28
+ const configRoot = readConfiguredRoot();
29
+ if (configRoot && hasProjectMarker(configRoot)) {
30
+ return configRoot;
31
+ }
32
+ let fileDir = resolve(fallbackFromDir);
33
+ for (let i = 0;i < 5; i += 1) {
34
+ if (hasProjectMarker(fileDir)) {
35
+ return fileDir;
36
+ }
37
+ fileDir = dirname(fileDir);
38
+ }
39
+ return cwd;
40
+ }
41
+ function walkUpForRoot(start) {
42
+ let searchDir = resolve(start);
43
+ const root = parse(searchDir).root || "/";
44
+ while (searchDir !== root) {
45
+ if (hasProjectMarker(searchDir)) {
46
+ return searchDir;
47
+ }
48
+ searchDir = dirname(searchDir);
49
+ }
50
+ if (hasProjectMarker(root)) {
51
+ return root;
52
+ }
53
+ return "";
54
+ }
55
+ function readConfiguredRoot() {
56
+ const configPath = resolve(process.env.HOME || "~", ".config", "project-rig", "root");
57
+ if (!existsSync(configPath)) {
58
+ return "";
59
+ }
60
+ try {
61
+ const value = readFileSync(configPath, "utf-8").split(/\r?\n/)[0]?.trim() || "";
62
+ return value;
63
+ } catch {
64
+ return "";
65
+ }
66
+ }
67
+ export {
68
+ resolveProjectRoot
69
+ };
@@ -0,0 +1,58 @@
1
+ export declare const RUN_WORKSPACE_ISOLATION_MODE: "worktree";
2
+ export declare const RUN_WORKSPACE_PROVIDER: "pi";
3
+ export declare const REMOTE_RIG_RUN_COMMAND: "rig-run";
4
+ export declare const REMOTE_RESOLVED_RIG_RUN_COMMAND: "\"$__RIG_RUN_BIN\"";
5
+ export type RunWorkspaceProvisioningOptions = {
6
+ readonly projectRoot: string;
7
+ readonly id: string;
8
+ readonly taskId: string;
9
+ readonly mode: typeof RUN_WORKSPACE_ISOLATION_MODE;
10
+ readonly provider: typeof RUN_WORKSPACE_PROVIDER;
11
+ };
12
+ export type RigRunLaunchOptions = {
13
+ readonly taskId: string;
14
+ readonly title?: string | null | undefined;
15
+ readonly model?: string | null | undefined;
16
+ readonly prompt?: string | null | undefined;
17
+ readonly forceSteerOnce?: boolean | undefined;
18
+ };
19
+ export type RemoteCheckoutPlan = {
20
+ readonly mode: "configured" | "managed";
21
+ readonly repoName: string;
22
+ readonly checkoutPath: string;
23
+ readonly checkoutPathKind: "literal" | "home-relative";
24
+ readonly originUrl: string | null;
25
+ };
26
+ export type RunGithubAuthMaterialization = {
27
+ readonly operatorAuthPath: string;
28
+ readonly operatorStateDir: string;
29
+ readonly remoteAuthPath: string;
30
+ readonly remoteStateDir: string;
31
+ };
32
+ export declare function buildRunWorkspaceProvisioningOptions(input: {
33
+ readonly projectRoot: string;
34
+ readonly runId: string;
35
+ readonly taskId: string;
36
+ }): RunWorkspaceProvisioningOptions;
37
+ export declare function buildRigRunLaunchArgs(input: RigRunLaunchOptions, runId: string, projectRoot: string): string[];
38
+ export declare function resolveRemoteCheckoutPlan(input: {
39
+ readonly selectedCheckout?: string | null;
40
+ readonly projectSlug?: string | null;
41
+ }): RemoteCheckoutPlan;
42
+ export declare function resolveRunGithubAuthMaterialization(projectRoot: string, checkoutExpression?: string): RunGithubAuthMaterialization;
43
+ export declare function readGithubAuthStateBase64(operatorAuthPath: string): string;
44
+ export declare function buildRemoteRigRunResolutionShell(installUrl: string, command?: "rig-run"): string[];
45
+ export declare function isCompiledBunBinary(importMetaUrl: string): boolean;
46
+ export declare function resolveCompiledRigRunSiblingPath(execPath: string, platform?: NodeJS.Platform): string;
47
+ export declare function ensureRigRunSiblingCurrent(rigBin: string, rigRun: string): void;
48
+ export declare function resolveLocalDispatchRigRunBin(input: {
49
+ readonly importMetaUrl: string;
50
+ readonly sourceRigRunPath: string;
51
+ readonly execPath?: string;
52
+ readonly platform?: NodeJS.Platform;
53
+ }): string;
54
+ export declare function resolveInProcessRigRunBin(input: {
55
+ readonly importMetaUrl: string;
56
+ readonly sourceRigRunPath: string;
57
+ readonly execPath?: string;
58
+ }): string;