run402 4.24.0 → 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.
- package/cli.mjs +6 -6
- package/git-remote-run402.mjs +270 -0
- package/lib/command-manifest.mjs +11 -1
- package/lib/credentials.mjs +4 -0
- package/lib/domains.mjs +0 -14
- package/lib/gitvault.mjs +370 -0
- package/lib/init.mjs +143 -1
- package/lib/pay.test.mjs +0 -4
- package/package.json +8 -2
- package/sdk/dist/index.d.ts +20 -3
- package/sdk/dist/index.d.ts.map +1 -1
- package/sdk/dist/index.js +22 -3
- package/sdk/dist/index.js.map +1 -1
- package/sdk/dist/namespaces/admin.d.ts +0 -5
- package/sdk/dist/namespaces/admin.d.ts.map +1 -1
- package/sdk/dist/namespaces/admin.js +0 -9
- package/sdk/dist/namespaces/admin.js.map +1 -1
- package/sdk/dist/namespaces/assets.d.ts +1 -20
- package/sdk/dist/namespaces/assets.d.ts.map +1 -1
- package/sdk/dist/namespaces/assets.js +0 -27
- package/sdk/dist/namespaces/assets.js.map +1 -1
- package/sdk/dist/namespaces/auth.js +1 -1
- package/sdk/dist/namespaces/auth.js.map +1 -1
- package/sdk/dist/namespaces/domains.d.ts +0 -19
- package/sdk/dist/namespaces/domains.d.ts.map +1 -1
- package/sdk/dist/namespaces/domains.js +0 -19
- package/sdk/dist/namespaces/domains.js.map +1 -1
- package/sdk/dist/namespaces/gitvault.crypto.d.ts +319 -0
- package/sdk/dist/namespaces/gitvault.crypto.d.ts.map +1 -0
- package/sdk/dist/namespaces/gitvault.crypto.js +690 -0
- package/sdk/dist/namespaces/gitvault.crypto.js.map +1 -0
- package/sdk/dist/namespaces/gitvault.d.ts +473 -0
- package/sdk/dist/namespaces/gitvault.d.ts.map +1 -0
- package/sdk/dist/namespaces/gitvault.js +714 -0
- package/sdk/dist/namespaces/gitvault.js.map +1 -0
- package/sdk/dist/namespaces/gitvault.types.d.ts +481 -0
- package/sdk/dist/namespaces/gitvault.types.d.ts.map +1 -0
- package/sdk/dist/namespaces/gitvault.types.js +19 -0
- package/sdk/dist/namespaces/gitvault.types.js.map +1 -0
- package/sdk/dist/namespaces/org.d.ts +1 -3
- package/sdk/dist/namespaces/org.d.ts.map +1 -1
- package/sdk/dist/namespaces/org.js +6 -10
- package/sdk/dist/namespaces/org.js.map +1 -1
- package/sdk/dist/namespaces/pay.d.ts +0 -8
- package/sdk/dist/namespaces/pay.d.ts.map +1 -1
- package/sdk/dist/namespaces/pay.js.map +1 -1
- package/sdk/dist/namespaces/projects.d.ts +2 -2
- package/sdk/dist/namespaces/projects.d.ts.map +1 -1
- package/sdk/dist/namespaces/projects.js +4 -12
- package/sdk/dist/namespaces/projects.js.map +1 -1
- package/sdk/dist/namespaces/secrets.d.ts +0 -2
- package/sdk/dist/namespaces/secrets.d.ts.map +1 -1
- package/sdk/dist/namespaces/secrets.js +3 -10
- package/sdk/dist/namespaces/secrets.js.map +1 -1
- package/sdk/dist/namespaces/subdomains.d.ts +0 -2
- package/sdk/dist/namespaces/subdomains.d.ts.map +1 -1
- package/sdk/dist/namespaces/subdomains.js +5 -16
- package/sdk/dist/namespaces/subdomains.js.map +1 -1
- package/sdk/dist/namespaces/transfers.d.ts +0 -2
- package/sdk/dist/namespaces/transfers.d.ts.map +1 -1
- package/sdk/dist/namespaces/transfers.js +8 -10
- package/sdk/dist/namespaces/transfers.js.map +1 -1
- package/sdk/dist/namespaces/wallets.d.ts +0 -12
- package/sdk/dist/namespaces/wallets.d.ts.map +1 -1
- package/sdk/dist/namespaces/wallets.js +0 -14
- package/sdk/dist/namespaces/wallets.js.map +1 -1
- package/sdk/dist/node/gitvault-creation-journal.d.ts +222 -0
- package/sdk/dist/node/gitvault-creation-journal.d.ts.map +1 -0
- package/sdk/dist/node/gitvault-creation-journal.js +365 -0
- package/sdk/dist/node/gitvault-creation-journal.js.map +1 -0
- package/sdk/dist/node/gitvault-deploy.d.ts +211 -0
- package/sdk/dist/node/gitvault-deploy.d.ts.map +1 -0
- package/sdk/dist/node/gitvault-deploy.js +292 -0
- package/sdk/dist/node/gitvault-deploy.js.map +1 -0
- package/sdk/dist/node/gitvault-keystore.d.ts +207 -0
- package/sdk/dist/node/gitvault-keystore.d.ts.map +1 -0
- package/sdk/dist/node/gitvault-keystore.js +529 -0
- package/sdk/dist/node/gitvault-keystore.js.map +1 -0
- package/sdk/dist/node/gitvault-prune.d.ts +341 -0
- package/sdk/dist/node/gitvault-prune.d.ts.map +1 -0
- package/sdk/dist/node/gitvault-prune.js +410 -0
- package/sdk/dist/node/gitvault-prune.js.map +1 -0
- package/sdk/dist/node/gitvault-publication.d.ts +730 -0
- package/sdk/dist/node/gitvault-publication.d.ts.map +1 -0
- package/sdk/dist/node/gitvault-publication.js +1475 -0
- package/sdk/dist/node/gitvault-publication.js.map +1 -0
- package/sdk/dist/node/gitvault-snapshot.d.ts +190 -0
- package/sdk/dist/node/gitvault-snapshot.d.ts.map +1 -0
- package/sdk/dist/node/gitvault-snapshot.js +505 -0
- package/sdk/dist/node/gitvault-snapshot.js.map +1 -0
- package/sdk/dist/node/index.d.ts +11 -0
- package/sdk/dist/node/index.d.ts.map +1 -1
- package/sdk/dist/node/index.js +9 -0
- package/sdk/dist/node/index.js.map +1 -1
- package/sdk/dist/node/paid-fetch.d.ts.map +1 -1
- package/sdk/dist/node/paid-fetch.js +0 -4
- package/sdk/dist/node/paid-fetch.js.map +1 -1
- package/sdk/dist/node/sites-node.d.ts +1 -8
- package/sdk/dist/node/sites-node.d.ts.map +1 -1
- package/sdk/dist/node/sites-node.js +1 -7
- package/sdk/dist/node/sites-node.js.map +1 -1
- package/sdk/dist/scoped.d.ts +4 -35
- package/sdk/dist/scoped.d.ts.map +1 -1
- package/sdk/dist/scoped.js +9 -74
- package/sdk/dist/scoped.js.map +1 -1
- package/lib/sender-domain.mjs +0 -36
- package/sdk/dist/deprecate.d.ts +0 -23
- package/sdk/dist/deprecate.d.ts.map +0 -1
- package/sdk/dist/deprecate.js +0 -42
- package/sdk/dist/deprecate.js.map +0 -1
- package/sdk/dist/namespaces/sender-domain.d.ts +0 -47
- package/sdk/dist/namespaces/sender-domain.d.ts.map +0 -1
- package/sdk/dist/namespaces/sender-domain.js +0 -45
- 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
|
+
);
|
package/lib/command-manifest.mjs
CHANGED
|
@@ -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
|
};
|
package/lib/credentials.mjs
CHANGED
|
@@ -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
|
}
|