run402 4.95.0 → 4.97.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 +5 -5
- package/cli.mjs +2 -28
- package/core-dist/keystore.js +1 -1
- package/git-remote-run402.mjs +6 -6
- package/lib/buzz.mjs +1 -1
- package/lib/cold-start.mjs +2 -2
- package/lib/command-manifest.mjs +6 -47
- package/lib/credentials.mjs +5 -5
- package/lib/daemon-path.mjs +3 -3
- package/lib/deploy-v2.mjs +44 -44
- package/lib/doctor.mjs +45 -54
- package/lib/errors.mjs +1 -1
- package/lib/events.mjs +1 -1
- package/lib/grants.mjs +148 -46
- package/lib/init.mjs +29 -29
- package/lib/logs.mjs +1 -1
- package/lib/next-actions.mjs +1 -1
- package/lib/org-context.mjs +9 -9
- package/lib/orgs.mjs +19 -19
- package/lib/path-lookup.mjs +7 -10
- package/lib/path-lookup.test.mjs +19 -21
- package/lib/projects.mjs +1 -1
- package/lib/remote-helper-session.mjs +93 -93
- package/lib/repos.mjs +174 -175
- package/lib/rooms-context.mjs +7 -7
- package/lib/sites.mjs +5 -5
- package/lib/up.mjs +14 -14
- package/lib/{gitvault-capabilities.mjs → vault-capabilities.mjs} +10 -14
- package/lib/{gitvault-daemon-run.mjs → vault-daemon-run.mjs} +2 -2
- package/lib/{gitvault-daemon.mjs → vault-daemon.mjs} +23 -23
- package/lib/{gitvault-scaffold.mjs → vault-scaffold.mjs} +12 -12
- package/lib/{gitvault-target.mjs → vault-target.mjs} +22 -17
- package/package.json +2 -2
- package/sdk/core-dist/keystore.js +1 -1
- package/sdk/dist/errors.d.ts +5 -5
- package/sdk/dist/errors.d.ts.map +1 -1
- package/sdk/dist/errors.js +2 -2
- package/sdk/dist/errors.js.map +1 -1
- package/sdk/dist/grant-key-credentials.d.ts +38 -0
- package/sdk/dist/grant-key-credentials.d.ts.map +1 -0
- package/sdk/dist/grant-key-credentials.js +42 -0
- package/sdk/dist/grant-key-credentials.js.map +1 -0
- package/sdk/dist/index.d.ts +16 -23
- package/sdk/dist/index.d.ts.map +1 -1
- package/sdk/dist/index.js +14 -21
- package/sdk/dist/index.js.map +1 -1
- package/sdk/dist/namespaces/billing.d.ts +1 -1
- package/sdk/dist/namespaces/billing.js +1 -1
- package/sdk/dist/namespaces/buzz.js +1 -1
- package/sdk/dist/namespaces/buzz.js.map +1 -1
- package/sdk/dist/namespaces/credentials.d.ts +4 -4
- package/sdk/dist/namespaces/credentials.js +4 -4
- package/sdk/dist/namespaces/deploy.d.ts +5 -5
- package/sdk/dist/namespaces/deploy.d.ts.map +1 -1
- package/sdk/dist/namespaces/deploy.js +25 -25
- package/sdk/dist/namespaces/deploy.js.map +1 -1
- package/sdk/dist/namespaces/deploy.types.d.ts +17 -17
- package/sdk/dist/namespaces/deploy.types.d.ts.map +1 -1
- package/sdk/dist/namespaces/domains.d.ts +1 -1
- package/sdk/dist/namespaces/domains.js +1 -1
- package/sdk/dist/namespaces/errors.d.ts +1 -1
- package/sdk/dist/namespaces/errors.js +2 -2
- package/sdk/dist/namespaces/escalations.d.ts +2 -2
- package/sdk/dist/namespaces/escalations.js +2 -2
- package/sdk/dist/namespaces/escalations.types.d.ts +3 -3
- package/sdk/dist/namespaces/escalations.types.d.ts.map +1 -1
- package/sdk/dist/namespaces/events.d.ts +1 -1
- package/sdk/dist/namespaces/events.js +1 -1
- package/sdk/dist/namespaces/functions.js +1 -1
- package/sdk/dist/namespaces/functions.js.map +1 -1
- package/sdk/dist/namespaces/grants.d.ts +43 -9
- package/sdk/dist/namespaces/grants.d.ts.map +1 -1
- package/sdk/dist/namespaces/grants.js +99 -10
- package/sdk/dist/namespaces/grants.js.map +1 -1
- package/sdk/dist/namespaces/grants.types.d.ts +120 -15
- package/sdk/dist/namespaces/grants.types.d.ts.map +1 -1
- package/sdk/dist/namespaces/grants.types.js +12 -3
- package/sdk/dist/namespaces/grants.types.js.map +1 -1
- package/sdk/dist/namespaces/identity-links.types.d.ts +2 -2
- package/sdk/dist/namespaces/identity-links.types.d.ts.map +1 -1
- package/sdk/dist/namespaces/org.d.ts +3 -3
- package/sdk/dist/namespaces/org.js +3 -3
- package/sdk/dist/namespaces/org.types.d.ts +3 -3
- package/sdk/dist/namespaces/pay.js +1 -1
- package/sdk/dist/namespaces/projects.d.ts +1 -1
- package/sdk/dist/namespaces/projects.js +1 -1
- package/sdk/dist/namespaces/{gitvault.d.ts → repos.d.ts} +436 -449
- package/sdk/dist/namespaces/repos.d.ts.map +1 -0
- package/sdk/dist/namespaces/{gitvault.js → repos.js} +452 -460
- package/sdk/dist/namespaces/repos.js.map +1 -0
- package/sdk/dist/namespaces/rooms.d.ts +3 -3
- package/sdk/dist/namespaces/rooms.js +5 -5
- package/sdk/dist/namespaces/rooms.js.map +1 -1
- package/sdk/dist/namespaces/session.d.ts +2 -2
- package/sdk/dist/namespaces/session.d.ts.map +1 -1
- package/sdk/dist/namespaces/session.js +2 -2
- package/sdk/dist/namespaces/session.js.map +1 -1
- package/sdk/dist/namespaces/tier.d.ts +3 -3
- package/sdk/dist/namespaces/tier.d.ts.map +1 -1
- package/sdk/dist/namespaces/{gitvault.crypto.d.ts → vault.crypto.d.ts} +141 -141
- package/sdk/dist/namespaces/vault.crypto.d.ts.map +1 -0
- package/sdk/dist/namespaces/{gitvault.crypto.js → vault.crypto.js} +173 -173
- package/sdk/dist/namespaces/vault.crypto.js.map +1 -0
- package/sdk/dist/namespaces/{gitvault.types.d.ts → vault.types.d.ts} +136 -136
- package/sdk/dist/namespaces/vault.types.d.ts.map +1 -0
- package/sdk/dist/namespaces/{gitvault.types.js → vault.types.js} +4 -4
- package/sdk/dist/namespaces/vault.types.js.map +1 -0
- package/sdk/dist/node/bearer-redeem-key.d.ts +1 -1
- package/sdk/dist/node/bearer-redeem-key.js +5 -5
- package/sdk/dist/node/bearer-redeem-key.js.map +1 -1
- package/sdk/dist/node/credentials.d.ts +9 -9
- package/sdk/dist/node/credentials.d.ts.map +1 -1
- package/sdk/dist/node/credentials.js +12 -12
- package/sdk/dist/node/credentials.js.map +1 -1
- package/sdk/dist/node/http-dispatcher.js +5 -5
- package/sdk/dist/node/http-dispatcher.js.map +1 -1
- package/sdk/dist/node/index.d.ts +34 -34
- package/sdk/dist/node/index.d.ts.map +1 -1
- package/sdk/dist/node/index.js +41 -41
- package/sdk/dist/node/index.js.map +1 -1
- package/sdk/dist/node/{gitvault-address.d.ts → vault-address.d.ts} +42 -42
- package/sdk/dist/node/vault-address.d.ts.map +1 -0
- package/sdk/dist/node/{gitvault-address.js → vault-address.js} +43 -43
- package/sdk/dist/node/vault-address.js.map +1 -0
- package/sdk/dist/node/{gitvault-apply.d.ts → vault-apply.d.ts} +27 -27
- package/sdk/dist/node/vault-apply.d.ts.map +1 -0
- package/sdk/dist/node/{gitvault-apply.js → vault-apply.js} +38 -38
- package/sdk/dist/node/vault-apply.js.map +1 -0
- package/sdk/dist/node/vault-byo-config.d.ts +29 -0
- package/sdk/dist/node/vault-byo-config.d.ts.map +1 -0
- package/sdk/dist/node/{gitvault-byo-config.js → vault-byo-config.js} +18 -18
- package/sdk/dist/node/vault-byo-config.js.map +1 -0
- package/sdk/dist/node/vault-byo-probe.d.ts +20 -0
- package/sdk/dist/node/vault-byo-probe.d.ts.map +1 -0
- package/sdk/dist/node/vault-byo-probe.js +57 -0
- package/sdk/dist/node/vault-byo-probe.js.map +1 -0
- package/sdk/dist/node/{gitvault-creation-journal.d.ts → vault-creation-journal.d.ts} +69 -69
- package/sdk/dist/node/vault-creation-journal.d.ts.map +1 -0
- package/sdk/dist/node/{gitvault-creation-journal.js → vault-creation-journal.js} +54 -54
- package/sdk/dist/node/vault-creation-journal.js.map +1 -0
- package/sdk/dist/node/{gitvault-degraded-read.d.ts → vault-degraded-read.d.ts} +34 -34
- package/sdk/dist/node/vault-degraded-read.d.ts.map +1 -0
- package/sdk/dist/node/{gitvault-degraded-read.js → vault-degraded-read.js} +24 -24
- package/sdk/dist/node/vault-degraded-read.js.map +1 -0
- package/sdk/dist/node/{gitvault-deploy.d.ts → vault-deploy.d.ts} +67 -67
- package/sdk/dist/node/vault-deploy.d.ts.map +1 -0
- package/sdk/dist/node/{gitvault-deploy.js → vault-deploy.js} +27 -27
- package/sdk/dist/node/vault-deploy.js.map +1 -0
- package/sdk/dist/node/{gitvault-edge-fetch.d.ts → vault-edge-fetch.d.ts} +9 -9
- package/sdk/dist/node/vault-edge-fetch.d.ts.map +1 -0
- package/sdk/dist/node/{gitvault-edge-fetch.js → vault-edge-fetch.js} +11 -11
- package/sdk/dist/node/vault-edge-fetch.js.map +1 -0
- package/sdk/dist/node/{gitvault-handoff.d.ts → vault-handoff.d.ts} +9 -9
- package/sdk/dist/node/vault-handoff.d.ts.map +1 -0
- package/sdk/dist/node/{gitvault-handoff.js → vault-handoff.js} +24 -24
- package/sdk/dist/node/vault-handoff.js.map +1 -0
- package/sdk/dist/node/{gitvault-keystore.d.ts → vault-keystore.d.ts} +87 -77
- package/sdk/dist/node/vault-keystore.d.ts.map +1 -0
- package/sdk/dist/node/{gitvault-keystore.js → vault-keystore.js} +105 -68
- package/sdk/dist/node/vault-keystore.js.map +1 -0
- package/sdk/dist/node/{gitvault-member-bundle.d.ts → vault-member-bundle.d.ts} +17 -17
- package/sdk/dist/node/vault-member-bundle.d.ts.map +1 -0
- package/sdk/dist/node/{gitvault-member-bundle.js → vault-member-bundle.js} +11 -11
- package/sdk/dist/node/vault-member-bundle.js.map +1 -0
- package/sdk/dist/node/{gitvault-mirror-backend.d.ts → vault-mirror-backend.d.ts} +26 -26
- package/sdk/dist/node/vault-mirror-backend.d.ts.map +1 -0
- package/sdk/dist/node/{gitvault-mirror-backend.js → vault-mirror-backend.js} +25 -25
- package/sdk/dist/node/vault-mirror-backend.js.map +1 -0
- package/sdk/dist/node/{gitvault-mirror-config.d.ts → vault-mirror-config.d.ts} +23 -23
- package/sdk/dist/node/vault-mirror-config.d.ts.map +1 -0
- package/sdk/dist/node/{gitvault-mirror-config.js → vault-mirror-config.js} +16 -16
- package/sdk/dist/node/vault-mirror-config.js.map +1 -0
- package/sdk/dist/node/{gitvault-mirror.d.ts → vault-mirror.d.ts} +36 -36
- package/sdk/dist/node/vault-mirror.d.ts.map +1 -0
- package/sdk/dist/node/{gitvault-mirror.js → vault-mirror.js} +75 -75
- package/sdk/dist/node/vault-mirror.js.map +1 -0
- package/sdk/dist/node/{gitvault-native-crypto.d.ts → vault-native-crypto.d.ts} +10 -10
- package/sdk/dist/node/vault-native-crypto.d.ts.map +1 -0
- package/sdk/dist/node/{gitvault-native-crypto.js → vault-native-crypto.js} +16 -16
- package/sdk/dist/node/vault-native-crypto.js.map +1 -0
- package/sdk/dist/node/{gitvault-open-or-create.d.ts → vault-open-or-create.d.ts} +16 -16
- package/sdk/dist/node/vault-open-or-create.d.ts.map +1 -0
- package/sdk/dist/node/{gitvault-open-or-create.js → vault-open-or-create.js} +10 -10
- package/sdk/dist/node/vault-open-or-create.js.map +1 -0
- package/sdk/dist/node/{gitvault-prewarm.d.ts → vault-prewarm.d.ts} +13 -13
- package/sdk/dist/node/vault-prewarm.d.ts.map +1 -0
- package/sdk/dist/node/{gitvault-prewarm.js → vault-prewarm.js} +25 -25
- package/sdk/dist/node/vault-prewarm.js.map +1 -0
- package/sdk/dist/node/{gitvault-profile-scan.d.ts → vault-profile-scan.d.ts} +3 -3
- package/sdk/dist/node/vault-profile-scan.d.ts.map +1 -0
- package/sdk/dist/node/{gitvault-profile-scan.js → vault-profile-scan.js} +12 -12
- package/sdk/dist/node/vault-profile-scan.js.map +1 -0
- package/sdk/dist/node/{gitvault-prune.d.ts → vault-prune.d.ts} +61 -61
- package/sdk/dist/node/vault-prune.d.ts.map +1 -0
- package/sdk/dist/node/{gitvault-prune.js → vault-prune.js} +34 -34
- package/sdk/dist/node/vault-prune.js.map +1 -0
- package/sdk/dist/node/{gitvault-publication.d.ts → vault-publication.d.ts} +399 -399
- package/sdk/dist/node/vault-publication.d.ts.map +1 -0
- package/sdk/dist/node/{gitvault-publication.js → vault-publication.js} +584 -584
- package/sdk/dist/node/vault-publication.js.map +1 -0
- package/sdk/dist/node/{gitvault-push-to-create.d.ts → vault-push-to-create.d.ts} +16 -16
- package/sdk/dist/node/vault-push-to-create.d.ts.map +1 -0
- package/sdk/dist/node/{gitvault-push-to-create.js → vault-push-to-create.js} +9 -9
- package/sdk/dist/node/vault-push-to-create.js.map +1 -0
- package/sdk/dist/node/{gitvault-recover.d.ts → vault-recover.d.ts} +50 -50
- package/sdk/dist/node/vault-recover.d.ts.map +1 -0
- package/sdk/dist/node/{gitvault-recover.js → vault-recover.js} +84 -84
- package/sdk/dist/node/vault-recover.js.map +1 -0
- package/sdk/dist/node/{gitvault-restore.d.ts → vault-restore.d.ts} +8 -8
- package/sdk/dist/node/vault-restore.d.ts.map +1 -0
- package/sdk/dist/node/{gitvault-restore.js → vault-restore.js} +11 -11
- package/sdk/dist/node/vault-restore.js.map +1 -0
- package/sdk/dist/node/{gitvault-snapshot.d.ts → vault-snapshot.d.ts} +48 -48
- package/sdk/dist/node/vault-snapshot.d.ts.map +1 -0
- package/sdk/dist/node/{gitvault-snapshot.js → vault-snapshot.js} +61 -61
- package/sdk/dist/node/vault-snapshot.js.map +1 -0
- package/sdk/dist/node/{gitvault-writer-state.d.ts → vault-writer-state.d.ts} +8 -8
- package/sdk/dist/node/vault-writer-state.d.ts.map +1 -0
- package/sdk/dist/node/{gitvault-writer-state.js → vault-writer-state.js} +15 -15
- package/sdk/dist/node/vault-writer-state.js.map +1 -0
- package/sdk/dist/node/workflow-output.js +4 -4
- package/sdk/dist/node/workflow-output.js.map +1 -1
- package/sdk/dist/project-auth-classification.d.ts +8 -8
- package/sdk/dist/project-auth-classification.d.ts.map +1 -1
- package/sdk/dist/project-auth-classification.js +7 -7
- package/sdk/dist/project-auth-classification.js.map +1 -1
- package/sdk/dist/scoped.d.ts +6 -14
- package/sdk/dist/scoped.d.ts.map +1 -1
- package/sdk/dist/scoped.js +10 -21
- package/sdk/dist/scoped.js.map +1 -1
- package/vault-surface.json +40 -0
- package/gitvault-surface.json +0 -89
- package/lib/delegates.mjs +0 -189
- package/lib/gitvault.mjs +0 -163
- package/lib/source-access.mjs +0 -70
- package/sdk/dist/delegate-credentials.d.ts +0 -37
- package/sdk/dist/delegate-credentials.d.ts.map +0 -1
- package/sdk/dist/delegate-credentials.js +0 -41
- package/sdk/dist/delegate-credentials.js.map +0 -1
- package/sdk/dist/namespaces/delegates.d.ts +0 -50
- package/sdk/dist/namespaces/delegates.d.ts.map +0 -1
- package/sdk/dist/namespaces/delegates.js +0 -99
- package/sdk/dist/namespaces/delegates.js.map +0 -1
- package/sdk/dist/namespaces/delegates.types.d.ts +0 -88
- package/sdk/dist/namespaces/delegates.types.d.ts.map +0 -1
- package/sdk/dist/namespaces/delegates.types.js +0 -10
- package/sdk/dist/namespaces/delegates.types.js.map +0 -1
- package/sdk/dist/namespaces/gitvault.crypto.d.ts.map +0 -1
- package/sdk/dist/namespaces/gitvault.crypto.js.map +0 -1
- package/sdk/dist/namespaces/gitvault.d.ts.map +0 -1
- package/sdk/dist/namespaces/gitvault.js.map +0 -1
- package/sdk/dist/namespaces/gitvault.types.d.ts.map +0 -1
- package/sdk/dist/namespaces/gitvault.types.js.map +0 -1
- package/sdk/dist/node/gitvault-address.d.ts.map +0 -1
- package/sdk/dist/node/gitvault-address.js.map +0 -1
- package/sdk/dist/node/gitvault-apply.d.ts.map +0 -1
- package/sdk/dist/node/gitvault-apply.js.map +0 -1
- package/sdk/dist/node/gitvault-byo-config.d.ts +0 -29
- package/sdk/dist/node/gitvault-byo-config.d.ts.map +0 -1
- package/sdk/dist/node/gitvault-byo-config.js.map +0 -1
- package/sdk/dist/node/gitvault-byo-probe.d.ts +0 -20
- package/sdk/dist/node/gitvault-byo-probe.d.ts.map +0 -1
- package/sdk/dist/node/gitvault-byo-probe.js +0 -57
- package/sdk/dist/node/gitvault-byo-probe.js.map +0 -1
- package/sdk/dist/node/gitvault-creation-journal.d.ts.map +0 -1
- package/sdk/dist/node/gitvault-creation-journal.js.map +0 -1
- package/sdk/dist/node/gitvault-degraded-read.d.ts.map +0 -1
- package/sdk/dist/node/gitvault-degraded-read.js.map +0 -1
- package/sdk/dist/node/gitvault-deploy.d.ts.map +0 -1
- package/sdk/dist/node/gitvault-deploy.js.map +0 -1
- package/sdk/dist/node/gitvault-edge-fetch.d.ts.map +0 -1
- package/sdk/dist/node/gitvault-edge-fetch.js.map +0 -1
- package/sdk/dist/node/gitvault-handoff.d.ts.map +0 -1
- package/sdk/dist/node/gitvault-handoff.js.map +0 -1
- package/sdk/dist/node/gitvault-keystore.d.ts.map +0 -1
- package/sdk/dist/node/gitvault-keystore.js.map +0 -1
- package/sdk/dist/node/gitvault-member-bundle.d.ts.map +0 -1
- package/sdk/dist/node/gitvault-member-bundle.js.map +0 -1
- package/sdk/dist/node/gitvault-mirror-backend.d.ts.map +0 -1
- package/sdk/dist/node/gitvault-mirror-backend.js.map +0 -1
- package/sdk/dist/node/gitvault-mirror-config.d.ts.map +0 -1
- package/sdk/dist/node/gitvault-mirror-config.js.map +0 -1
- package/sdk/dist/node/gitvault-mirror.d.ts.map +0 -1
- package/sdk/dist/node/gitvault-mirror.js.map +0 -1
- package/sdk/dist/node/gitvault-native-crypto.d.ts.map +0 -1
- package/sdk/dist/node/gitvault-native-crypto.js.map +0 -1
- package/sdk/dist/node/gitvault-open-or-create.d.ts.map +0 -1
- package/sdk/dist/node/gitvault-open-or-create.js.map +0 -1
- package/sdk/dist/node/gitvault-prewarm.d.ts.map +0 -1
- package/sdk/dist/node/gitvault-prewarm.js.map +0 -1
- package/sdk/dist/node/gitvault-profile-scan.d.ts.map +0 -1
- package/sdk/dist/node/gitvault-profile-scan.js.map +0 -1
- package/sdk/dist/node/gitvault-prune.d.ts.map +0 -1
- package/sdk/dist/node/gitvault-prune.js.map +0 -1
- package/sdk/dist/node/gitvault-publication.d.ts.map +0 -1
- package/sdk/dist/node/gitvault-publication.js.map +0 -1
- package/sdk/dist/node/gitvault-push-to-create.d.ts.map +0 -1
- package/sdk/dist/node/gitvault-push-to-create.js.map +0 -1
- package/sdk/dist/node/gitvault-recover.d.ts.map +0 -1
- package/sdk/dist/node/gitvault-recover.js.map +0 -1
- package/sdk/dist/node/gitvault-restore.d.ts.map +0 -1
- package/sdk/dist/node/gitvault-restore.js.map +0 -1
- package/sdk/dist/node/gitvault-snapshot.d.ts.map +0 -1
- package/sdk/dist/node/gitvault-snapshot.js.map +0 -1
- package/sdk/dist/node/gitvault-writer-state.d.ts.map +0 -1
- package/sdk/dist/node/gitvault-writer-state.js.map +0 -1
package/lib/grants.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getSdk } from "./sdk.mjs";
|
|
2
|
-
import { reportSdkError, parseFlagJson } from "./sdk-errors.mjs";
|
|
2
|
+
import { reportSdkError, parseFlagJson, fail } from "./sdk-errors.mjs";
|
|
3
3
|
import {
|
|
4
4
|
normalizeArgv,
|
|
5
5
|
assertKnownFlags,
|
|
@@ -9,114 +9,213 @@ import {
|
|
|
9
9
|
failUnknownSubcommand,
|
|
10
10
|
} from "./argparse.mjs";
|
|
11
11
|
|
|
12
|
-
const HELP = `run402 grants — per-project capability grants
|
|
12
|
+
const HELP = `run402 grants — per-project capability grants and their grant keys
|
|
13
13
|
|
|
14
14
|
Usage:
|
|
15
15
|
run402 grants <subcommand> [args...]
|
|
16
16
|
|
|
17
17
|
Subcommands:
|
|
18
|
-
create <wallet> --capability <cap> [--project <project_id>] [--policy <json>] [--expires <iso8601>]
|
|
19
|
-
|
|
18
|
+
create <wallet> --capability <cap> [--project <project_id>] [--policy <json>] [--expires-at <iso8601>]
|
|
19
|
+
[--key [--kind <kind>] [--scope <json>] [--spend-cap <json>]]
|
|
20
|
+
Issue a grant; with --key also mint its first
|
|
21
|
+
grant key (the token is printed ONCE)
|
|
22
|
+
list [--project <project_id>] List grants with their keys nested (never tokens)
|
|
20
23
|
revoke <grant_id> [--project <project_id>]
|
|
21
|
-
Revoke a
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
Revoke a grant and every key minted against it
|
|
25
|
+
revoke-key <key_id> [--project <project_id>]
|
|
26
|
+
Revoke one grant key; the grant stays
|
|
27
|
+
rotate-key <key_id> [--project <project_id>]
|
|
28
|
+
Replace a lost key; the new token is printed ONCE
|
|
26
29
|
|
|
27
30
|
Notes:
|
|
28
|
-
-
|
|
29
|
-
|
|
31
|
+
- A grant is the permission row: this wallet may do this capability on this
|
|
32
|
+
project until this date. An agent with its own wallet needs only the grant.
|
|
33
|
+
- A grant key is a credential minted against exactly one grant: narrower than
|
|
34
|
+
it, spend-capped, expiring, revocable on its own, never an owner. Hand it to
|
|
35
|
+
an agent without a wallet: RUN402_GRANT_KEY=<token> run402 deploy ...
|
|
36
|
+
- Every subcommand requires owner of the project's org. Minting a key with a
|
|
37
|
+
sign-in session takes a fresh step-up.
|
|
38
|
+
- The key token is shown ONCE and cannot be read back. Store it immediately;
|
|
39
|
+
if you lose it, 'rotate-key' issues a new one.
|
|
30
40
|
- capability examples: deploy, functions:write.
|
|
31
41
|
- JSON in, JSON out.
|
|
32
42
|
|
|
33
43
|
Examples:
|
|
34
44
|
run402 grants create 0xf39Fd6...92266 --capability deploy --project prj_abc
|
|
35
|
-
run402 grants create
|
|
36
|
-
run402 grants
|
|
45
|
+
run402 grants create 0x90F3eB...F80a --capability deploy --key --project prj_abc
|
|
46
|
+
run402 grants list --project prj_abc
|
|
47
|
+
run402 grants revoke-key 5845f17b-... --project prj_abc
|
|
48
|
+
run402 grants revoke 1d86452e-... --project prj_abc
|
|
37
49
|
`;
|
|
38
50
|
|
|
39
51
|
const SUB_HELP = {
|
|
40
|
-
create: `run402 grants create — issue a per-project capability grant
|
|
52
|
+
create: `run402 grants create — issue a per-project capability grant, optionally with a grant key
|
|
41
53
|
|
|
42
54
|
Usage:
|
|
43
|
-
run402 grants create <wallet> --capability <cap> [--project <project_id>] [--policy <json>] [--expires <iso8601>]
|
|
44
|
-
|
|
45
|
-
Legacy (still supported):
|
|
46
|
-
run402 grants create <project_id> <wallet> <capability> [options]
|
|
55
|
+
run402 grants create <wallet> --capability <cap> [--project <project_id>] [--policy <json>] [--expires-at <iso8601>]
|
|
56
|
+
[--key [--kind <kind>] [--scope <json>] [--spend-cap <json>]]
|
|
47
57
|
|
|
48
58
|
Arguments:
|
|
49
|
-
<wallet>
|
|
59
|
+
<wallet> EVM address or named wallet the grant is issued to
|
|
50
60
|
|
|
51
61
|
Options:
|
|
52
|
-
--project <project_id>
|
|
53
|
-
--capability <cap>
|
|
54
|
-
--policy <json>
|
|
55
|
-
--expires <iso8601>
|
|
62
|
+
--project <project_id> Project to grant access to (defaults to the active project)
|
|
63
|
+
--capability <cap> e.g. deploy, functions:write
|
|
64
|
+
--policy <json> Capability-scoping policy object (gateway-interpreted)
|
|
65
|
+
--expires-at <iso8601> Expiry; omit for a non-expiring grant. With --key the
|
|
66
|
+
key takes the same expiry.
|
|
67
|
+
--key Also mint the grant's first grant key in the same
|
|
68
|
+
transaction. Its token is printed ONCE.
|
|
69
|
+
--kind <kind> Key kind (with --key). Default run402_agent_key, the
|
|
70
|
+
only kind that returns a bearer token.
|
|
71
|
+
--scope <json> Key scope (with --key), e.g. {"v":1,"capabilities":["deploy"]}.
|
|
72
|
+
Defaults to the grant's own capability; never wider.
|
|
73
|
+
--spend-cap <json> Key spend cap (with --key), e.g.
|
|
74
|
+
{"v":1,"currency":"usd_micros","per_period":5000000,"period":"month"}
|
|
56
75
|
|
|
57
76
|
Requires you to be an owner of the project's org.
|
|
77
|
+
`,
|
|
78
|
+
list: `run402 grants list — list a project's grants with their grant keys
|
|
79
|
+
|
|
80
|
+
Usage:
|
|
81
|
+
run402 grants list [--project <project_id>]
|
|
82
|
+
|
|
83
|
+
Each grant carries its keys under "keys" (kind, scope, spend cap, expiry,
|
|
84
|
+
revocation). Never returns a token or secret material.
|
|
58
85
|
`,
|
|
59
86
|
revoke: `run402 grants revoke — revoke a per-project capability grant
|
|
60
87
|
|
|
61
88
|
Usage:
|
|
62
89
|
run402 grants revoke <grant_id> [--project <project_id>]
|
|
63
90
|
|
|
64
|
-
|
|
65
|
-
|
|
91
|
+
Revokes the grant and every grant key minted against it. Requires you to be an
|
|
92
|
+
owner of the project's org.
|
|
93
|
+
`,
|
|
94
|
+
"revoke-key": `run402 grants revoke-key — revoke one grant key
|
|
66
95
|
|
|
96
|
+
Usage:
|
|
97
|
+
run402 grants revoke-key <key_id> [--project <project_id>]
|
|
98
|
+
|
|
99
|
+
Takes effect for every subsequent request. The grant and its other keys stay.
|
|
67
100
|
Requires you to be an owner of the project's org.
|
|
101
|
+
`,
|
|
102
|
+
"rotate-key": `run402 grants rotate-key — replace a grant key
|
|
103
|
+
|
|
104
|
+
Usage:
|
|
105
|
+
run402 grants rotate-key <key_id> [--project <project_id>]
|
|
106
|
+
|
|
107
|
+
Revokes the key and mints a fresh one against the same grant with the same
|
|
108
|
+
kind, scope, spend cap and expiry. The NEW token is printed once. Use this when
|
|
109
|
+
a token is lost or possibly exposed.
|
|
68
110
|
`,
|
|
69
111
|
};
|
|
70
112
|
|
|
71
|
-
const CREATE_VALUE_FLAGS = ["--project", "--capability", "--policy", "--expires"];
|
|
113
|
+
const CREATE_VALUE_FLAGS = ["--project", "--capability", "--policy", "--expires-at", "--kind", "--scope", "--spend-cap"];
|
|
114
|
+
const KEY_ONLY_FLAGS = ["--kind", "--scope", "--spend-cap"];
|
|
115
|
+
|
|
116
|
+
function warnTokenOnce(key) {
|
|
117
|
+
if (!key?.token) return;
|
|
118
|
+
console.error("");
|
|
119
|
+
console.error("The grant key token above is shown ONCE and cannot be read back.");
|
|
120
|
+
console.error("Store it now, then use it as: RUN402_GRANT_KEY=<token>");
|
|
121
|
+
}
|
|
72
122
|
|
|
73
123
|
async function create(args) {
|
|
74
124
|
const a = normalizeArgv(args);
|
|
75
|
-
assertKnownFlags(a, [...CREATE_VALUE_FLAGS, "--help", "-h"], CREATE_VALUE_FLAGS);
|
|
125
|
+
assertKnownFlags(a, [...CREATE_VALUE_FLAGS, "--key", "--help", "-h"], CREATE_VALUE_FLAGS);
|
|
76
126
|
const policyRaw = flagValue(a, "--policy");
|
|
77
|
-
const expiresAt = flagValue(a, "--expires");
|
|
78
|
-
const
|
|
127
|
+
const expiresAt = flagValue(a, "--expires-at") || undefined;
|
|
128
|
+
const capability = flagValue(a, "--capability");
|
|
129
|
+
const wantsKey = a.includes("--key");
|
|
130
|
+
const kind = flagValue(a, "--kind") || undefined;
|
|
131
|
+
const scopeRaw = flagValue(a, "--scope");
|
|
132
|
+
const spendCapRaw = flagValue(a, "--spend-cap");
|
|
79
133
|
const { projectId, rest } = resolveProjectSelector(a, { valueFlags: CREATE_VALUE_FLAGS });
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
missing: capabilityFlag ? "Missing <wallet>." : "Missing <wallet> and/or <capability>.",
|
|
134
|
+
const [wallet] = requirePositionalCount(rest, CREATE_VALUE_FLAGS, {
|
|
135
|
+
min: 1,
|
|
136
|
+
max: 1,
|
|
137
|
+
command: "run402 grants create <wallet> --capability <cap> [--project <project_id>] [--key]",
|
|
138
|
+
missing: "Missing <wallet>.",
|
|
86
139
|
});
|
|
87
|
-
|
|
88
|
-
|
|
140
|
+
if (!capability) {
|
|
141
|
+
fail({
|
|
142
|
+
code: "BAD_USAGE",
|
|
143
|
+
message: "Missing --capability <cap>.",
|
|
144
|
+
hint: "run402 grants create <wallet> --capability deploy [--project <project_id>]",
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
if (!wantsKey) {
|
|
148
|
+
const stray = KEY_ONLY_FLAGS.filter((f) => a.includes(f));
|
|
149
|
+
if (stray.length > 0) {
|
|
150
|
+
fail({
|
|
151
|
+
code: "BAD_USAGE",
|
|
152
|
+
message: `${stray.join(", ")} describe a grant key; add --key to mint one.`,
|
|
153
|
+
hint: "run402 grants create <wallet> --capability <cap> --key [--kind <kind>] [--scope <json>] [--spend-cap <json>]",
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
89
157
|
const policy = policyRaw != null ? parseFlagJson("--policy", policyRaw) : undefined;
|
|
158
|
+
let key;
|
|
159
|
+
if (wantsKey) {
|
|
160
|
+
key = {};
|
|
161
|
+
if (kind) key.kind = kind;
|
|
162
|
+
if (scopeRaw != null) key.scope = parseFlagJson("--scope", scopeRaw);
|
|
163
|
+
if (spendCapRaw != null) key.spendCap = parseFlagJson("--spend-cap", spendCapRaw);
|
|
164
|
+
if (expiresAt) key.expiresAt = expiresAt;
|
|
165
|
+
}
|
|
90
166
|
try {
|
|
91
167
|
const res = await getSdk().grants.create(projectId, {
|
|
92
168
|
wallet,
|
|
93
169
|
capability,
|
|
94
170
|
policy,
|
|
95
|
-
expiresAt
|
|
171
|
+
expiresAt,
|
|
172
|
+
key,
|
|
96
173
|
});
|
|
97
174
|
console.log(JSON.stringify(res, null, 2));
|
|
175
|
+
warnTokenOnce(res?.key);
|
|
98
176
|
} catch (err) {
|
|
99
177
|
reportSdkError(err);
|
|
100
178
|
}
|
|
101
179
|
}
|
|
102
180
|
|
|
103
|
-
async function
|
|
181
|
+
async function list(args) {
|
|
104
182
|
const a = normalizeArgv(args);
|
|
105
183
|
assertKnownFlags(a, ["--project", "--help", "-h"], ["--project"]);
|
|
106
184
|
const { projectId, rest } = resolveProjectSelector(a, { valueFlags: ["--project"] });
|
|
107
|
-
|
|
108
|
-
min:
|
|
109
|
-
max:
|
|
110
|
-
command: "run402 grants
|
|
111
|
-
missing: "
|
|
185
|
+
requirePositionalCount(rest, ["--project"], {
|
|
186
|
+
min: 0,
|
|
187
|
+
max: 0,
|
|
188
|
+
command: "run402 grants list [--project <project_id>]",
|
|
189
|
+
missing: "",
|
|
112
190
|
});
|
|
113
191
|
try {
|
|
114
|
-
console.log(JSON.stringify(await getSdk().grants.
|
|
192
|
+
console.log(JSON.stringify(await getSdk().grants.list(projectId), null, 2));
|
|
115
193
|
} catch (err) {
|
|
116
194
|
reportSdkError(err);
|
|
117
195
|
}
|
|
118
196
|
}
|
|
119
197
|
|
|
198
|
+
function oneIdCommand({ verb, idName, method }) {
|
|
199
|
+
return async (args) => {
|
|
200
|
+
const a = normalizeArgv(args);
|
|
201
|
+
assertKnownFlags(a, ["--project", "--help", "-h"], ["--project"]);
|
|
202
|
+
const { projectId, rest } = resolveProjectSelector(a, { valueFlags: ["--project"] });
|
|
203
|
+
const [id] = requirePositionalCount(rest, ["--project"], {
|
|
204
|
+
min: 1,
|
|
205
|
+
max: 1,
|
|
206
|
+
command: `run402 grants ${verb} <${idName}> [--project <project_id>]`,
|
|
207
|
+
missing: `Missing <${idName}>.`,
|
|
208
|
+
});
|
|
209
|
+
try {
|
|
210
|
+
const res = await getSdk().grants[method](projectId, id);
|
|
211
|
+
console.log(JSON.stringify(res, null, 2));
|
|
212
|
+
warnTokenOnce(res?.key);
|
|
213
|
+
} catch (err) {
|
|
214
|
+
reportSdkError(err);
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
120
219
|
export async function run(sub, args) {
|
|
121
220
|
if (!sub || sub === "--help" || sub === "-h") {
|
|
122
221
|
console.log(HELP);
|
|
@@ -128,7 +227,10 @@ export async function run(sub, args) {
|
|
|
128
227
|
}
|
|
129
228
|
switch (sub) {
|
|
130
229
|
case "create": await create(args); break;
|
|
131
|
-
case "
|
|
230
|
+
case "list": await list(args); break;
|
|
231
|
+
case "revoke": await oneIdCommand({ verb: "revoke", idName: "grant_id", method: "revoke" })(args); break;
|
|
232
|
+
case "revoke-key": await oneIdCommand({ verb: "revoke-key", idName: "key_id", method: "revokeKey" })(args); break;
|
|
233
|
+
case "rotate-key": await oneIdCommand({ verb: "rotate-key", idName: "key_id", method: "rotateKey" })(args); break;
|
|
132
234
|
default:
|
|
133
235
|
failUnknownSubcommand("grants", sub);
|
|
134
236
|
}
|
package/lib/init.mjs
CHANGED
|
@@ -51,7 +51,7 @@ Options:
|
|
|
51
51
|
nothing; RUN402_AGENT_NAME=<name> sets or overrides it. Change
|
|
52
52
|
it any time with \`run402 whoami --set-name <name>\`.
|
|
53
53
|
--git-remote Also 'git init' the current directory when it is not a
|
|
54
|
-
repository yet, so the
|
|
54
|
+
repository yet, so the vault remote can be added there.
|
|
55
55
|
Opt-in on purpose: init is often run outside a project
|
|
56
56
|
directory and must never create a repository somewhere you
|
|
57
57
|
did not ask it to. Inside an EXISTING repository the remote
|
|
@@ -62,11 +62,11 @@ Output:
|
|
|
62
62
|
tier, projects_saved, active_project_id, next_step }. Progress lines
|
|
63
63
|
(Config / Wallet / Balance / Tier / Next) go to stderr so a human
|
|
64
64
|
re-running interactively sees what's happening while a script piping stdout
|
|
65
|
-
to jq stays clean. The summary also carries {
|
|
66
|
-
remote), or {
|
|
67
|
-
added, or {
|
|
65
|
+
to jq stays clean. The summary also carries { vault } (the scaffolded
|
|
66
|
+
remote), or { vault: null, vault_skipped } naming why no remote was
|
|
67
|
+
added, or { vault: null, vault_error } when it could not be added.
|
|
68
68
|
\`projects_saved\` counts projects with LOCAL keys; \`active_project_id\` is the
|
|
69
|
-
project the
|
|
69
|
+
project the vault scaffold acted on.
|
|
70
70
|
|
|
71
71
|
init scaffolds the git remote only. It does NOT allocate the vault — that is
|
|
72
72
|
\`run402 repos create --project <project_id>\`, which mints key material and a
|
|
@@ -215,13 +215,13 @@ function errorMessage(err) {
|
|
|
215
215
|
}
|
|
216
216
|
|
|
217
217
|
/**
|
|
218
|
-
* Which project the
|
|
218
|
+
* Which project the vault scaffold acts on — and why it may act on none.
|
|
219
219
|
*
|
|
220
220
|
* The CLI-wide resolution order (`RUN402_PROJECT_ID`, then the active
|
|
221
221
|
* project), not `getActiveProjectId()` alone. Reading only the active project
|
|
222
222
|
* meant an agent that had exported `RUN402_PROJECT_ID` — the normal way to
|
|
223
223
|
* address a project without mutating machine state — got a silent no-op: no
|
|
224
|
-
* remote, no allocation, and no `
|
|
224
|
+
* remote, no allocation, and no `vault` key in the summary at all, with
|
|
225
225
|
* nothing anywhere saying `run402 projects use` was a prerequisite
|
|
226
226
|
* (dogfood #1, finding A). A command that exits 0 having done nothing is worse
|
|
227
227
|
* than an error, so the no-project case names itself.
|
|
@@ -375,7 +375,7 @@ export async function run(args = []) {
|
|
|
375
375
|
...(voucherCode ? { voucher: null } : {}),
|
|
376
376
|
tier: null,
|
|
377
377
|
projects_saved: 0,
|
|
378
|
-
/** The project init acted on — the input to the
|
|
378
|
+
/** The project init acted on — the input to the vault scaffold decision. */
|
|
379
379
|
active_project_id: null,
|
|
380
380
|
next_actions: [],
|
|
381
381
|
next_step: null,
|
|
@@ -637,17 +637,17 @@ export async function run(args = []) {
|
|
|
637
637
|
// local keys, so it is legitimately not counted here; `active_project_id`
|
|
638
638
|
// below is the field that says which project init actually acted on, and
|
|
639
639
|
// reading `projects_saved: 0` as "init did nothing" is what made the
|
|
640
|
-
//
|
|
640
|
+
// vault skip look silent (dogfood #1, finding A).
|
|
641
641
|
summary.projects_saved = Object.keys(store.projects).length;
|
|
642
642
|
line("Projects", `${summary.projects_saved} saved`);
|
|
643
643
|
|
|
644
|
-
// 5b.
|
|
644
|
+
// 5b. vault git remote (vault-client-surface, task 5.7).
|
|
645
645
|
//
|
|
646
646
|
// Purely LOCAL git. No vault is allocated and no key material is written
|
|
647
647
|
// here — the spec is explicit that neither exists until first capture, so the
|
|
648
648
|
// cold-start path gains no prompt and no new failure mode. Allocation happens
|
|
649
|
-
// on the first `git push origin <branch>` or `run402
|
|
650
|
-
// repo-first-onramp) — NOT on deploy: `
|
|
649
|
+
// on the first `git push origin <branch>` or `run402 repos capture` (D2,
|
|
650
|
+
// repo-first-onramp) — NOT on deploy: `applyWithVault` only ever reads an
|
|
651
651
|
// EXISTING vault's policy (D3) and never allocates one that does not exist,
|
|
652
652
|
// so a project with no vault deploys exactly as it always did.
|
|
653
653
|
//
|
|
@@ -664,11 +664,11 @@ export async function run(args = []) {
|
|
|
664
664
|
const { projectId: activeProjectId, skipped: noProjectSkip } = resolveScaffoldProject();
|
|
665
665
|
summary.active_project_id = activeProjectId;
|
|
666
666
|
if (noProjectSkip) {
|
|
667
|
-
summary.
|
|
668
|
-
summary.
|
|
669
|
-
line("
|
|
667
|
+
summary.vault = null;
|
|
668
|
+
summary.vault_skipped = noProjectSkip;
|
|
669
|
+
line("Vault", "skipped — no project selected (run402 projects use <project_id>)");
|
|
670
670
|
} else {
|
|
671
|
-
summary.
|
|
671
|
+
summary.vault = null;
|
|
672
672
|
try {
|
|
673
673
|
// Dynamic import: the scaffold is the only thing here that needs the
|
|
674
674
|
// Node SDK's hardened git runner, and a top-level import would drag it
|
|
@@ -681,43 +681,43 @@ export async function run(args = []) {
|
|
|
681
681
|
insideRepo = false;
|
|
682
682
|
}
|
|
683
683
|
if (!insideRepo && !scaffoldGitRemote) {
|
|
684
|
-
summary.
|
|
685
|
-
line("
|
|
684
|
+
summary.vault_skipped = "not a git repository — re-run with --git-remote to create one and add the remote";
|
|
685
|
+
line("Vault", "skipped — not a git repository (--git-remote creates one)");
|
|
686
686
|
} else {
|
|
687
687
|
const orgId = await resolveOwningOrgId(activeProjectId);
|
|
688
688
|
if (!orgId) {
|
|
689
|
-
summary.
|
|
690
|
-
line("
|
|
689
|
+
summary.vault_skipped = `could not resolve the owning org for ${activeProjectId} — the run402 remote was not added`;
|
|
690
|
+
line("Vault", "skipped — owning org unresolved");
|
|
691
691
|
} else {
|
|
692
|
-
const remote = await getSdk().
|
|
692
|
+
const remote = await getSdk().repos.scaffoldRemote({
|
|
693
693
|
repo_dir: process.cwd(),
|
|
694
694
|
org_id: orgId,
|
|
695
695
|
project_id: activeProjectId,
|
|
696
696
|
});
|
|
697
697
|
// `allocated: false` is stated, not left to be inferred: this was
|
|
698
698
|
// local git only, and no vault exists for the project yet.
|
|
699
|
-
summary.
|
|
699
|
+
summary.vault = { ...remote, allocated: false };
|
|
700
700
|
if (remote.already_present && remote.existing_url !== remote.url) {
|
|
701
701
|
// Left exactly as it was. Name the URL that is actually in place
|
|
702
702
|
// rather than implying the remote now points at this project.
|
|
703
|
-
line("
|
|
703
|
+
line("Vault", `remote '${remote.name}' already points at ${remote.existing_url} — left unchanged (${remote.reason})`);
|
|
704
704
|
} else if (remote.already_present) {
|
|
705
|
-
line("
|
|
705
|
+
line("Vault", `remote '${remote.name}' already set (${remote.url})`);
|
|
706
706
|
} else {
|
|
707
707
|
// D1: `origin` when it was free, `run402` when it was already
|
|
708
708
|
// taken by something else — `remote.reason` says which happened.
|
|
709
|
-
line("
|
|
709
|
+
line("Vault", `${remote.created_repository ? "initialized a repository and added" : "added"} remote '${remote.name}' -> ${remote.url} (${remote.reason})`);
|
|
710
710
|
}
|
|
711
711
|
}
|
|
712
712
|
}
|
|
713
713
|
} catch (err) {
|
|
714
714
|
const reason = errorMessage(err);
|
|
715
|
-
summary.
|
|
716
|
-
summary.
|
|
717
|
-
code: err?.body?.code ?? err?.code ?? "
|
|
715
|
+
summary.vault = null;
|
|
716
|
+
summary.vault_error = {
|
|
717
|
+
code: err?.body?.code ?? err?.code ?? "VAULT_SCAFFOLD_FAILED",
|
|
718
718
|
message: reason,
|
|
719
719
|
};
|
|
720
|
-
line("
|
|
720
|
+
line("Vault", `remote not added: ${reason}`);
|
|
721
721
|
}
|
|
722
722
|
}
|
|
723
723
|
|
package/lib/logs.mjs
CHANGED
|
@@ -62,7 +62,7 @@ Output:
|
|
|
62
62
|
|
|
63
63
|
Auth:
|
|
64
64
|
The project's cached service_key when present; otherwise your wallet /
|
|
65
|
-
session /
|
|
65
|
+
session / grant key, which the gateway authorizes with project.read — an org
|
|
66
66
|
member or teammate agent following an error fingerprint's drill-down needs
|
|
67
67
|
no project key.
|
|
68
68
|
|
package/lib/next-actions.mjs
CHANGED
|
@@ -81,7 +81,7 @@ export function upDeployAction() {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
/**
|
|
84
|
-
* `repos create` (and `
|
|
84
|
+
* `repos create` (and `vault init`) on an org with no slug set: the
|
|
85
85
|
* response's `address: null` had no pointer to WHY, or to the named-addressing
|
|
86
86
|
* feature at all. Owner-only. Wording deliberately
|
|
87
87
|
* omits a price: the first slug per org is free, and renames/re-claims cost
|
package/lib/org-context.mjs
CHANGED
|
@@ -97,15 +97,15 @@ function orgFromRoomEnv(env) {
|
|
|
97
97
|
* `r402.orgId` from `cwd`'s LOCAL git config (kygit-handoff design D10) —
|
|
98
98
|
* best-effort, gracefully degrading like `findBindingKey`: no repository,
|
|
99
99
|
* no pin, or a shape-invalid value all answer `null` rather than throwing,
|
|
100
|
-
* so a bare directory or a checkout with no
|
|
100
|
+
* so a bare directory or a checkout with no vault remote costs nothing.
|
|
101
101
|
* Checks a real vault checkout's pin first (gated on `r402.repoId`), then
|
|
102
102
|
* falls back to the bare org/room pin a room-only `rooms join <key>` writes
|
|
103
103
|
* in a directory with no vault at all (add-room-invite design D10).
|
|
104
104
|
*/
|
|
105
|
-
async function
|
|
105
|
+
async function readVaultPinnedOrgId(cwd) {
|
|
106
106
|
try {
|
|
107
|
-
const {
|
|
108
|
-
const pinned = await
|
|
107
|
+
const { readPinnedVaultRepo, readPinnedRoomBinding } = await import("#sdk/node");
|
|
108
|
+
const pinned = await readPinnedVaultRepo(cwd);
|
|
109
109
|
const orgId = trimmed(pinned?.org_id);
|
|
110
110
|
if (orgId && ORG_ID_RE.test(orgId)) return orgId;
|
|
111
111
|
const bare = await readPinnedRoomBinding(cwd);
|
|
@@ -254,14 +254,14 @@ export async function resolveOrg(input = {}, opts = {}) {
|
|
|
254
254
|
}
|
|
255
255
|
if (bindingOrg) return { orgId: bindingOrg, source: "binding", sourceDetail: bindingHit.file };
|
|
256
256
|
|
|
257
|
-
// --- Class 3.5:
|
|
257
|
+
// --- Class 3.5: vault local pin (kygit-handoff design D10) ------------
|
|
258
258
|
// `r402.orgId` in this checkout's LOCAL git config — written by every
|
|
259
|
-
//
|
|
259
|
+
// vault pin site (`repos create`, `resume`, address resolution), a
|
|
260
260
|
// rung below env/binding and above profile state: a checkout that IS a
|
|
261
261
|
// resumed/pinned vault should resolve its own org with zero configuration,
|
|
262
262
|
// but an explicit env var or a committed `.run402.json` binding still wins.
|
|
263
|
-
const pinnedOrg = await
|
|
264
|
-
if (pinnedOrg) return { orgId: pinnedOrg, source: "
|
|
263
|
+
const pinnedOrg = await readVaultPinnedOrgId(cwd);
|
|
264
|
+
if (pinnedOrg) return { orgId: pinnedOrg, source: "vault_pin", sourceDetail: "r402.orgId (local git config)" };
|
|
265
265
|
|
|
266
266
|
// --- Class 4: profile state ----------------------------------------------
|
|
267
267
|
const selected = trimmed(coreGetActiveOrgId());
|
|
@@ -434,7 +434,7 @@ export function orgProvenance(resolved) {
|
|
|
434
434
|
* owns THIS project id", which is what a project-scoped scaffold needs and
|
|
435
435
|
* where an `--org` override would be a mis-binding, not a convenience.
|
|
436
436
|
*
|
|
437
|
-
* WHY IT CACHES (task 5.12c): the
|
|
437
|
+
* WHY IT CACHES (task 5.12c): the vault scaffold is the one part of
|
|
438
438
|
* `run402 init` the client-surface spec says adds no network dependency to the
|
|
439
439
|
* cold-start path, and resolving the org through `projects.list()` quietly made
|
|
440
440
|
* that untrue. `org_id` is a non-secret routing identifier the control plane
|
package/lib/orgs.mjs
CHANGED
|
@@ -44,7 +44,7 @@ Usage:
|
|
|
44
44
|
run402 orgs members add [<org_id>] <wallet_address> [--role <role>]
|
|
45
45
|
run402 orgs members role [<org_id>] --principal <principal_id> --role <role>
|
|
46
46
|
run402 orgs members rm [<org_id>] --principal <principal_id>
|
|
47
|
-
run402 orgs members revoke-key [<org_id>] --principal <principal_id> [--reason <why>] (owner + step-up; revokes the member's
|
|
47
|
+
run402 orgs members revoke-key [<org_id>] --principal <principal_id> [--reason <why>] (owner + step-up; revokes the member's vault encryption key — its next vault operation enrolls afresh)
|
|
48
48
|
run402 orgs invite list [<org_id>]
|
|
49
49
|
run402 orgs invite create [<org_id>] --email <email> [--role <role>] [--ttl-hours N]
|
|
50
50
|
run402 orgs invite rm [<org_id>] --principal <principal_id>
|
|
@@ -53,7 +53,7 @@ Usage:
|
|
|
53
53
|
omit it and the org comes from --org, then RUN402_ORG, then the .run402.json
|
|
54
54
|
binding, then 'run402 orgs use'. Inside a bound checkout two agents add each
|
|
55
55
|
other with nothing to look up: run402 orgs members add <wallet_address> --role developer.
|
|
56
|
-
A developer-or-above add REFUSES (
|
|
56
|
+
A developer-or-above add REFUSES (VAULT_WRITER_NOT_ADMITTED) unless this session's key is an admitted writer on every vault of the org, so the new member can push at once; a current writer admits your key with run402 repos access sync.
|
|
57
57
|
The second attribute may also be passed positionally (run402 orgs members add <wallet_address>).
|
|
58
58
|
|
|
59
59
|
Subcommands:
|
|
@@ -186,7 +186,7 @@ Usage:
|
|
|
186
186
|
run402 orgs members add [<org_id>] <wallet_address> [--role <role>]
|
|
187
187
|
run402 orgs members role [<org_id>] --principal <principal_id> --role <role>
|
|
188
188
|
run402 orgs members rm [<org_id>] --principal <principal_id>
|
|
189
|
-
run402 orgs members revoke-key [<org_id>] --principal <principal_id> [--reason <why>] (owner + step-up; revokes the member's
|
|
189
|
+
run402 orgs members revoke-key [<org_id>] --principal <principal_id> [--reason <why>] (owner + step-up; revokes the member's vault encryption key — its next vault operation enrolls afresh)
|
|
190
190
|
|
|
191
191
|
Roles: ${ROLE_LIST} (add defaults to developer). Mutations require an active owner.
|
|
192
192
|
Demoting/removing the org's only active owner fails with 409 LAST_OWNER.
|
|
@@ -202,7 +202,7 @@ Usage:
|
|
|
202
202
|
omit it and the org comes from --org, then RUN402_ORG, then the .run402.json
|
|
203
203
|
binding, then 'run402 orgs use'. Inside a bound checkout two agents add each
|
|
204
204
|
other with nothing to look up: run402 orgs members add <wallet_address> --role developer.
|
|
205
|
-
A developer-or-above add REFUSES (
|
|
205
|
+
A developer-or-above add REFUSES (VAULT_WRITER_NOT_ADMITTED) unless this session's key is an admitted writer on every vault of the org, so the new member can push at once; a current writer admits your key with run402 repos access sync.
|
|
206
206
|
The second attribute may also be passed positionally (run402 orgs members add <wallet_address>).
|
|
207
207
|
|
|
208
208
|
An invite is claimed at the recipient's first login. Mutations require an active owner
|
|
@@ -223,7 +223,7 @@ Requires an admin+ membership on the org. Newest-first. Page forward with --afte
|
|
|
223
223
|
|
|
224
224
|
|
|
225
225
|
/**
|
|
226
|
-
*
|
|
226
|
+
* vault-multi-writer (rev 47) task 6.3 / design D3 — decided 2026-09-03
|
|
227
227
|
* (Tal): `orgs members add` REFUSES a writer-eligible add (developer or
|
|
228
228
|
* above) when this session's own key is not an admitted writer on EVERY
|
|
229
229
|
* vault of the org, because a developer who cannot push is not the member
|
|
@@ -237,11 +237,11 @@ Requires an admin+ membership on the org. Newest-first. Page forward with --afte
|
|
|
237
237
|
* Every read failure REFUSES too (an unverifiable gate is not a passed one).
|
|
238
238
|
*/
|
|
239
239
|
async function assertCallerCanAdmitWritersEverywhere(sdk, orgId, effectiveRole) {
|
|
240
|
-
const listing = await sdk.
|
|
240
|
+
const listing = await sdk.repos.listByOrg(orgId);
|
|
241
241
|
const vaults = listing?.vaults ?? [];
|
|
242
242
|
const blocked = [];
|
|
243
243
|
for (const v of vaults) {
|
|
244
|
-
const st = await sdk.
|
|
244
|
+
const st = await sdk.repos.status({ repo_id: v.repo_id, reconcile: "forbidden" });
|
|
245
245
|
const vault = st?.vault ?? null;
|
|
246
246
|
if (!vault || vault.read_only_terminal) continue;
|
|
247
247
|
const me = st?.keystore?.identity_fingerprint ?? null;
|
|
@@ -250,7 +250,7 @@ async function assertCallerCanAdmitWritersEverywhere(sdk, orgId, effectiveRole)
|
|
|
250
250
|
}
|
|
251
251
|
if (blocked.length > 0) {
|
|
252
252
|
fail({
|
|
253
|
-
code: "
|
|
253
|
+
code: "VAULT_WRITER_NOT_ADMITTED",
|
|
254
254
|
message: `refusing to add a ${effectiveRole}: this session's key is not an admitted writer on ${blocked.length} of this org's ${vaults.length} vault(s), so the new member could not push there`,
|
|
255
255
|
details: { role: effectiveRole, vaults: blocked, checked: vaults.length },
|
|
256
256
|
next_actions: [
|
|
@@ -266,7 +266,7 @@ async function assertCallerCanAdmitWritersEverywhere(sdk, orgId, effectiveRole)
|
|
|
266
266
|
}
|
|
267
267
|
|
|
268
268
|
/**
|
|
269
|
-
* `orgs members rm`'s inline epoch rotation (
|
|
269
|
+
* `orgs members rm`'s inline epoch rotation (vault-multi-writer D6, the
|
|
270
270
|
* kygit-handoff member-removal decision): for every vault of the org where
|
|
271
271
|
* this session's key is an admitted writer, drive
|
|
272
272
|
* `rotateEpochForKeyRevocation(principalId)` — the one self-contained
|
|
@@ -275,7 +275,7 @@ async function assertCallerCanAdmitWritersEverywhere(sdk, orgId, effectiveRole)
|
|
|
275
275
|
* no vault (nothing to rotate, nothing to report).
|
|
276
276
|
*/
|
|
277
277
|
async function rotateOrgVaultsAfterRemoval(sdk, orgId, principalId) {
|
|
278
|
-
const listing = await sdk.
|
|
278
|
+
const listing = await sdk.repos.listByOrg(orgId);
|
|
279
279
|
const vaults = listing?.vaults ?? [];
|
|
280
280
|
if (vaults.length === 0) return null;
|
|
281
281
|
const rotated = [];
|
|
@@ -284,7 +284,7 @@ async function rotateOrgVaultsAfterRemoval(sdk, orgId, principalId) {
|
|
|
284
284
|
for (const v of vaults) {
|
|
285
285
|
let st;
|
|
286
286
|
try {
|
|
287
|
-
st = await sdk.
|
|
287
|
+
st = await sdk.repos.status({ repo_id: v.repo_id, reconcile: "forbidden" });
|
|
288
288
|
} catch (err) {
|
|
289
289
|
errors.push({ repo_id: v.repo_id, code: err?.code ?? null, error: err?.message ?? String(err) });
|
|
290
290
|
continue;
|
|
@@ -300,7 +300,7 @@ async function rotateOrgVaultsAfterRemoval(sdk, orgId, principalId) {
|
|
|
300
300
|
try {
|
|
301
301
|
// reason:"member_removed" — writer-capable (no owner step-up); the
|
|
302
302
|
// removal itself advanced the counters this rotation is fenced on.
|
|
303
|
-
const r = await sdk.
|
|
303
|
+
const r = await sdk.repos.rotateEpochForMemberRemoval({ repo_id: v.repo_id });
|
|
304
304
|
rotated.push({ repo_id: v.repo_id, new_epoch: r.new_epoch, generation: r.generation, included: r.included.length, writers_removed: r.writers_removed?.length ?? 0, self_check: r.self_check });
|
|
305
305
|
} catch (err) {
|
|
306
306
|
errors.push({ repo_id: v.repo_id, code: err?.code ?? null, error: err?.message ?? String(err) });
|
|
@@ -717,7 +717,7 @@ async function runMember(args) {
|
|
|
717
717
|
await assertCallerCanAdmitWritersEverywhere(sdk, org, effectiveRole);
|
|
718
718
|
}
|
|
719
719
|
const res = await sdk.org(org).members.add({ wallet, role: role || undefined });
|
|
720
|
-
//
|
|
720
|
+
// vault-multi-writer (rev 47) task 6.3 — the gateway names which of
|
|
721
721
|
// this org's vaults now have a pending writer candidate (D3: there is
|
|
722
722
|
// no server-side writer admission — the client holds the keys) via a
|
|
723
723
|
// `sync_writers` next_action carrying `vault_ids[]`. Run the ACTUAL
|
|
@@ -737,7 +737,7 @@ async function runMember(args) {
|
|
|
737
737
|
const errors = [];
|
|
738
738
|
for (const vaultId of syncTarget.vault_ids) {
|
|
739
739
|
try {
|
|
740
|
-
const r = await sdk.
|
|
740
|
+
const r = await sdk.repos.reconcile({ repo_id: vaultId });
|
|
741
741
|
if (!r.eligible) notEligible.push(vaultId);
|
|
742
742
|
else if (r.admitted.length > 0) admitted.push({ repo_id: vaultId, admitted: r.admitted });
|
|
743
743
|
} catch (err) {
|
|
@@ -748,7 +748,7 @@ async function runMember(args) {
|
|
|
748
748
|
}
|
|
749
749
|
console.log(JSON.stringify(writerSync ? { ...res, writer_sync: writerSync } : res, null, 2));
|
|
750
750
|
if (writerSync?.not_eligible.length > 0) {
|
|
751
|
-
console.error(`writer sync: this session is not (yet) a writer on ${writerSync.not_eligible.length} vault(s) — the new member is a pending writer candidate there until a CURRENT writer's next
|
|
751
|
+
console.error(`writer sync: this session is not (yet) a writer on ${writerSync.not_eligible.length} vault(s) — the new member is a pending writer candidate there until a CURRENT writer's next vault operation admits them.`);
|
|
752
752
|
}
|
|
753
753
|
if (writerSync?.errors.length > 0) {
|
|
754
754
|
for (const e of writerSync.errors) console.error(`writer sync: ${e.repo_id} — ${e.error}`);
|
|
@@ -760,10 +760,10 @@ async function runMember(args) {
|
|
|
760
760
|
}
|
|
761
761
|
|
|
762
762
|
if (memberAction === "revoke-key") {
|
|
763
|
-
//
|
|
764
|
-
//
|
|
763
|
+
// vault-agent-envelopes D3: owner + step-up revokes a member's current
|
|
764
|
+
// vault encryption key — the ONLY rotation path for a member that shares
|
|
765
765
|
// an org with another custodian (a lost/rebuilt keystore fails
|
|
766
|
-
//
|
|
766
|
+
// VAULT_KEY_ROTATION_REQUIRED and points here). Adapter only.
|
|
767
767
|
const a = normalizeArgv(rest);
|
|
768
768
|
assertKnownFlags(a, ["--org", "--principal", "--reason", "--help", "-h"], ["--org", "--principal", "--reason"]);
|
|
769
769
|
const principalFlag = flagValue(a, "--principal");
|
|
@@ -818,7 +818,7 @@ async function runMember(args) {
|
|
|
818
818
|
try {
|
|
819
819
|
const sdk = getSdk();
|
|
820
820
|
const res = await sdk.org(org).members.revoke(principalId);
|
|
821
|
-
//
|
|
821
|
+
// vault-multi-writer D6 — a removal rides `rotate_epoch`: the gateway
|
|
822
822
|
// has just blocked the principal's writer keys and flipped its desired
|
|
823
823
|
// row to pending_removal, so every ordinary push on every vault of this
|
|
824
824
|
// org now refuses EPOCH_ROTATION_REQUIRED until a surviving writer
|