run402 4.40.0 → 4.42.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/README.md +7 -4
- package/cli.mjs +10 -2
- package/lib/argparse.mjs +1 -1
- package/lib/assets.mjs +10 -14
- package/lib/command-manifest.mjs +58 -49
- package/lib/deploy-v2.mjs +7 -10
- package/lib/doctor.mjs +12 -5
- package/lib/gitvault-scaffold.mjs +1 -1
- package/lib/gitvault-target.mjs +7 -9
- package/lib/gitvault.mjs +117 -1163
- package/lib/init.mjs +2 -2
- package/lib/notifications.mjs +4 -8
- package/lib/operator.mjs +1 -1
- package/lib/org.mjs +3 -3
- package/lib/repos.mjs +1261 -249
- package/lib/rooms-context.mjs +2 -3
- package/lib/rooms.mjs +11 -14
- package/lib/update-check.mjs +6 -8
- package/package.json +1 -1
- package/sdk/dist/errors.d.ts +5 -2
- package/sdk/dist/errors.d.ts.map +1 -1
- package/sdk/dist/errors.js.map +1 -1
- package/sdk/dist/namespaces/admin.d.ts +1 -1
- package/sdk/dist/namespaces/admin.js +1 -1
- package/sdk/dist/namespaces/deploy.types.d.ts +2 -2
- package/sdk/dist/namespaces/gitvault.crypto.d.ts +10 -0
- package/sdk/dist/namespaces/gitvault.crypto.d.ts.map +1 -1
- package/sdk/dist/namespaces/gitvault.crypto.js +11 -1
- package/sdk/dist/namespaces/gitvault.crypto.js.map +1 -1
- package/sdk/dist/namespaces/gitvault.d.ts +267 -31
- package/sdk/dist/namespaces/gitvault.d.ts.map +1 -1
- package/sdk/dist/namespaces/gitvault.js +299 -51
- package/sdk/dist/namespaces/gitvault.js.map +1 -1
- package/sdk/dist/namespaces/projects.d.ts +1 -1
- package/sdk/dist/namespaces/projects.js +1 -1
- package/sdk/dist/namespaces/projects.types.d.ts +1 -1
- package/sdk/dist/namespaces/projects.types.d.ts.map +1 -1
- package/sdk/dist/node/gitvault-apply.d.ts +1 -1
- package/sdk/dist/node/gitvault-apply.d.ts.map +1 -1
- package/sdk/dist/node/gitvault-apply.js +6 -6
- package/sdk/dist/node/gitvault-apply.js.map +1 -1
- package/sdk/dist/node/gitvault-creation-journal.js +1 -1
- package/sdk/dist/node/gitvault-creation-journal.js.map +1 -1
- package/sdk/dist/node/gitvault-deploy.js +2 -2
- package/sdk/dist/node/gitvault-deploy.js.map +1 -1
- package/sdk/dist/node/gitvault-mirror-backend.js +1 -1
- package/sdk/dist/node/gitvault-mirror-backend.js.map +1 -1
- package/sdk/dist/node/gitvault-mirror.js +1 -1
- package/sdk/dist/node/gitvault-mirror.js.map +1 -1
- package/sdk/dist/node/gitvault-publication.d.ts +70 -20
- package/sdk/dist/node/gitvault-publication.d.ts.map +1 -1
- package/sdk/dist/node/gitvault-publication.js +44 -17
- package/sdk/dist/node/gitvault-publication.js.map +1 -1
- package/sdk/dist/node/gitvault-recover.d.ts +15 -1
- package/sdk/dist/node/gitvault-recover.d.ts.map +1 -1
- package/sdk/dist/node/gitvault-recover.js +8 -4
- package/sdk/dist/node/gitvault-recover.js.map +1 -1
- package/sdk/dist/scoped.d.ts +2 -2
- package/sdk/dist/scoped.js +1 -1
package/README.md
CHANGED
|
@@ -52,7 +52,9 @@ For app manifests with `verify.http[]`, `up` runs HTTP checks after deploy. Fres
|
|
|
52
52
|
|
|
53
53
|
For typed `run402.deploy.ts` configs, pass `--manifest` explicitly because TypeScript/JavaScript configs execute local code. Use `--check` for local-only import/normalize/file validation, `--print-spec` to inspect the normalized `ReleaseSpec`, `--plan` for a gateway-reviewed non-deploying plan, and `--require-plan <plan_id>` to apply only that reviewed intent. Warning flags are not used with `--require-plan`; the reviewed plan binds the exact warning/destructive set. Run402 Core skips Cloud allowance/tier prerequisites and fails closed when no Core project is selected.
|
|
54
54
|
|
|
55
|
-
###
|
|
55
|
+
### repos — host-blind encrypted git repos (zero deploy ceremony)
|
|
56
|
+
|
|
57
|
+
One noun, twelve verbs (`repo` singular resolves identically). `run402 gitvault <verb>` answers `COMMAND_MOVED`/`COMMAND_REMOVED`.
|
|
56
58
|
|
|
57
59
|
```bash
|
|
58
60
|
run402 init # once per machine
|
|
@@ -61,13 +63,14 @@ git push -u origin main # allocates the vault on first pus
|
|
|
61
63
|
|
|
62
64
|
# or, one call: provision + allocate + scaffold, nothing deployed
|
|
63
65
|
run402 repos create my-notes
|
|
66
|
+
run402 repos view --human
|
|
64
67
|
run402 repos list --org org_1a2b3c
|
|
65
|
-
run402 repos delete prj_xyz --force
|
|
68
|
+
run402 repos delete --project prj_xyz --force # refuses without --force while the vault holds generations
|
|
66
69
|
```
|
|
67
70
|
|
|
68
|
-
A hosted git remote, encrypted before it leaves the machine — no deploy, no manifest, no app. `origin` is claimed additively — an existing `origin` is never touched, the run402 remote falls back to `run402` instead. `repos
|
|
71
|
+
A hosted git remote, encrypted before it leaves the machine — no deploy, no manifest, no app. `origin` is claimed additively — an existing `origin` is never touched, the run402 remote falls back to `run402` instead. Every mutating `repos` verb (`create`, `rename`, `delete`, `snapshot`, `policy`, `mirror`, `gc`) is CLI/OpenClaw-only by design — no MCP tool exists or will exist for them (one-shot recovery receipts, immutable generations, irreversible delete). Three READ-ONLY tools do exist — `repos_view`, `repos_list_heads`, `repos_fsck` — teaching only `repos` spellings. See `run402 repos --help` for the full tiered surface (common: `create`/`view`/`list`; occasional: `snapshot`/`mirror`/`recover`; lifecycle: `rename`/`delete`; maintenance: `fsck`/`gc`/`access`/`policy`), and the CLI reference's `repos` section for the terminal-loss statement and the progressive backup warning.
|
|
69
72
|
|
|
70
|
-
**Named addressing
|
|
73
|
+
**Named addressing.** `run402 org slug <slug>` (owner-only, small one-time fee) claims an org's globally-unique, address-form slug, after which `run402::<slug>/<name>` addresses any repo under it — `git push` to a name that doesn't exist yet push-to-creates it. `run402 repos rename <name> [--project <id>]` claims the per-org-unique `<name>` half explicitly (no fee); `repos create` claims one automatically, best-effort, when the org already has a slug. Also CLI/SDK-only — no MCP tool.
|
|
71
74
|
|
|
72
75
|
### Allowance
|
|
73
76
|
|
package/cli.mjs
CHANGED
|
@@ -32,7 +32,7 @@ SET UP — get an agent funded and entitled
|
|
|
32
32
|
BUILD & SHIP — the app and everything it serves
|
|
33
33
|
up Provision/link/deploy the current app with SDK orchestration
|
|
34
34
|
projects Manage projects (provision, list, get, sql, delete)
|
|
35
|
-
repos
|
|
35
|
+
repos Host-blind encrypted git repos: create/view/list, snapshot/mirror/recover, fsck/gc/access/policy (repo aliases repos)
|
|
36
36
|
deploy Unified deploy operations (requires active tier)
|
|
37
37
|
apply Alias for deploy apply; supports --rehearse for migration rehearsal
|
|
38
38
|
functions Manage serverless functions (deploy, invoke, logs, list, delete)
|
|
@@ -84,7 +84,7 @@ PLATFORM — everything else, and the things still finding a home
|
|
|
84
84
|
transfer Two-party project transfer (init, preview, list, accept, cancel)
|
|
85
85
|
cloud Cloud portability archive export (archives create/download/status)
|
|
86
86
|
archives Inspect and verify portable project archives locally
|
|
87
|
-
gitvault
|
|
87
|
+
gitvault RETIRED — moved to repos (repo-surface-consolidation); every spelling answers COMMAND_MOVED/COMMAND_REMOVED
|
|
88
88
|
buzz Buzz human/community/agent control-plane workflows
|
|
89
89
|
apps Browse and manage the app marketplace
|
|
90
90
|
ai AI translation and moderation tools
|
|
@@ -227,6 +227,14 @@ switch (cmd) {
|
|
|
227
227
|
await run(sub, rest);
|
|
228
228
|
break;
|
|
229
229
|
}
|
|
230
|
+
// `repo` singular resolves identically to `repos` (design D1 — agents type
|
|
231
|
+
// what `gh repo` already taught them). Its own case block, not a
|
|
232
|
+
// fall-through, so cli-conventions-gate's per-line family scanner sees it.
|
|
233
|
+
case "repo": {
|
|
234
|
+
const { run } = await import("./lib/repos.mjs");
|
|
235
|
+
await run(sub, rest);
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
230
238
|
case "apply": {
|
|
231
239
|
const { runDeployV2 } = await import("./lib/deploy-v2.mjs");
|
|
232
240
|
await runDeployV2("apply", [sub, ...rest].filter(Boolean));
|
package/lib/argparse.mjs
CHANGED
|
@@ -272,7 +272,7 @@ export function requirePositionalCount(args = [], flagsWithValues = [], opts = {
|
|
|
272
272
|
return pos;
|
|
273
273
|
}
|
|
274
274
|
|
|
275
|
-
// Resolve a positional project_id argument with active-project fallback
|
|
275
|
+
// Resolve a positional project_id argument with active-project fallback.
|
|
276
276
|
// If the first positional starts with "prj_", treat it as the project id and
|
|
277
277
|
// strip it from the rest. Otherwise, fall through to the active project from
|
|
278
278
|
// the keystore. Callers can tighten the legacy shorthand when a bare non-prj
|
package/lib/assets.mjs
CHANGED
|
@@ -265,7 +265,7 @@ function parseArgs(rawArgs) {
|
|
|
265
265
|
else if (a === "--limit") out.limit = parseIntegerFlag("--limit", args[++i], { min: 1, max: 1000 });
|
|
266
266
|
else if (a === "--output" || a === "-o") out.output = args[++i];
|
|
267
267
|
else if (a === "--ttl") out.ttl = parseIntegerFlag("--ttl", args[++i], { min: 60, max: 604800 });
|
|
268
|
-
//
|
|
268
|
+
// Repeatable --meta key=value (number-coerce on pure digits,
|
|
269
269
|
// comma-split into string[] when value contains ',', "true"/"false"
|
|
270
270
|
// → boolean, else string). Multiple --meta flags accumulate into a
|
|
271
271
|
// single flat metadata object.
|
|
@@ -274,7 +274,7 @@ function parseArgs(rawArgs) {
|
|
|
274
274
|
applyMetaFlag(out.metadata, args[++i]);
|
|
275
275
|
}
|
|
276
276
|
else if (a === "--exif-policy") out.exifPolicy = parseExifPolicyFlag(args[++i]);
|
|
277
|
-
//
|
|
277
|
+
// ls --sort and repeatable --filter k=v.
|
|
278
278
|
else if (a === "--sort") out.sort = parseSortFlag(args[++i]);
|
|
279
279
|
else if (a === "--filter") {
|
|
280
280
|
if (out.filter === null) out.filter = {};
|
|
@@ -285,7 +285,7 @@ function parseArgs(rawArgs) {
|
|
|
285
285
|
return out;
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
-
/**
|
|
288
|
+
/** Parses a single `--meta key=value` token. Coerces value:
|
|
289
289
|
* - numeric digits (with optional leading `-` / decimal) → number
|
|
290
290
|
* - "true" / "false" → boolean
|
|
291
291
|
* - commas → string[] (each segment stays a string; agents wanting
|
|
@@ -436,23 +436,19 @@ async function putOne(projectId, filePath, opts) {
|
|
|
436
436
|
}
|
|
437
437
|
const destKey = computeDestKey(filePath, opts.key);
|
|
438
438
|
|
|
439
|
-
//
|
|
440
|
-
// now delegates to `sdk.assets.put`, which routes through the
|
|
439
|
+
// The CLI delegates to `sdk.assets.put`, which routes through the
|
|
441
440
|
// unified-apply hero (apply/v1/plans -> content/v1/plans -> S3 PUT ->
|
|
442
|
-
// commit).
|
|
443
|
-
//
|
|
444
|
-
//
|
|
445
|
-
//
|
|
446
|
-
// at the apply-plan level (24h plan TTL); a future CLI redesign can
|
|
447
|
-
// expose that. The --concurrency and --no-resume flags are accepted but
|
|
448
|
-
// ignored — the SDK upload paths handle parallelism internally.
|
|
441
|
+
// commit). Resume semantics live at the apply-plan level (24h plan TTL);
|
|
442
|
+
// a future CLI redesign can expose that. The --concurrency and --no-resume
|
|
443
|
+
// flags are accepted but ignored — the SDK upload paths handle parallelism
|
|
444
|
+
// internally.
|
|
449
445
|
log(opts, { event: "start", key: destKey, size_bytes: stat.size });
|
|
450
446
|
const bytes = new Uint8Array(readFileSync(filePath));
|
|
451
447
|
const result = await getSdk().assets.put(projectId, destKey, { bytes }, {
|
|
452
448
|
contentType: opts.contentType ?? guessContentType(destKey),
|
|
453
449
|
visibility: opts.private ? "private" : "public",
|
|
454
450
|
immutable: opts.immutable,
|
|
455
|
-
//
|
|
451
|
+
// Threads caller-supplied metadata + EXIF policy through. The
|
|
456
452
|
// SDK validates each value with the same error code (INVALID_ASSET_
|
|
457
453
|
// METADATA / INVALID_EXIF_POLICY) the gateway returns, so a bad
|
|
458
454
|
// --meta / --exif-policy fails fast without an HTTP roundtrip.
|
|
@@ -532,7 +528,7 @@ async function ls(argv) {
|
|
|
532
528
|
const data = await getSdk().assets.ls(resolvedId, {
|
|
533
529
|
prefix: opts.prefix ?? undefined,
|
|
534
530
|
limit: opts.limit ?? undefined,
|
|
535
|
-
//
|
|
531
|
+
// Forward sort + filter to the SDK. Cursor stays absent for
|
|
536
532
|
// first-page calls; agents pass `--cursor` for follow-ups but the
|
|
537
533
|
// cursor is sort-pinned by the gateway so it doesn't accept a
|
|
538
534
|
// separate `--cursor` flag here yet.
|
package/lib/command-manifest.mjs
CHANGED
|
@@ -197,7 +197,7 @@ export const COMMAND_MANIFEST = [
|
|
|
197
197
|
{ path: ["org", "get"], positionals: [p("org_id")], projectScoped: false, legacyPositionalProject: false, minimalArgs: ["org_gate1"] },
|
|
198
198
|
{ path: ["org", "rename"], positionals: [p("org_id")], projectScoped: false, legacyPositionalProject: false, minimalArgs: ["org_gate1", "--name", "Gate"] },
|
|
199
199
|
{ path: ["org", "payout-wallet"], positionals: [p("org_id")], projectScoped: false, legacyPositionalProject: false, minimalArgs: ["org_gate1", "--wallet", "0x1111111111111111111111111111111111111111"] },
|
|
200
|
-
//
|
|
200
|
+
// `--org` (not a positional org_id
|
|
201
201
|
// like rename/payout-wallet above) goes through resolveOrg's SHAPE
|
|
202
202
|
// validation (a real UUID) — "org_gate1" fails that locally, so this needs
|
|
203
203
|
// the same UUID-shaped fixture `org use` below already established.
|
|
@@ -255,52 +255,36 @@ export const COMMAND_MANIFEST = [
|
|
|
255
255
|
{ path: ["claims", "list"], positionals: [], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub" },
|
|
256
256
|
{ path: ["claims", "release"], positionals: [p("claim_id")], projectScoped: true, legacyPositionalProject: false, minimalArgs: ["clm_1"], runStyle: "sub" },
|
|
257
257
|
|
|
258
|
-
// ── gitvault
|
|
259
|
-
//
|
|
260
|
-
//
|
|
261
|
-
//
|
|
258
|
+
// ── gitvault — no manifest entries ──────────────────────
|
|
259
|
+
// `cli/lib/gitvault.mjs`'s dispatcher handles every `gitvault <verb>` and
|
|
260
|
+
// answers a structural COMMAND_MOVED (or, for `push`/`reconcile`,
|
|
261
|
+
// COMMAND_REMOVED) redirect that dispatches nothing — see
|
|
262
|
+
// RESERVED_SUBCOMMANDS below, and "gitvault" in SKIPPED_FAMILIES.
|
|
263
|
+
|
|
264
|
+
// ── repos (the consolidated 12-verb family) ─
|
|
265
|
+
// Every verb needs a real principal keystore and, for most, an allocated
|
|
266
|
+
// repo and a local git working tree, so the gate runs structural checks
|
|
267
|
+
// only — an in-process behavioral run would either no-op against the
|
|
262
268
|
// universal `{}` fetch mock or touch the gate's own checkout.
|
|
263
|
-
{ path: ["
|
|
264
|
-
{ path: ["
|
|
265
|
-
{ path: ["
|
|
266
|
-
//
|
|
267
|
-
//
|
|
268
|
-
|
|
269
|
-
{ path: ["
|
|
270
|
-
{ path: ["
|
|
271
|
-
{ path: ["
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
{ path: ["
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
//
|
|
278
|
-
//
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
// runs structural checks only.
|
|
283
|
-
{ path: ["gitvault", "mirror", "set"], positionals: [p("destination")], projectScoped: true, legacyPositionalProject: false, minimalArgs: ["s3://example-mirror-bucket"], runStyle: "sub", skipBehavioral: "writes mirror destination config beside the keystore (client-side only)" },
|
|
284
|
-
{ path: ["gitvault", "mirror", "remove"], positionals: [], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub", skipBehavioral: "removes mirror destination config beside the keystore (client-side only)" },
|
|
285
|
-
{ path: ["gitvault", "mirror", "status"], positionals: [], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub", skipBehavioral: "reads the configured mirror + the live vault record's newest_generation" },
|
|
286
|
-
{ path: ["gitvault", "mirror", "sync"], positionals: [], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub", skipBehavioral: "lists the live vault's stored objects and reconciles them against the configured mirror" },
|
|
287
|
-
{ path: ["gitvault", "mirror", "verify"], positionals: [], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub", skipBehavioral: "keyless discovery + chain verification against the configured mirror; touches no key material" },
|
|
288
|
-
|
|
289
|
-
// ── gitvault recover (gitvault-mirror-and-recover, design D4) ────────────
|
|
290
|
-
// `r402s-recover`: offline, no server call at all — reads only from the
|
|
291
|
-
// mirror source named on the command line. Not project-scoped (the source
|
|
292
|
-
// URL, not the active project, addresses the vault to recover).
|
|
293
|
-
{ path: ["gitvault", "recover"], positionals: [p("source")], projectScoped: false, legacyPositionalProject: false, minimalArgs: ["s3://example-mirror-bucket", "--out", "__SCRATCH_DIR__/recover-out"], runStyle: "sub", skipBehavioral: "materializes a git repository from a mirror source, offline, with no server call" },
|
|
294
|
-
|
|
295
|
-
// ── repos (vault-only porcelain, repo-first-onramp D8, task 2.6) ────────
|
|
296
|
-
// `create` writes real git state into cwd and allocates a vault; `list`
|
|
297
|
-
// and `delete` read/mutate the live gitvault record — same structural-
|
|
298
|
-
// checks-only rationale as the gitvault family just above.
|
|
299
|
-
{ path: ["repos", "create"], positionals: [p("name")], projectScoped: false, legacyPositionalProject: false, minimalArgs: ["my-notes"], runStyle: "sub", skipBehavioral: "provisions a project, allocates a vault, and scaffolds a real git remote into cwd" },
|
|
300
|
-
{ path: ["repos", "list"], positionals: [], projectScoped: false, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub", skipBehavioral: "cross-references live projects against their live gitvault status, one call per project" },
|
|
301
|
-
{ path: ["repos", "delete"], positionals: [], projectScoped: true, legacyPositionalProject: true, minimalArgs: [], runStyle: "sub", skipBehavioral: "irreversibly deletes a project after reading its live vault generation count" },
|
|
302
|
-
// repo-first-onramp task 4.2 (design D6): explicit address-form name claim.
|
|
303
|
-
{ path: ["repos", "name"], positionals: [p("name")], projectScoped: true, legacyPositionalProject: false, minimalArgs: ["my-notes"], runStyle: "sub", skipBehavioral: "claims a per-org-unique repo name against a live project" },
|
|
269
|
+
{ path: ["repos", "create"], positionals: [p("name", { required: false })], projectScoped: false, legacyPositionalProject: false, minimalArgs: ["my-notes"], runStyle: "sub", skipBehavioral: "provisions a project, allocates a repo, and scaffolds a real git remote into cwd" },
|
|
270
|
+
{ path: ["repos", "list"], positionals: [], projectScoped: false, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub", skipBehavioral: "reads the live bulk vaults-by-org route, or falls back to a live per-project walk" },
|
|
271
|
+
{ path: ["repos", "view"], positionals: [], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub", skipBehavioral: "reads the local principal keystore and the live repo record" },
|
|
272
|
+
// (`gh repo rename`): claims/renames the per-org-unique address-form
|
|
273
|
+
// name, with `--repo`/`--project` addressing.
|
|
274
|
+
{ path: ["repos", "rename"], positionals: [p("new_name")], projectScoped: true, legacyPositionalProject: false, minimalArgs: ["my-notes"], runStyle: "sub", skipBehavioral: "claims a per-org-unique repo name against a live project" },
|
|
275
|
+
{ path: ["repos", "delete"], positionals: [], projectScoped: true, legacyPositionalProject: true, minimalArgs: [], runStyle: "sub", skipBehavioral: "irreversibly deletes a project after reading its live non-repo-resource state and vault generation count" },
|
|
276
|
+
{ path: ["repos", "snapshot"], positionals: [], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub", skipBehavioral: "captures the cwd git working tree and publishes a signed head" },
|
|
277
|
+
{ path: ["repos", "policy"], positionals: [p("repos_policy")], projectScoped: true, legacyPositionalProject: false, minimalArgs: ["required"], runStyle: "sub", skipBehavioral: "owner + step-up mutation of the live project's activation policy" },
|
|
278
|
+
// ONE flag-driven verb; `<destination>` is a real attribute (not a
|
|
279
|
+
// sub-verb literal), so this stays a single manifest entry.
|
|
280
|
+
{ path: ["repos", "mirror"], positionals: [p("destination", { required: false })], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub", skipBehavioral: "reads/writes mirror destination config beside the keystore and may move real bytes into a customer-owned bucket" },
|
|
281
|
+
{ path: ["repos", "fsck"], positionals: [], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub", skipBehavioral: "walks the live head chain and materializes the ref map against the keystore's local pins" },
|
|
282
|
+
{ path: ["repos", "gc"], positionals: [], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub", skipBehavioral: "publishes a checkpoint under a maintenance lease and materializes the live vault head to enumerate retention roots" },
|
|
283
|
+
// `access repair` is a nested sub-verb literal — two manifest entries,
|
|
284
|
+
// one dispatched `run("access", ...)` case in repos.mjs.
|
|
285
|
+
{ path: ["repos", "access"], positionals: [], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub", skipBehavioral: "reads the live org encryption-key directory + vault envelope recipients" },
|
|
286
|
+
{ path: ["repos", "access", "repair"], positionals: [], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub", skipBehavioral: "always refuses — gated on gitvault-human-envelopes' epoch-rotation work, not shipped yet" },
|
|
287
|
+
{ path: ["repos", "recover"], positionals: [p("source")], projectScoped: false, legacyPositionalProject: false, minimalArgs: ["s3://example-mirror-bucket", "--out", "__SCRATCH_DIR__/recover-out"], runStyle: "sub", skipBehavioral: "materializes a git repository from a mirror source, offline, with no server call" },
|
|
304
288
|
{ path: ["errors"], positionals: [p("fingerprint_id", { required: false })], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "merged" },
|
|
305
289
|
|
|
306
290
|
// ── jobs ─────────────────────────────────────────────────────────────────
|
|
@@ -453,7 +437,7 @@ export const COMMAND_MANIFEST = [
|
|
|
453
437
|
{ path: ["service", "health"], positionals: [], projectScoped: false, legacyPositionalProject: false, minimalArgs: [] },
|
|
454
438
|
{ path: ["cache", "inspect"], positionals: [p("url")], projectScoped: false, legacyPositionalProject: false, minimalArgs: ["https://example.com/"] },
|
|
455
439
|
{ path: ["cache", "invalidate"], positionals: [p("url", { required: false })], projectScoped: false, legacyPositionalProject: false, minimalArgs: ["--all", "--host", "example.com"] },
|
|
456
|
-
// projectScoped
|
|
440
|
+
// projectScoped: --project targets the gitvault
|
|
457
441
|
// check only (see doctor.mjs's own HELP) — every other check stays
|
|
458
442
|
// wallet/machine-wide, but the gate's contract is "accepts --project
|
|
459
443
|
// without rejecting it," which this satisfies.
|
|
@@ -475,6 +459,15 @@ export const SKIPPED_FAMILIES = {
|
|
|
475
459
|
// Split into `deliveries` / `contacts` / `subscriptions`; every subcommand
|
|
476
460
|
// answers COMMAND_REMOVED naming its successor.
|
|
477
461
|
"notifications": "reserved group; split by legible-cli-surface",
|
|
462
|
+
// The gitvault dispatcher answers COMMAND_MOVED/COMMAND_REMOVED for every
|
|
463
|
+
// `gitvault <verb>` (see RESERVED_SUBCOMMANDS below for the per-verb list)
|
|
464
|
+
// — the family stays dispatched in cli.mjs (so the redirect fires instead
|
|
465
|
+
// of UNKNOWN_COMMAND) but has zero manifest entries, since a redirect
|
|
466
|
+
// dispatches nothing.
|
|
467
|
+
"gitvault": "retired; every subcommand answers COMMAND_MOVED/COMMAND_REMOVED naming its `repos`/git successor",
|
|
468
|
+
// `repo` singular resolves identically to `repos` — same
|
|
469
|
+
// module, same case block in cli.mjs, so it needs no manifest of its own.
|
|
470
|
+
"repo": "alias for `repos`, resolves identically (design D1)",
|
|
478
471
|
};
|
|
479
472
|
|
|
480
473
|
/**
|
|
@@ -486,8 +479,8 @@ export const SKIPPED_FAMILIES = {
|
|
|
486
479
|
* belongs in no capability mapping, so the inventory gate must not demand one.
|
|
487
480
|
*
|
|
488
481
|
* The family-level equivalent is SKIPPED_FAMILIES above. This exists because
|
|
489
|
-
*
|
|
490
|
-
*
|
|
482
|
+
* some spellings are retired INSIDE a family rather than as a whole family,
|
|
483
|
+
* which the family list cannot express.
|
|
491
484
|
*/
|
|
492
485
|
export const RESERVED_SUBCOMMANDS = {
|
|
493
486
|
"rooms:who": "renamed to `rooms join` — an interrogative must not name a write",
|
|
@@ -500,4 +493,20 @@ export const RESERVED_SUBCOMMANDS = {
|
|
|
500
493
|
"rooms:get": "moved to `messages get` — the verb acts on a message",
|
|
501
494
|
"rooms:ack": "moved to `messages ack` — the verb acts on a message",
|
|
502
495
|
"escalations:contacts": "merged into `contacts` — the ladder and Telegram channels are one question",
|
|
496
|
+
// Every `gitvault <verb>` spelling. Nine
|
|
497
|
+
// answer COMMAND_MOVED naming their `repos` successor; `push` and
|
|
498
|
+
// `reconcile` answer COMMAND_REMOVED (no equivalent successor for either —
|
|
499
|
+
// `push`'s one-release alias window is over, `reconcile` was a workaround
|
|
500
|
+
// with no permanent replacement, only a read at `repos access`).
|
|
501
|
+
"gitvault:init": "moved to `repos create --project <id>`",
|
|
502
|
+
"gitvault:status": "moved to `repos view`",
|
|
503
|
+
"gitvault:snapshot": "moved to `repos snapshot`",
|
|
504
|
+
"gitvault:policy": "moved to `repos policy`",
|
|
505
|
+
"gitvault:compact": "moved to `repos gc`",
|
|
506
|
+
"gitvault:prune": "moved to `repos gc`",
|
|
507
|
+
"gitvault:verify": "moved to `repos fsck`",
|
|
508
|
+
"gitvault:mirror": "moved to `repos mirror`",
|
|
509
|
+
"gitvault:recover": "moved to `repos recover`",
|
|
510
|
+
"gitvault:push": "removed — its one-release deprecation-alias window is over; `git push` / `repos snapshot`",
|
|
511
|
+
"gitvault:reconcile": "removed — a workaround with no permanent successor; read `repos access` instead",
|
|
503
512
|
};
|
package/lib/deploy-v2.mjs
CHANGED
|
@@ -1400,16 +1400,13 @@ const CI_DEPLOY_ERROR_GUIDANCE = {
|
|
|
1400
1400
|
};
|
|
1401
1401
|
|
|
1402
1402
|
/**
|
|
1403
|
-
* The
|
|
1404
|
-
*
|
|
1405
|
-
*
|
|
1406
|
-
*
|
|
1407
|
-
*
|
|
1408
|
-
*
|
|
1409
|
-
*
|
|
1410
|
-
* `grandfather_policy` — is now TRUE as written, so it is relayed untouched;
|
|
1411
|
-
* re-authoring it here would be the client lying in the opposite direction.
|
|
1412
|
-
* `cli-deploy-gitvault-lane.test.mjs` is the successor gate.
|
|
1403
|
+
* The gateway's envelope for gitvault deploy errors — `upgrade_client` first,
|
|
1404
|
+
* then `grandfather_policy` — is relayed untouched: `applyCmd` deploys
|
|
1405
|
+
* through `applyWithGitvault`, which declares `{capture_id,
|
|
1406
|
+
* snapshot_oid_hmac}` at plan time and presents an activation token at
|
|
1407
|
+
* commit, satisfying `gitvault_policy: required`, so no client-side rewrite
|
|
1408
|
+
* of the envelope is needed. `cli-deploy-gitvault-lane.test.mjs` is the gate
|
|
1409
|
+
* for this behavior.
|
|
1413
1410
|
*/
|
|
1414
1411
|
function reportDeployApplyError(err, useGithubActionsOidc) {
|
|
1415
1412
|
const warningEnhanced = enhanceDeployWarningError(err);
|
package/lib/doctor.mjs
CHANGED
|
@@ -568,6 +568,11 @@ export async function run(sub, args = []) {
|
|
|
568
568
|
pending_overrides: gv.pending_overrides,
|
|
569
569
|
pins: gv.pins,
|
|
570
570
|
remote: gv.remote,
|
|
571
|
+
// dogfood item 2: `null` (unknown) or <= 1 means the single-principal
|
|
572
|
+
// V0-A terminal-loss statement below is honest; >= 2 means the SDK
|
|
573
|
+
// already proved a second covering recipient, so the hint switches
|
|
574
|
+
// to the durability sentence instead of the terminal-loss claim.
|
|
575
|
+
covering_recipients: gv.covering_recipients ?? null,
|
|
571
576
|
};
|
|
572
577
|
const gaps = [];
|
|
573
578
|
// The one that actually breaks the next deploy: the project demands a
|
|
@@ -575,13 +580,13 @@ export async function run(sub, args = []) {
|
|
|
575
580
|
if (gv.gitvault_policy === "required" && !gv.keystore.holds_repo_key) {
|
|
576
581
|
gaps.push(
|
|
577
582
|
"gitvault_policy is 'required' but this machine holds no key for the vault — a deploy from here is refused with GITVAULT_CLIENT_UPGRADE_REQUIRED. " +
|
|
578
|
-
"Run 'run402
|
|
583
|
+
"Run 'run402 repos create --project <id>' (idempotent; resolves to the existing repo), or 'run402 repos policy grandfathered --reason <why>' to un-gate the project.",
|
|
579
584
|
);
|
|
580
585
|
} else if (gv.gitvault_policy === "required" && !gv.keystore.can_sign) {
|
|
581
586
|
gaps.push("gitvault_policy is 'required' and this keystore is read-only (no signing key) — it can verify but cannot publish the capture a deploy needs");
|
|
582
587
|
}
|
|
583
588
|
if (gv.pending_overrides > 0) {
|
|
584
|
-
gaps.push(`${gv.pending_overrides} unvaulted-override journal(s) are still open — run 'run402
|
|
589
|
+
gaps.push(`${gv.pending_overrides} unvaulted-override journal(s) are still open — run 'run402 repos snapshot' to drain them`);
|
|
585
590
|
}
|
|
586
591
|
// `matches` is a TRI-STATE (kychee-com/run402#562): `false` alone is
|
|
587
592
|
// a real mismatch. `null` (a slug-form remote not yet resolved on
|
|
@@ -616,7 +621,7 @@ export async function run(sub, args = []) {
|
|
|
616
621
|
keystore_still_required: mirrorStatus.keystore_still_required,
|
|
617
622
|
};
|
|
618
623
|
if (!mirrorStatus.configured) {
|
|
619
|
-
value.gitvault_mirror.advisory = "no ciphertext mirror is configured for this vault — the exit ramp is opt-in; 'run402
|
|
624
|
+
value.gitvault_mirror.advisory = "no ciphertext mirror is configured for this vault — the exit ramp is opt-in; 'run402 repos mirror <destination>' to configure one.";
|
|
620
625
|
} else if (mirrorStatus.is_current === false) {
|
|
621
626
|
gaps.push(`the ciphertext mirror at ${mirrorStatus.destination} is STALE (mirrored generation ${mirrorStatus.mirrored_generation ?? "(none)"}, vault newest ${mirrorStatus.newest_generation ?? "(none)"}) — ${mirrorStatus.closing_command}`);
|
|
622
627
|
}
|
|
@@ -631,8 +636,10 @@ export async function run(sub, args = []) {
|
|
|
631
636
|
status: gaps.length > 0 ? "warning" : "ok",
|
|
632
637
|
value: gaps.length > 0 ? { ...value, gaps } : value,
|
|
633
638
|
hint: gv.vault === null
|
|
634
|
-
? `No vault for this project (that is a normal shape). Allocate one with 'run402
|
|
635
|
-
:
|
|
639
|
+
? `No vault for this project (that is a normal shape). Allocate one with 'run402 repos create --project <id>'. Keystore: ${gv.keystore.root}`
|
|
640
|
+
: gv.durability_statement
|
|
641
|
+
? `Back up ${gv.keystore.root} anyway — ${gv.durability_statement} (covering_recipients: ${gv.covering_recipients})`
|
|
642
|
+
: `Back up ${gv.keystore.root} — whole-machine or whole-keystore loss is terminal for vault history.`,
|
|
636
643
|
});
|
|
637
644
|
} catch (err) {
|
|
638
645
|
// A gateway without gitvault, an unreachable API, or a project this
|
|
@@ -49,7 +49,7 @@ export async function scaffoldGitvaultRemote({ repoDir = process.cwd(), projectI
|
|
|
49
49
|
const remote = await getSdk().gitvault.scaffoldRemote({ repo_dir: repoDir, org_id: resolvedOrgId, project_id: projectId });
|
|
50
50
|
// `allocated: false` is stated, not left to be inferred: this is local
|
|
51
51
|
// git only — no vault exists for the project yet (allocation happens
|
|
52
|
-
// lazily on first push, or explicitly via `run402
|
|
52
|
+
// lazily on first push, or explicitly via `run402 repos create --project <id>`).
|
|
53
53
|
out.gitvault = { ...remote, allocated: false };
|
|
54
54
|
} catch (err) {
|
|
55
55
|
out.gitvault = null;
|
package/lib/gitvault-target.mjs
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared "which vault does this repo mean" resolution for `run402 gitvault`
|
|
3
|
-
* and `run402 doctor`'s gitvault check
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* repo, and a stale active-project pointer silently targeted a DIFFERENT
|
|
9
|
-
* one instead (GITVAULT_ACCESS_DENIED, or worse, a silently WRONG vault).
|
|
3
|
+
* and `run402 doctor`'s gitvault check. When a verb runs standing inside a
|
|
4
|
+
* repository that already names its own vault via a pinned repo id or a
|
|
5
|
+
* run402/origin remote, targeting prefers that repo over the active-project
|
|
6
|
+
* pointer — git muscle memory says a command run inside a repo acts on THAT
|
|
7
|
+
* repo.
|
|
10
8
|
*
|
|
11
9
|
* Targeting order for a verb run standing inside a git repository, highest
|
|
12
10
|
* first:
|
|
13
11
|
* 1. an explicit --repo/--project flag (owned by each call site — this
|
|
14
12
|
* module supplies only the fallback chain beneath it, plus the
|
|
15
13
|
* mismatch warning against tier 3)
|
|
16
|
-
* 2. the
|
|
14
|
+
* 2. the pinned `r402.repoId` in local git config — addresses the
|
|
17
15
|
* vault by repo_id directly, no network read at all
|
|
18
16
|
* 3. the repo's run402/origin remote address — id-form is parsed
|
|
19
17
|
* directly out of the address string (free, no network); slug-form is
|
|
@@ -24,7 +22,7 @@
|
|
|
24
22
|
* 5. the profile's active project
|
|
25
23
|
*
|
|
26
24
|
* Outside a repository (or when repo detection itself fails), only tiers 4
|
|
27
|
-
* and 5 apply
|
|
25
|
+
* and 5 apply.
|
|
28
26
|
*
|
|
29
27
|
* ARCHITECTURAL NOTE: this is CLI-edge policy (which flag/env/file wins),
|
|
30
28
|
* not gitvault protocol behavior — the same class of concern
|