run402 4.24.1 → 4.25.0

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 (112) hide show
  1. package/cli.mjs +6 -6
  2. package/git-remote-run402.mjs +270 -0
  3. package/lib/command-manifest.mjs +11 -1
  4. package/lib/credentials.mjs +4 -0
  5. package/lib/domains.mjs +0 -14
  6. package/lib/gitvault.mjs +370 -0
  7. package/lib/init.mjs +143 -1
  8. package/lib/pay.test.mjs +0 -4
  9. package/package.json +8 -2
  10. package/sdk/dist/index.d.ts +20 -3
  11. package/sdk/dist/index.d.ts.map +1 -1
  12. package/sdk/dist/index.js +22 -3
  13. package/sdk/dist/index.js.map +1 -1
  14. package/sdk/dist/namespaces/admin.d.ts +0 -5
  15. package/sdk/dist/namespaces/admin.d.ts.map +1 -1
  16. package/sdk/dist/namespaces/admin.js +0 -9
  17. package/sdk/dist/namespaces/admin.js.map +1 -1
  18. package/sdk/dist/namespaces/assets.d.ts +1 -20
  19. package/sdk/dist/namespaces/assets.d.ts.map +1 -1
  20. package/sdk/dist/namespaces/assets.js +0 -27
  21. package/sdk/dist/namespaces/assets.js.map +1 -1
  22. package/sdk/dist/namespaces/domains.d.ts +0 -19
  23. package/sdk/dist/namespaces/domains.d.ts.map +1 -1
  24. package/sdk/dist/namespaces/domains.js +0 -19
  25. package/sdk/dist/namespaces/domains.js.map +1 -1
  26. package/sdk/dist/namespaces/gitvault.crypto.d.ts +319 -0
  27. package/sdk/dist/namespaces/gitvault.crypto.d.ts.map +1 -0
  28. package/sdk/dist/namespaces/gitvault.crypto.js +690 -0
  29. package/sdk/dist/namespaces/gitvault.crypto.js.map +1 -0
  30. package/sdk/dist/namespaces/gitvault.d.ts +473 -0
  31. package/sdk/dist/namespaces/gitvault.d.ts.map +1 -0
  32. package/sdk/dist/namespaces/gitvault.js +714 -0
  33. package/sdk/dist/namespaces/gitvault.js.map +1 -0
  34. package/sdk/dist/namespaces/gitvault.types.d.ts +481 -0
  35. package/sdk/dist/namespaces/gitvault.types.d.ts.map +1 -0
  36. package/sdk/dist/namespaces/gitvault.types.js +19 -0
  37. package/sdk/dist/namespaces/gitvault.types.js.map +1 -0
  38. package/sdk/dist/namespaces/org.d.ts +1 -3
  39. package/sdk/dist/namespaces/org.d.ts.map +1 -1
  40. package/sdk/dist/namespaces/org.js +6 -10
  41. package/sdk/dist/namespaces/org.js.map +1 -1
  42. package/sdk/dist/namespaces/pay.d.ts +0 -8
  43. package/sdk/dist/namespaces/pay.d.ts.map +1 -1
  44. package/sdk/dist/namespaces/pay.js.map +1 -1
  45. package/sdk/dist/namespaces/projects.d.ts +2 -2
  46. package/sdk/dist/namespaces/projects.d.ts.map +1 -1
  47. package/sdk/dist/namespaces/projects.js +4 -12
  48. package/sdk/dist/namespaces/projects.js.map +1 -1
  49. package/sdk/dist/namespaces/secrets.d.ts +0 -2
  50. package/sdk/dist/namespaces/secrets.d.ts.map +1 -1
  51. package/sdk/dist/namespaces/secrets.js +3 -10
  52. package/sdk/dist/namespaces/secrets.js.map +1 -1
  53. package/sdk/dist/namespaces/subdomains.d.ts +0 -2
  54. package/sdk/dist/namespaces/subdomains.d.ts.map +1 -1
  55. package/sdk/dist/namespaces/subdomains.js +5 -16
  56. package/sdk/dist/namespaces/subdomains.js.map +1 -1
  57. package/sdk/dist/namespaces/transfers.d.ts +0 -2
  58. package/sdk/dist/namespaces/transfers.d.ts.map +1 -1
  59. package/sdk/dist/namespaces/transfers.js +8 -10
  60. package/sdk/dist/namespaces/transfers.js.map +1 -1
  61. package/sdk/dist/namespaces/wallets.d.ts +0 -12
  62. package/sdk/dist/namespaces/wallets.d.ts.map +1 -1
  63. package/sdk/dist/namespaces/wallets.js +0 -14
  64. package/sdk/dist/namespaces/wallets.js.map +1 -1
  65. package/sdk/dist/node/gitvault-creation-journal.d.ts +222 -0
  66. package/sdk/dist/node/gitvault-creation-journal.d.ts.map +1 -0
  67. package/sdk/dist/node/gitvault-creation-journal.js +365 -0
  68. package/sdk/dist/node/gitvault-creation-journal.js.map +1 -0
  69. package/sdk/dist/node/gitvault-deploy.d.ts +211 -0
  70. package/sdk/dist/node/gitvault-deploy.d.ts.map +1 -0
  71. package/sdk/dist/node/gitvault-deploy.js +292 -0
  72. package/sdk/dist/node/gitvault-deploy.js.map +1 -0
  73. package/sdk/dist/node/gitvault-keystore.d.ts +207 -0
  74. package/sdk/dist/node/gitvault-keystore.d.ts.map +1 -0
  75. package/sdk/dist/node/gitvault-keystore.js +529 -0
  76. package/sdk/dist/node/gitvault-keystore.js.map +1 -0
  77. package/sdk/dist/node/gitvault-prune.d.ts +341 -0
  78. package/sdk/dist/node/gitvault-prune.d.ts.map +1 -0
  79. package/sdk/dist/node/gitvault-prune.js +410 -0
  80. package/sdk/dist/node/gitvault-prune.js.map +1 -0
  81. package/sdk/dist/node/gitvault-publication.d.ts +730 -0
  82. package/sdk/dist/node/gitvault-publication.d.ts.map +1 -0
  83. package/sdk/dist/node/gitvault-publication.js +1475 -0
  84. package/sdk/dist/node/gitvault-publication.js.map +1 -0
  85. package/sdk/dist/node/gitvault-snapshot.d.ts +190 -0
  86. package/sdk/dist/node/gitvault-snapshot.d.ts.map +1 -0
  87. package/sdk/dist/node/gitvault-snapshot.js +505 -0
  88. package/sdk/dist/node/gitvault-snapshot.js.map +1 -0
  89. package/sdk/dist/node/index.d.ts +11 -0
  90. package/sdk/dist/node/index.d.ts.map +1 -1
  91. package/sdk/dist/node/index.js +9 -0
  92. package/sdk/dist/node/index.js.map +1 -1
  93. package/sdk/dist/node/paid-fetch.d.ts.map +1 -1
  94. package/sdk/dist/node/paid-fetch.js +0 -4
  95. package/sdk/dist/node/paid-fetch.js.map +1 -1
  96. package/sdk/dist/node/sites-node.d.ts +1 -8
  97. package/sdk/dist/node/sites-node.d.ts.map +1 -1
  98. package/sdk/dist/node/sites-node.js +1 -7
  99. package/sdk/dist/node/sites-node.js.map +1 -1
  100. package/sdk/dist/scoped.d.ts +4 -35
  101. package/sdk/dist/scoped.d.ts.map +1 -1
  102. package/sdk/dist/scoped.js +9 -74
  103. package/sdk/dist/scoped.js.map +1 -1
  104. package/lib/sender-domain.mjs +0 -36
  105. package/sdk/dist/deprecate.d.ts +0 -23
  106. package/sdk/dist/deprecate.d.ts.map +0 -1
  107. package/sdk/dist/deprecate.js +0 -42
  108. package/sdk/dist/deprecate.js.map +0 -1
  109. package/sdk/dist/namespaces/sender-domain.d.ts +0 -47
  110. package/sdk/dist/namespaces/sender-domain.d.ts.map +0 -1
  111. package/sdk/dist/namespaces/sender-domain.js +0 -45
  112. package/sdk/dist/namespaces/sender-domain.js.map +0 -1
package/cli.mjs CHANGED
@@ -49,6 +49,7 @@ Commands:
49
49
  rooms Coordinate with the other agents on your project (who/send/ack)
50
50
  escalations Page a human when you judge you need one (raise/list/ack)
51
51
  claims Say what you're working on before you collide (advisory)
52
+ gitvault Host-blind encrypted Git remote (status/push/compact/prune/verify)
52
53
  errors Grouped error fingerprints + a promote/revert verdict (release-baselined)
53
54
  jobs Submit and inspect platform-managed jobs
54
55
  functions Manage serverless functions (deploy, invoke, logs, list, delete)
@@ -64,7 +65,6 @@ Commands:
64
65
  email Send template-based emails from your project
65
66
  message Send messages to Run402 developers
66
67
  auth Manage project user authentication (magic link, passwords, settings)
67
- sender-domain Removed; use domains connect/status/repair
68
68
  billing Email organizations, Stripe tier checkout, email packs
69
69
  contracts KMS signers ($0.04/day rental + $0.000005/sign)
70
70
  agent Manage agent identity (contact info)
@@ -292,6 +292,11 @@ switch (cmd) {
292
292
  await run(sub, rest);
293
293
  break;
294
294
  }
295
+ case "gitvault": {
296
+ const { run } = await import("./lib/gitvault.mjs");
297
+ await run(sub, rest);
298
+ break;
299
+ }
295
300
  case "escalations": {
296
301
  const { run } = await import("./lib/escalations.mjs");
297
302
  await run(sub, rest);
@@ -382,11 +387,6 @@ switch (cmd) {
382
387
  await run(sub, rest);
383
388
  break;
384
389
  }
385
- case "sender-domain": {
386
- const { run } = await import("./lib/sender-domain.mjs");
387
- await run(sub, rest);
388
- break;
389
- }
390
390
  case "billing": {
391
391
  const { run } = await import("./lib/billing.mjs");
392
392
  await run(sub, rest);
@@ -0,0 +1,270 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * `git-remote-run402` — the git remote helper for gitvault, so plain
4
+ * `git clone|fetch|push run402::<org_id>/<project_id>` speaks to a host-blind
5
+ * encrypted vault with no run402-specific git ceremony.
6
+ *
7
+ * ARCHITECTURAL LAW (gitvault-client-surface, "All protocol logic lives in the
8
+ * SDK"): this file is a THIN ADAPTER over `r.gitvault`. It translates git's
9
+ * remote-helper wire protocol into SDK calls and back, and does nothing else.
10
+ * No crypto, no HTTP, no ref-policy decisions, no pack building — those live
11
+ * once in `@run402/sdk`. Even git itself is invoked only through the SDK's own
12
+ * `hardenedGit` (hooks, fsmonitor, replace-refs and filter autodetection are
13
+ * neutralized there), so this helper hand-rolls no git behaviour either.
14
+ *
15
+ * PROTOCOL SURFACE (gitremote-helpers(1)). Implemented:
16
+ * capabilities → advertises fetch, push, option
17
+ * list [for-push] → the vault's canonical ref map + the HEAD target
18
+ * fetch <sha1> <name> → restore the vault's object database into this repo
19
+ * push [+]<src>:<dst> → publish one atomic ref transaction
20
+ * option <name> <value> → ok / unsupported, never a silent lie
21
+ *
22
+ * NOT advertised, deliberately: `list` is a COMMAND in this protocol, not a
23
+ * capability keyword — git's capability vocabulary is fetch/push/import/export/
24
+ * connect/stateless-connect/option/refspec/check-connectivity/object-format/
25
+ * signed-tags/bidi-import/get. Advertising `list` would be a line git silently
26
+ * discards; the command itself is implemented above. `connect`,
27
+ * `stateless-connect`, `import`, and `export` are NOT implemented: the vault is
28
+ * not a git-protocol endpoint you can tunnel to, and pretending otherwise would
29
+ * hand git a transport that cannot answer.
30
+ *
31
+ * KNOWN LIMITS, stated rather than papered over:
32
+ * - `fetch` restores the vault's object database WHOLESALE. The SDK exposes
33
+ * no per-ref object selection, so one batch = one full restore. That is a
34
+ * superset of what git asked for (git writes the refs itself from `list`),
35
+ * never a subset — but it is not incremental.
36
+ * - `push` never changes the vault's HEAD target; the SDK carries it forward.
37
+ * A fresh vault defaults to `refs/heads/main`, so a first push of some
38
+ * other branch leaves HEAD naming a ref that does not exist yet. Use
39
+ * `run402 gitvault push`, which sets the HEAD target from the local HEAD.
40
+ * - `option dry-run` is `unsupported`: this helper cannot rehearse a
41
+ * publication, and reporting a fake success would be worse than refusing.
42
+ * - The repository is discovered from `process.cwd()` (git's own upward
43
+ * discovery). `hardenedGit` scrubs `GIT_DIR`/`GIT_WORK_TREE` on purpose, so
44
+ * an invocation from outside the work tree is not supported.
45
+ */
46
+
47
+ import { createInterface } from "node:readline";
48
+ import { getSdk } from "./lib/sdk.mjs";
49
+ import { parseGitvaultRemoteUrl } from "#sdk";
50
+ import { hardenedGit } from "#sdk/node";
51
+
52
+ const out = (line) => process.stdout.write(`${line}\n`);
53
+ /** Every helper response block is terminated by a blank line. */
54
+ const endBlock = () => process.stdout.write("\n");
55
+ const note = (line) => process.stderr.write(`git-remote-run402: ${line}\n`);
56
+
57
+ /** Protocol lines are single-line: collapse anything that could break framing. */
58
+ function oneLine(value) {
59
+ return String(value ?? "").replace(/\s+/g, " ").trim().slice(0, 400);
60
+ }
61
+
62
+ function describeError(err) {
63
+ const code = err?.code ?? err?.body?.code ?? null;
64
+ const message = err?.message ?? err?.body?.message ?? String(err);
65
+ return oneLine(code ? `${code}: ${message}` : message);
66
+ }
67
+
68
+ /**
69
+ * Resolve the vault address from git's argv.
70
+ *
71
+ * Git invokes `git-remote-<transport> <remote> <url>`, and for a
72
+ * `<transport>::<address>` URL it passes the BARE `<address>` as the second
73
+ * argument — the `run402::` prefix is already stripped. The SDK's parser is the
74
+ * only thing that understands the address grammar, so both spellings are handed
75
+ * to it rather than re-implemented here.
76
+ */
77
+ function resolveRemoteAddress(argv) {
78
+ for (const raw of [argv[1], argv[0]]) {
79
+ if (typeof raw !== "string" || raw.length === 0) continue;
80
+ const direct = parseGitvaultRemoteUrl(raw);
81
+ if (direct) return direct;
82
+ // Re-add the prefix ONLY for a bare, colon-free address. Without this
83
+ // guard `https://example.com/x` parses as org `https:` / project
84
+ // `/example.com/x`, and `git@github.com:x/y.git` as org `git@github.com:x`
85
+ // — a confidently wrong answer is worse than no answer here. The colon is
86
+ // git's own URL punctuation, never part of a bare `<org_id>/<project_id>`.
87
+ if (raw.includes(":")) continue;
88
+ const prefixed = parseGitvaultRemoteUrl(`run402::${raw}`);
89
+ if (prefixed) return prefixed;
90
+ }
91
+ return null;
92
+ }
93
+
94
+ /** `[+]<src>:<dst>` — an empty `<src>` is a deletion. */
95
+ function parsePushSpec(spec) {
96
+ const forced = spec.startsWith("+");
97
+ const body = forced ? spec.slice(1) : spec;
98
+ const colon = body.indexOf(":");
99
+ if (colon === -1) return { src: body, dst: body, force: forced };
100
+ return { src: body.slice(0, colon), dst: body.slice(colon + 1), force: forced };
101
+ }
102
+
103
+ async function main(argv) {
104
+ const address = resolveRemoteAddress(argv);
105
+ if (!address) {
106
+ note(`could not read a run402 remote address from ${JSON.stringify(argv.join(" "))} — expected run402::<org_id>/<project_id>`);
107
+ return 1;
108
+ }
109
+
110
+ const repoDir = process.cwd();
111
+ const target = { project_id: address.project_id, repo_dir: repoDir };
112
+ let verbosity = 1;
113
+
114
+ /** Open the vault lazily — `capabilities` and `option` must never touch the network. */
115
+ const openVault = async () => (await getSdk().gitvault.open(target)).vault;
116
+
117
+ async function runList() {
118
+ const state = await (await openVault()).materialize();
119
+ const refs = state.refs ?? {};
120
+ for (const ref of Object.keys(refs).sort()) out(`${refs[ref]} ${ref}`);
121
+ const head = state.head_target;
122
+ // A symref is only advertised when its target is actually present:
123
+ // pointing HEAD at a ref that does not exist is what an empty repository
124
+ // looks like, and git reads the empty list correctly on its own.
125
+ if (head?.kind === "symref" && Object.prototype.hasOwnProperty.call(refs, head.ref)) out(`@${head.ref} HEAD`);
126
+ else if (head?.kind === "detached") out(`${head.oid} HEAD`);
127
+ endBlock();
128
+ }
129
+
130
+ async function runFetch(batch) {
131
+ if (verbosity >= 1) note(`restoring the vault object database for ${batch.length} ref(s)`);
132
+ const restored = await getSdk().gitvault.restore({ ...target, target_dir: repoDir });
133
+ if (verbosity >= 1) note(`restored generation ${restored.generation}`);
134
+ endBlock();
135
+ }
136
+
137
+ async function runPush(batch) {
138
+ const specs = batch.map(parsePushSpec);
139
+ try {
140
+ const vault = await openVault();
141
+ const base = await vault.materialize();
142
+ const updates = [];
143
+ for (const spec of specs) {
144
+ const expectedOld = base.refs?.[spec.dst] ?? null;
145
+ // A deletion carries an empty <src>. Everything else is resolved by
146
+ // git itself; `--end-of-options` keeps a hostile refname from being
147
+ // read as a flag.
148
+ const newOid = spec.src === ""
149
+ ? null
150
+ : (await hardenedGit(repoDir, ["rev-parse", "--verify", "--end-of-options", spec.src])).text().trim();
151
+ updates.push({
152
+ ref: spec.dst,
153
+ expected_old_oid: expectedOld,
154
+ new_oid: newOid,
155
+ // Force-with-lease still requires a lease, so a CREATE is never
156
+ // forced. The SDK owns what force actually permits.
157
+ force: spec.force && expectedOld !== null,
158
+ });
159
+ }
160
+ // ONE transaction for the whole batch: the SDK evaluates fast-forward,
161
+ // tag immutability, protocol-ref refusal and retention roots, builds the
162
+ // packs, and publishes — all or nothing.
163
+ const published = await vault.push({ transaction: { updates } });
164
+ if (verbosity >= 1) note(`published generation ${published.generation} (${published.form})`);
165
+ for (const spec of specs) out(`ok ${spec.dst}`);
166
+ } catch (err) {
167
+ // The transaction is atomic, so a failure failed every ref in it. Report
168
+ // it against each one rather than letting some look like they landed.
169
+ const reason = describeError(err);
170
+ for (const spec of specs) out(`error ${spec.dst} ${reason}`);
171
+ }
172
+ endBlock();
173
+ }
174
+
175
+ function handleOption(name, value) {
176
+ switch (name) {
177
+ case "verbosity": {
178
+ const parsed = Number.parseInt(value, 10);
179
+ if (!Number.isFinite(parsed)) { out("error expected an integer verbosity"); return; }
180
+ verbosity = parsed;
181
+ out("ok");
182
+ return;
183
+ }
184
+ case "progress":
185
+ // Progress is stderr chatter, which `verbosity` already governs.
186
+ out("ok");
187
+ return;
188
+ case "atomic":
189
+ // Every push here is a single ref transaction, so the guarantee holds
190
+ // whichever way git asked for it.
191
+ out("ok");
192
+ return;
193
+ default:
194
+ // Includes dry-run, object-format, depth, cloning, check-connectivity,
195
+ // followtags, pushcert: honestly unsupported rather than acknowledged.
196
+ out("unsupported");
197
+ }
198
+ }
199
+
200
+ let fetchBatch = [];
201
+ let pushBatch = [];
202
+
203
+ async function flushBatches() {
204
+ if (fetchBatch.length > 0) {
205
+ const batch = fetchBatch;
206
+ fetchBatch = [];
207
+ await runFetch(batch);
208
+ return;
209
+ }
210
+ if (pushBatch.length > 0) {
211
+ const batch = pushBatch;
212
+ pushBatch = [];
213
+ await runPush(batch);
214
+ }
215
+ }
216
+
217
+ const rl = createInterface({ input: process.stdin, crlfDelay: Infinity });
218
+ try {
219
+ for await (const raw of rl) {
220
+ const line = raw.replace(/\r$/, "");
221
+ if (line === "") {
222
+ await flushBatches();
223
+ continue;
224
+ }
225
+ const space = line.indexOf(" ");
226
+ const command = space === -1 ? line : line.slice(0, space);
227
+ const rest = space === -1 ? "" : line.slice(space + 1);
228
+ switch (command) {
229
+ case "capabilities":
230
+ out("fetch");
231
+ out("push");
232
+ out("option");
233
+ endBlock();
234
+ break;
235
+ case "list":
236
+ await runList();
237
+ break;
238
+ case "option": {
239
+ const optSpace = rest.indexOf(" ");
240
+ handleOption(optSpace === -1 ? rest : rest.slice(0, optSpace), optSpace === -1 ? "" : rest.slice(optSpace + 1));
241
+ break;
242
+ }
243
+ case "fetch":
244
+ fetchBatch.push(rest);
245
+ break;
246
+ case "push":
247
+ pushBatch.push(rest);
248
+ break;
249
+ default:
250
+ note(`unknown command: ${oneLine(line)}`);
251
+ return 1;
252
+ }
253
+ }
254
+ // EOF. Git always terminates a batch with a blank line, but flushing here
255
+ // means a truncated stream still does the work it already asked for
256
+ // instead of silently dropping it.
257
+ await flushBatches();
258
+ return 0;
259
+ } finally {
260
+ rl.close();
261
+ }
262
+ }
263
+
264
+ // Never `process.exit()` mid-stream: that can truncate a pending stdout write
265
+ // on a pipe, which git reads as a protocol violation. Set the code and let Node
266
+ // flush and exit on its own.
267
+ main(process.argv.slice(2)).then(
268
+ (code) => { process.exitCode = code; },
269
+ (err) => { note(describeError(err)); process.exitCode = 1; },
270
+ );
@@ -232,6 +232,17 @@ export const COMMAND_MANIFEST = [
232
232
  { path: ["claims", "create"], positionals: [p("resource")], projectScoped: true, legacyPositionalProject: false, minimalArgs: ["deploy"], runStyle: "sub" },
233
233
  { path: ["claims", "list"], positionals: [], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub" },
234
234
  { path: ["claims", "release"], positionals: [p("claim_id")], projectScoped: true, legacyPositionalProject: false, minimalArgs: ["clm_1"], runStyle: "sub" },
235
+
236
+ // ── gitvault (host-blind encrypted Git remote) ───────────────────────────
237
+ // Every verb needs a real principal keystore, an allocated vault, and (for
238
+ // all but `status`) a local git working tree, so the gate runs structural
239
+ // checks only — an in-process behavioral run would either no-op against the
240
+ // universal `{}` fetch mock or touch the gate's own checkout.
241
+ { path: ["gitvault", "status"], positionals: [], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub", skipBehavioral: "reads the local principal keystore and the live vault record" },
242
+ { path: ["gitvault", "push"], positionals: [], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub", skipBehavioral: "captures the cwd git working tree and publishes a signed head" },
243
+ { path: ["gitvault", "compact"], positionals: [], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub", skipBehavioral: "takes a maintenance lease and builds a checkpoint from the local repository" },
244
+ { path: ["gitvault", "prune"], positionals: [], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub", skipBehavioral: "materializes the live vault head to enumerate retention roots" },
245
+ { path: ["gitvault", "verify"], positionals: [], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub", skipBehavioral: "walks the live head chain against the keystore's authenticated pin" },
235
246
  { path: ["errors"], positionals: [p("fingerprint_id", { required: false })], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "merged" },
236
247
 
237
248
  // ── jobs ─────────────────────────────────────────────────────────────────
@@ -403,6 +414,5 @@ export const COMMAND_MANIFEST = [
403
414
  // completeness check against cli.mjs's dispatch switch.
404
415
  export const SKIPPED_FAMILIES = {
405
416
  "apply": "pure alias for `deploy apply` (covered by the deploy family)",
406
- "sender-domain": "removed command — every subcommand errors with COMMAND_REMOVED",
407
417
  "dev": "interactive wrapper that spawns `astro dev`",
408
418
  };
@@ -324,6 +324,10 @@ async function importKey(args) {
324
324
  site_url: existing?.site_url,
325
325
  deployed_at: existing?.deployed_at,
326
326
  last_deployment_id: existing?.last_deployment_id,
327
+ // Carried, not re-derived: a key import says nothing about which org owns
328
+ // the project, and dropping the cache here would make `run402 init` reach
329
+ // for the control plane again on a machine that already knew.
330
+ org_id: existing?.org_id,
327
331
  source: "manual_import",
328
332
  cached_at: new Date().toISOString(),
329
333
  });
package/lib/domains.mjs CHANGED
@@ -20,9 +20,6 @@ Subcommands:
20
20
  wait <domain> [--project <id>] [--until active|safe|receive-active] [--timeout-ms <n>] [--interval-ms <n>]
21
21
  activate <domain> [--project <id>]
22
22
  disconnect <domain> --confirm [--project <id>]
23
-
24
- Removed:
25
- add, delete. Use connect/disconnect.
26
23
  `;
27
24
 
28
25
  const SUB_HELP = {
@@ -135,15 +132,6 @@ function printHandoff(data) {
135
132
  console.error(lines.join("\n"));
136
133
  }
137
134
 
138
- function removed(command, replacement) {
139
- fail({
140
- code: "COMMAND_REMOVED",
141
- message: `${command} has been removed. Use ${replacement}.`,
142
- details: { command, replacement },
143
- next_actions: [{ type: "use_replacement_command", command: replacement }],
144
- });
145
- }
146
-
147
135
  function parseCommon(args, extraKnown = [], valueFlags = COMMON_VALUE_FLAGS) {
148
136
  const parsed = normalizeArgv(args);
149
137
  assertKnownFlags(parsed, [...valueFlags, ...extraKnown, "--help", "-h"], valueFlags);
@@ -447,8 +435,6 @@ export async function run(sub, args) {
447
435
  case "wait": await action("wait", args); break;
448
436
  case "activate": await action("activate", args); break;
449
437
  case "disconnect": await disconnect(args); break;
450
- case "add": removed("run402 domains add", "run402 domains connect <domain> --project <id> --web"); break;
451
- case "delete": removed("run402 domains delete", "run402 domains disconnect <domain> --project <id> --confirm"); break;
452
438
  default:
453
439
  failUnknownSubcommand("domains", sub);
454
440
  }