run402 4.93.2 → 4.94.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 +43 -41
- package/cli.mjs +34 -29
- package/core-dist/config.js +52 -18
- package/core-dist/control-plane-session.js +31 -6
- package/core-dist/keystore.js +7 -7
- package/core-dist/profiles.js +9 -7
- package/core-dist/{allowance-auth.js → wallet-auth.js} +15 -15
- package/{sdk/core-dist/allowance.js → core-dist/wallet.js} +20 -20
- package/{sdk/core-dist/write-auth-session.js → core-dist/write-approvals.js} +19 -19
- package/gitvault-surface.json +1 -1
- package/lib/admin.mjs +1 -1
- package/lib/agent.mjs +20 -18
- package/lib/approve.mjs +81 -0
- package/lib/apps.mjs +3 -3
- package/lib/assets.mjs +2 -2
- package/lib/billing.mjs +6 -6
- package/lib/buzz-doctor.mjs +1 -1
- package/lib/buzz-notifications.mjs +1 -1
- package/lib/buzz.mjs +1 -1
- package/lib/buzz.test.mjs +1 -1
- package/lib/ci.mjs +4 -4
- package/lib/cold-start.mjs +21 -21
- package/lib/command-manifest.mjs +20 -34
- package/lib/config.mjs +24 -24
- package/lib/contacts.mjs +13 -13
- package/lib/contracts.mjs +1 -1
- package/lib/credentials.mjs +2 -2
- package/lib/daemon-path.mjs +1 -1
- package/lib/delegates.mjs +1 -1
- package/lib/deliveries.mjs +4 -4
- package/lib/deploy-rehearse-discovery.test.mjs +7 -7
- package/lib/deploy-v2.mjs +232 -184
- package/lib/deploy.mjs +21 -53
- package/lib/doctor-source-scan.mjs +1 -1
- package/lib/doctor.mjs +57 -57
- package/lib/escalations.mjs +1 -1
- package/lib/feedback.mjs +10 -10
- package/lib/gitvault-daemon.mjs +1 -1
- package/lib/image.mjs +3 -3
- package/lib/init.mjs +66 -66
- package/lib/lightning-wallet.mjs +28 -28
- package/lib/login.mjs +207 -0
- package/lib/logout.mjs +53 -0
- package/lib/next-actions.mjs +15 -15
- package/lib/org.mjs +100 -13
- package/lib/pay.mjs +1 -1
- package/lib/projects.mjs +26 -29
- package/lib/redeem.mjs +7 -8
- package/lib/remote-helper-session.mjs +12 -12
- package/lib/repos.mjs +62 -62
- package/lib/rooms.mjs +103 -67
- package/lib/sdk-errors.mjs +9 -4
- package/lib/sdk.mjs +3 -3
- package/lib/service.mjs +5 -5
- package/lib/session-proofs.mjs +21 -0
- package/lib/sign-in.mjs +296 -0
- package/lib/sites.mjs +8 -8
- package/lib/stats.mjs +2 -2
- package/lib/status.mjs +25 -25
- package/lib/subdomains.mjs +19 -19
- package/lib/subscriptions.mjs +8 -8
- package/lib/tier.mjs +29 -22
- package/lib/transfer.mjs +58 -85
- package/lib/up.mjs +7 -7
- package/lib/wallet-context.mjs +3 -3
- package/lib/wallet-context.test.mjs +3 -3
- package/lib/wallets.mjs +196 -31
- package/lib/webhook-secret.mjs +6 -6
- package/lib/whoami.mjs +75 -0
- package/package.json +1 -1
- package/sdk/core-dist/config.js +52 -18
- package/sdk/core-dist/control-plane-session.js +31 -6
- package/sdk/core-dist/keystore.js +7 -7
- package/sdk/core-dist/profiles.js +9 -7
- package/sdk/core-dist/{allowance-auth.js → wallet-auth.js} +15 -15
- package/{core-dist/allowance.js → sdk/core-dist/wallet.js} +20 -20
- package/{core-dist/write-auth-session.js → sdk/core-dist/write-approvals.js} +19 -19
- package/sdk/dist/actions.d.ts +1 -1
- package/sdk/dist/actions.d.ts.map +1 -1
- package/sdk/dist/control-plane-credentials.d.ts +8 -8
- package/sdk/dist/control-plane-credentials.js +7 -7
- package/sdk/dist/credentials.d.ts +19 -19
- package/sdk/dist/credentials.d.ts.map +1 -1
- package/sdk/dist/credentials.js +1 -1
- package/sdk/dist/errors.d.ts +24 -26
- package/sdk/dist/errors.d.ts.map +1 -1
- package/sdk/dist/errors.js +34 -37
- package/sdk/dist/errors.js.map +1 -1
- package/sdk/dist/index.d.ts +29 -16
- package/sdk/dist/index.d.ts.map +1 -1
- package/sdk/dist/index.js +29 -16
- package/sdk/dist/index.js.map +1 -1
- package/sdk/dist/kernel.d.ts +1 -1
- package/sdk/dist/kernel.d.ts.map +1 -1
- package/sdk/dist/kernel.js +10 -9
- package/sdk/dist/kernel.js.map +1 -1
- package/sdk/dist/namespaces/admin.d.ts +51 -120
- package/sdk/dist/namespaces/admin.d.ts.map +1 -1
- package/sdk/dist/namespaces/admin.js +41 -45
- package/sdk/dist/namespaces/admin.js.map +1 -1
- package/sdk/dist/namespaces/agent.d.ts +2 -2
- package/sdk/dist/namespaces/agent.d.ts.map +1 -1
- package/sdk/dist/namespaces/agent.js +2 -2
- package/sdk/dist/namespaces/agent.js.map +1 -1
- package/sdk/dist/namespaces/ai.d.ts +1 -1
- package/sdk/dist/namespaces/ai.js +1 -1
- package/sdk/dist/namespaces/billing.d.ts +6 -3
- package/sdk/dist/namespaces/billing.d.ts.map +1 -1
- package/sdk/dist/namespaces/billing.js.map +1 -1
- package/sdk/dist/namespaces/buzz-notifications.types.d.ts +1 -1
- package/sdk/dist/namespaces/buzz.d.ts +2 -2
- package/sdk/dist/namespaces/buzz.d.ts.map +1 -1
- package/sdk/dist/namespaces/buzz.js +2 -2
- package/sdk/dist/namespaces/buzz.js.map +1 -1
- package/sdk/dist/namespaces/buzz.types.d.ts +2 -2
- package/sdk/dist/namespaces/buzz.types.d.ts.map +1 -1
- package/sdk/dist/namespaces/credentials.types.d.ts +1 -1
- package/sdk/dist/namespaces/deploy.d.ts +1 -1
- package/sdk/dist/namespaces/deploy.js +5 -5
- package/sdk/dist/namespaces/deploy.js.map +1 -1
- package/sdk/dist/namespaces/deploy.types.d.ts +4 -4
- package/sdk/dist/namespaces/escalations.d.ts +1 -1
- package/sdk/dist/namespaces/escalations.js +1 -1
- package/sdk/dist/namespaces/escalations.types.d.ts +2 -2
- package/sdk/dist/namespaces/escalations.types.d.ts.map +1 -1
- package/sdk/dist/namespaces/events.types.d.ts +1 -1
- package/sdk/dist/namespaces/functions.d.ts +1 -1
- package/sdk/dist/namespaces/functions.js +1 -1
- package/sdk/dist/namespaces/gitvault.crypto.d.ts +1 -1
- package/sdk/dist/namespaces/gitvault.d.ts +24 -24
- package/sdk/dist/namespaces/gitvault.d.ts.map +1 -1
- package/sdk/dist/namespaces/gitvault.js +109 -109
- package/sdk/dist/namespaces/gitvault.js.map +1 -1
- package/sdk/dist/namespaces/me.d.ts +120 -0
- package/sdk/dist/namespaces/me.d.ts.map +1 -0
- package/sdk/dist/namespaces/me.js +34 -0
- package/sdk/dist/namespaces/me.js.map +1 -0
- package/sdk/dist/namespaces/org.d.ts +108 -6
- package/sdk/dist/namespaces/org.d.ts.map +1 -1
- package/sdk/dist/namespaces/org.js +63 -7
- package/sdk/dist/namespaces/org.js.map +1 -1
- package/sdk/dist/namespaces/org.types.d.ts +29 -9
- package/sdk/dist/namespaces/org.types.d.ts.map +1 -1
- package/sdk/dist/namespaces/pay.js +3 -3
- package/sdk/dist/namespaces/pay.js.map +1 -1
- package/sdk/dist/namespaces/projects.d.ts +9 -9
- package/sdk/dist/namespaces/projects.js +15 -15
- package/sdk/dist/namespaces/projects.js.map +1 -1
- package/sdk/dist/namespaces/projects.types.d.ts +28 -12
- package/sdk/dist/namespaces/projects.types.d.ts.map +1 -1
- package/sdk/dist/namespaces/rooms.d.ts +7 -7
- package/sdk/dist/namespaces/rooms.js +17 -17
- package/sdk/dist/namespaces/rooms.js.map +1 -1
- package/sdk/dist/namespaces/rooms.types.d.ts +9 -9
- package/sdk/dist/namespaces/rooms.types.d.ts.map +1 -1
- package/sdk/dist/namespaces/service.d.ts +1 -1
- package/sdk/dist/namespaces/service.js +1 -1
- package/sdk/dist/namespaces/{operator-session.d.ts → session.d.ts} +135 -51
- package/sdk/dist/namespaces/session.d.ts.map +1 -0
- package/sdk/dist/namespaces/{operator-session.js → session.js} +118 -30
- package/sdk/dist/namespaces/session.js.map +1 -0
- package/sdk/dist/namespaces/subdomains.d.ts +5 -5
- package/sdk/dist/namespaces/subdomains.d.ts.map +1 -1
- package/sdk/dist/namespaces/subdomains.js +6 -6
- package/sdk/dist/namespaces/subdomains.js.map +1 -1
- package/sdk/dist/namespaces/tier.d.ts +43 -20
- package/sdk/dist/namespaces/tier.d.ts.map +1 -1
- package/sdk/dist/namespaces/tier.js +7 -7
- package/sdk/dist/namespaces/tier.js.map +1 -1
- package/sdk/dist/namespaces/transfers.d.ts +74 -71
- package/sdk/dist/namespaces/transfers.d.ts.map +1 -1
- package/sdk/dist/namespaces/transfers.js +31 -38
- package/sdk/dist/namespaces/transfers.js.map +1 -1
- package/sdk/dist/namespaces/vouchers.d.ts +9 -10
- package/sdk/dist/namespaces/vouchers.d.ts.map +1 -1
- package/sdk/dist/namespaces/vouchers.js +4 -5
- package/sdk/dist/namespaces/vouchers.js.map +1 -1
- package/sdk/dist/namespaces/wallets.d.ts +72 -3
- package/sdk/dist/namespaces/wallets.d.ts.map +1 -1
- package/sdk/dist/namespaces/wallets.js +141 -3
- package/sdk/dist/namespaces/wallets.js.map +1 -1
- package/sdk/dist/namespaces/write-approval.d.ts +101 -0
- package/sdk/dist/namespaces/write-approval.d.ts.map +1 -0
- package/sdk/dist/namespaces/write-approval.js +62 -0
- package/sdk/dist/namespaces/write-approval.js.map +1 -0
- package/sdk/dist/node/actions-node.d.ts +3 -3
- package/sdk/dist/node/actions-node.d.ts.map +1 -1
- package/sdk/dist/node/actions-node.js +36 -36
- package/sdk/dist/node/actions-node.js.map +1 -1
- package/sdk/dist/node/{bearer-claim-key.d.ts → bearer-redeem-key.d.ts} +12 -12
- package/sdk/dist/node/bearer-redeem-key.d.ts.map +1 -0
- package/sdk/dist/node/{bearer-claim-key.js → bearer-redeem-key.js} +21 -21
- package/sdk/dist/node/bearer-redeem-key.js.map +1 -0
- package/sdk/dist/node/ci.d.ts +1 -1
- package/sdk/dist/node/ci.d.ts.map +1 -1
- package/sdk/dist/node/ci.js +6 -6
- package/sdk/dist/node/ci.js.map +1 -1
- package/sdk/dist/node/credentials.d.ts +17 -16
- package/sdk/dist/node/credentials.d.ts.map +1 -1
- package/sdk/dist/node/credentials.js +28 -27
- package/sdk/dist/node/credentials.js.map +1 -1
- package/sdk/dist/node/funding-recovery.d.ts +1 -1
- package/sdk/dist/node/funding-recovery.d.ts.map +1 -1
- package/sdk/dist/node/funding-recovery.js +1 -1
- package/sdk/dist/node/funding-recovery.js.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-handoff.d.ts +22 -22
- package/sdk/dist/node/gitvault-handoff.d.ts.map +1 -1
- package/sdk/dist/node/gitvault-handoff.js +45 -45
- package/sdk/dist/node/gitvault-handoff.js.map +1 -1
- package/sdk/dist/node/gitvault-prewarm.js +7 -7
- package/sdk/dist/node/gitvault-prewarm.js.map +1 -1
- package/sdk/dist/node/gitvault-restore.d.ts +1 -1
- package/sdk/dist/node/gitvault-restore.js +1 -1
- package/sdk/dist/node/index.d.ts +16 -16
- package/sdk/dist/node/index.d.ts.map +1 -1
- package/sdk/dist/node/index.js +15 -15
- package/sdk/dist/node/index.js.map +1 -1
- package/sdk/dist/node/org-adopt.d.ts +49 -0
- package/sdk/dist/node/org-adopt.d.ts.map +1 -0
- package/sdk/dist/node/org-adopt.js +77 -0
- package/sdk/dist/node/org-adopt.js.map +1 -0
- package/sdk/dist/node/paid-fetch.d.ts +7 -7
- package/sdk/dist/node/paid-fetch.d.ts.map +1 -1
- package/sdk/dist/node/paid-fetch.js +54 -54
- package/sdk/dist/node/paid-fetch.js.map +1 -1
- package/sdk/dist/node/preflight.d.ts +1 -1
- package/sdk/dist/node/preflight.d.ts.map +1 -1
- package/sdk/dist/node/preflight.js +1 -1
- package/sdk/dist/node/preflight.js.map +1 -1
- package/sdk/dist/node/source-scan.d.ts +1 -1
- package/sdk/dist/node/source-scan.js +2 -2
- package/sdk/dist/node/source-scan.js.map +1 -1
- package/sdk/dist/scoped.d.ts +3 -3
- package/sdk/dist/scoped.d.ts.map +1 -1
- package/sdk/dist/scoped.js +2 -2
- package/sdk/dist/scoped.js.map +1 -1
- package/core-dist/operator-session.js +0 -154
- package/lib/allowance.mjs +0 -355
- package/lib/operator-proofs.mjs +0 -22
- package/lib/operator.mjs +0 -771
- package/sdk/core-dist/operator-session.js +0 -154
- package/sdk/dist/namespaces/allowance.d.ts +0 -79
- package/sdk/dist/namespaces/allowance.d.ts.map +0 -1
- package/sdk/dist/namespaces/allowance.js +0 -149
- package/sdk/dist/namespaces/allowance.js.map +0 -1
- package/sdk/dist/namespaces/operator-session.d.ts.map +0 -1
- package/sdk/dist/namespaces/operator-session.js.map +0 -1
- package/sdk/dist/namespaces/operator.d.ts +0 -359
- package/sdk/dist/namespaces/operator.d.ts.map +0 -1
- package/sdk/dist/namespaces/operator.js +0 -272
- package/sdk/dist/namespaces/operator.js.map +0 -1
- package/sdk/dist/node/bearer-claim-key.d.ts.map +0 -1
- package/sdk/dist/node/bearer-claim-key.js.map +0 -1
- package/sdk/dist/node/operator-claim.d.ts +0 -49
- package/sdk/dist/node/operator-claim.d.ts.map +0 -1
- package/sdk/dist/node/operator-claim.js +0 -77
- package/sdk/dist/node/operator-claim.js.map +0 -1
package/lib/rooms.mjs
CHANGED
|
@@ -44,6 +44,8 @@ const INVITE_VALUE_FLAGS = [...ROOM_FLAGS, "--note", "--note-file", "--expires-i
|
|
|
44
44
|
const HELP = `run402 rooms — arrive in a room, see who is live, leave when done
|
|
45
45
|
|
|
46
46
|
Usage:
|
|
47
|
+
run402 rooms list [--org <org_id>]
|
|
48
|
+
run402 rooms get <room_key> [--org <org_id>]
|
|
47
49
|
run402 rooms join [--name <name>] [--task <text>]
|
|
48
50
|
run402 rooms join <kri1_…> [--json]
|
|
49
51
|
run402 rooms invite [--note <text> | --note-file <path> | stdin]
|
|
@@ -57,7 +59,7 @@ Addressing:
|
|
|
57
59
|
wallet profile's selected org
|
|
58
60
|
|
|
59
61
|
Room Invite (mint a key from the room you stand in, join through one):
|
|
60
|
-
- \`rooms invite\` mints a single-use \`kri1_…\` bearer key. Whoever
|
|
62
|
+
- \`rooms invite\` mints a single-use \`kri1_…\` bearer key. Whoever redeems it
|
|
61
63
|
FIRST becomes a permanent \`viewer\` of this org — the narrowest membership
|
|
62
64
|
that can message, and NEVER wider: there is no --role, and a viewer can
|
|
63
65
|
never be auto-admitted as a vault writer. To bring a collaborator into the
|
|
@@ -65,9 +67,9 @@ Room Invite (mint a key from the room you stand in, join through one):
|
|
|
65
67
|
instead — this door is talk-only.
|
|
66
68
|
- The key is printed to stdout EXACTLY ONCE (\`--json\` still keeps it out of
|
|
67
69
|
stderr). It is not recoverable if lost — mint a new one.
|
|
68
|
-
- \`rooms join <kri1_…>\` folds a funded-wallet chain (
|
|
69
|
-
empty → briefly wait for settlement) and pays a $0.01 testnet
|
|
70
|
-
x402 to
|
|
70
|
+
- \`rooms join <kri1_…>\` folds a funded-wallet chain (wallet → faucet if
|
|
71
|
+
empty → briefly wait for settlement) and pays a $0.01 testnet charge via
|
|
72
|
+
x402 to redeem it — the payment IS the join, so a joiner with no funds
|
|
71
73
|
fails closed rather than joining unpaid. No tier is purchased, no project
|
|
72
74
|
is created. A same-payer replay never pays twice.
|
|
73
75
|
- After a key-form join: the host org becomes this wallet's current org,
|
|
@@ -78,6 +80,14 @@ Room Invite (mint a key from the room you stand in, join through one):
|
|
|
78
80
|
stranger's clone).
|
|
79
81
|
|
|
80
82
|
Notes:
|
|
83
|
+
- A room key is a label: a project id (prj_…) or any label matching
|
|
84
|
+
/^[a-z0-9][a-z0-9._-]{0,63}$/. A project's default room is keyed by the
|
|
85
|
+
project id verbatim, with no prefix.
|
|
86
|
+
- list enumerates the rooms this credential can reach in the org (derived
|
|
87
|
+
from use — a key nobody has written under is not a room), newest activity
|
|
88
|
+
first. get inspects one room WITHOUT joining it (joining registers a
|
|
89
|
+
presence, which changes what you are looking at); an unused key reads as
|
|
90
|
+
empty (live_presences: 0, last_activity_at: null), never 404.
|
|
81
91
|
- join registers this session's presence and returns who else is live, what
|
|
82
92
|
they are working on, and what they have claimed — the arrive-and-look call.
|
|
83
93
|
- A quiet session's presence now resumes automatically across an idle gap
|
|
@@ -94,7 +104,7 @@ Notes:
|
|
|
94
104
|
the output says why. Omit --task and join best-effort fills it from your
|
|
95
105
|
harness's own thread title (Claude Code or Codex) — set
|
|
96
106
|
RUN402_NO_TASK_FROM_TITLE=1 to opt out.
|
|
97
|
-
- leave
|
|
107
|
+
- leave releases THIS session's presence: it stops reading as live and
|
|
98
108
|
its claims stop being held by a live session. Takes no argument — it uses
|
|
99
109
|
the presence this checkout cached when it joined. Pass a \`prs_…\` only to
|
|
100
110
|
release a specific one. Idempotent: a presence already gone (or belonging
|
|
@@ -103,11 +113,6 @@ Notes:
|
|
|
103
113
|
- Presence otherwise expires after ~1h of silence, which is why leaving
|
|
104
114
|
matters: without it a finished session keeps holding its claims for the
|
|
105
115
|
rest of that hour.
|
|
106
|
-
- Enumerating reachable rooms and inspecting one are available on the API
|
|
107
|
-
and in the SDK (\`rooms.list\` / \`rooms.get\`) but NOT yet as CLI
|
|
108
|
-
spellings: \`rooms list\` and \`rooms get\` currently answer with their
|
|
109
|
-
message successors, and a spelling that changes meaning never fails. They
|
|
110
|
-
wait one major.
|
|
111
116
|
- The messages themselves are \`run402 messages\`.
|
|
112
117
|
`;
|
|
113
118
|
|
|
@@ -171,6 +176,62 @@ async function who(args) {
|
|
|
171
176
|
}
|
|
172
177
|
}
|
|
173
178
|
|
|
179
|
+
/**
|
|
180
|
+
* `run402 rooms list` — the rooms this credential can reach in the org
|
|
181
|
+
* (`GET /orgs/v1/:org_id/rooms`), newest activity first. Rooms are derived
|
|
182
|
+
* from use, so a key nobody has written under is not listed.
|
|
183
|
+
*/
|
|
184
|
+
async function list(argv) {
|
|
185
|
+
const args = normalizeArgv(argv);
|
|
186
|
+
const valueFlags = ["--org", "--project"];
|
|
187
|
+
assertKnownFlags(args, [...valueFlags, "--help", "-h"], valueFlags);
|
|
188
|
+
requirePositionalCount(positionalArgs(args, valueFlags), valueFlags, {
|
|
189
|
+
min: 0, max: 0, command: "run402 rooms list [--org <org_id>]",
|
|
190
|
+
});
|
|
191
|
+
// An explicit --org is the whole address for a list; otherwise the org is
|
|
192
|
+
// whatever the room chain (RUN402_ROOM, a binding, the project default)
|
|
193
|
+
// resolves to.
|
|
194
|
+
const explicitOrg = flagValue(args, "--org");
|
|
195
|
+
const room = explicitOrg
|
|
196
|
+
? { orgId: explicitOrg, orgSource: "flag", orgSourceDetail: "--org" }
|
|
197
|
+
: await resolveRoom({ project: flagValue(args, "--project") });
|
|
198
|
+
try {
|
|
199
|
+
const page = await getSdk().rooms.list(room.orgId);
|
|
200
|
+
console.log(JSON.stringify({
|
|
201
|
+
org_id: room.orgId,
|
|
202
|
+
org_source: room.orgSource ?? null,
|
|
203
|
+
org_source_detail: room.orgSourceDetail ?? null,
|
|
204
|
+
...page,
|
|
205
|
+
}, null, 2));
|
|
206
|
+
} catch (err) {
|
|
207
|
+
reportSdkError(err);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* `run402 rooms get <room_key>` — look at one room WITHOUT joining it
|
|
213
|
+
* (`GET /orgs/v1/:org_id/rooms/:room_key`). An unused key reads as empty,
|
|
214
|
+
* never 404.
|
|
215
|
+
*/
|
|
216
|
+
async function get(argv) {
|
|
217
|
+
const args = normalizeArgv(argv);
|
|
218
|
+
const valueFlags = ["--org", "--project"];
|
|
219
|
+
assertKnownFlags(args, [...valueFlags, "--help", "-h"], valueFlags);
|
|
220
|
+
const positionals = positionalArgs(args, valueFlags);
|
|
221
|
+
requirePositionalCount(positionals, valueFlags, {
|
|
222
|
+
min: 1, max: 1, command: "run402 rooms get <room_key> [--org <org_id>]", missing: "Missing required argument: <room_key>.",
|
|
223
|
+
});
|
|
224
|
+
const room = await resolveRoom({
|
|
225
|
+
org: flagValue(args, "--org"), room: positionals[0], project: flagValue(args, "--project"),
|
|
226
|
+
});
|
|
227
|
+
try {
|
|
228
|
+
const summary = await getSdk().rooms.get(room.orgId, room.roomKey);
|
|
229
|
+
console.log(JSON.stringify(summary, null, 2));
|
|
230
|
+
} catch (err) {
|
|
231
|
+
reportSdkError(err);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
174
235
|
async function leave(argv) {
|
|
175
236
|
const args = normalizeArgv(argv);
|
|
176
237
|
assertKnownFlags(args, [...ROOM_FLAGS, "--help", "-h"], ROOM_FLAGS);
|
|
@@ -184,8 +245,8 @@ async function leave(argv) {
|
|
|
184
245
|
project: flagValue(args, "--project"),
|
|
185
246
|
});
|
|
186
247
|
|
|
187
|
-
// No argument is the normal case: a session that is DONE knows which
|
|
188
|
-
// is its own, and asking it to name one would be asking it to look up a
|
|
248
|
+
// No argument is the normal case: a session that is DONE knows which
|
|
249
|
+
// presence is its own, and asking it to name one would be asking it to look up a
|
|
189
250
|
// thing it already told us at join.
|
|
190
251
|
const cached = cachedPresenceId(room.orgId, room.roomKey);
|
|
191
252
|
const presenceId = positionals[0] ?? cached;
|
|
@@ -202,7 +263,7 @@ async function leave(argv) {
|
|
|
202
263
|
const result = await getSdk().rooms.leave(room.orgId, room.roomKey, presenceId);
|
|
203
264
|
// Only forget the cached id when the one we released WAS it. An explicit
|
|
204
265
|
// id that turned out to be someone else's must not evict this session's
|
|
205
|
-
// own
|
|
266
|
+
// own presence from the cache as a side effect.
|
|
206
267
|
if (result.left && presenceId === cached) {
|
|
207
268
|
updateRoomState(room.orgId, room.roomKey, { presence_id: null });
|
|
208
269
|
}
|
|
@@ -257,9 +318,9 @@ async function readRoomInviteNote(a) {
|
|
|
257
318
|
* this checkout stands in: register-or-resume the inviter's OWN presence
|
|
258
319
|
* FIRST (reusing {@link ensurePresence}, the same logic `rooms join`'s
|
|
259
320
|
* no-key form already runs) so the row carries `inviter_presence_id`, mint,
|
|
260
|
-
* post ONE room
|
|
321
|
+
* post ONE room message naming the invite id (never the key), echo the
|
|
261
322
|
* gateway's blast-radius warning to stderr, and print the `kri1_` key to
|
|
262
|
-
* stdout EXACTLY ONCE. A presence or
|
|
323
|
+
* stdout EXACTLY ONCE. A presence or message failure is reported on the result
|
|
263
324
|
* and never voids the mint.
|
|
264
325
|
*/
|
|
265
326
|
async function invite(argv) {
|
|
@@ -305,10 +366,10 @@ async function invite(argv) {
|
|
|
305
366
|
console.error(`invite minted: role ${result.role}, expires ${result.expires_at}, room ${result.room?.room_key ?? room.roomKey}`);
|
|
306
367
|
console.error("recipient runs: run402 rooms join <key printed below>");
|
|
307
368
|
|
|
308
|
-
// design D7: post ONE room
|
|
369
|
+
// design D7: post ONE room message AFTER the mint succeeds — never before
|
|
309
370
|
// (a mint refusal must leave no orphan message), and never naming the
|
|
310
371
|
// key, only the invite id.
|
|
311
|
-
let
|
|
372
|
+
let roomMessage = { posted: false, reason: "inviter presence was not registered" };
|
|
312
373
|
if (inviterPresence) {
|
|
313
374
|
const inviteShort = result.invite_id.slice(0, 8);
|
|
314
375
|
try {
|
|
@@ -317,22 +378,22 @@ async function invite(argv) {
|
|
|
317
378
|
presenceId: inviterPresence.presence_id,
|
|
318
379
|
idempotencyKey: `room-invite:${result.invite_id}:minted`,
|
|
319
380
|
});
|
|
320
|
-
|
|
321
|
-
// The inviter's own
|
|
381
|
+
roomMessage = { posted: true, message_id: sent.message_id, cursor: sent.cursor };
|
|
382
|
+
// The inviter's own message must not wake the inviter's next `messages
|
|
322
383
|
// wait` — advance this checkout's stored cursor past it (best-effort).
|
|
323
384
|
try { updateRoomState(room.orgId, room.roomKey, { cursor: sent.cursor }); } catch { /* never fails a mint */ }
|
|
324
385
|
} catch (e) {
|
|
325
|
-
|
|
386
|
+
roomMessage = { posted: false, reason: e instanceof Error ? e.message : String(e) };
|
|
326
387
|
}
|
|
327
388
|
}
|
|
328
389
|
if (inviterPresenceReport.registered === false) {
|
|
329
|
-
console.error(`note: your own presence was not registered (${inviterPresenceReport.error}) — the invite still mints and
|
|
390
|
+
console.error(`note: your own presence was not registered (${inviterPresenceReport.error}) — the invite still mints and can be redeemed`);
|
|
330
391
|
}
|
|
331
|
-
if (
|
|
332
|
-
console.error(`note: the room
|
|
392
|
+
if (roomMessage.posted === false && inviterPresence) {
|
|
393
|
+
console.error(`note: the room message was not posted (${roomMessage.reason}) — the invite still mints and can still be redeemed`);
|
|
333
394
|
}
|
|
334
395
|
|
|
335
|
-
const finalResult = { ...result, inviter_presence: inviterPresenceReport,
|
|
396
|
+
const finalResult = { ...result, inviter_presence: inviterPresenceReport, room_message: roomMessage };
|
|
336
397
|
if (asJson) {
|
|
337
398
|
printInviteResultJson(finalResult);
|
|
338
399
|
} else {
|
|
@@ -356,8 +417,8 @@ function printInviteResultKeyOnly(result) {
|
|
|
356
417
|
/**
|
|
357
418
|
* `run402 rooms join <kri1_…>` (add-room-invite design D9/D10) — parse the
|
|
358
419
|
* key CLIENT-SIDE first (a wrong-kind vault key refuses by name before ANY
|
|
359
|
-
* network call, including the faucet), fold `ensureFundedWallet` (
|
|
360
|
-
* → faucet-if-empty → brief settlement poll, announced on stderr),
|
|
420
|
+
* network call, including the faucet), fold `ensureFundedWallet` (wallet
|
|
421
|
+
* → faucet-if-empty → brief settlement poll, announced on stderr), redeem
|
|
361
422
|
* through the SDK's paid fetch, then leave arrival state exactly where
|
|
362
423
|
* `run402 messages wait` reads it: the host org as this wallet's current
|
|
363
424
|
* org; the binding written to `.run402.json` outside a git repository, or
|
|
@@ -384,8 +445,8 @@ async function joinWithKey(key, a) {
|
|
|
384
445
|
await ensureFundedWallet((line) => console.error(line));
|
|
385
446
|
const result = await getSdk().rooms.join(key);
|
|
386
447
|
|
|
387
|
-
// Arrival state (design D10) — best-effort throughout: the
|
|
388
|
-
// succeeded, and none of this may fail a completed join.
|
|
448
|
+
// Arrival state (design D10) — best-effort throughout: the redemption
|
|
449
|
+
// already succeeded, and none of this may fail a completed join.
|
|
389
450
|
try {
|
|
390
451
|
const { setSelectedOrgId } = await import("./org-context.mjs");
|
|
391
452
|
setSelectedOrgId(result.org_id);
|
|
@@ -415,7 +476,7 @@ async function joinWithKey(key, a) {
|
|
|
415
476
|
if (typeof result.cursor === "string") {
|
|
416
477
|
try { updateRoomState(result.org_id, result.room.room_key, { cursor: result.cursor }); } catch { /* best-effort */ }
|
|
417
478
|
}
|
|
418
|
-
// Live-proof defect A: cache the presence the
|
|
479
|
+
// Live-proof defect A: cache the presence the REDEMPTION ITSELF registered,
|
|
419
480
|
// exactly the way `registerFreshPresence`'s no-key `join` already does
|
|
420
481
|
// via `rememberPresence` — without this, the joiner's very next
|
|
421
482
|
// coordination call (no cached presence_id) registers a SECOND presence
|
|
@@ -430,20 +491,20 @@ async function joinWithKey(key, a) {
|
|
|
430
491
|
}
|
|
431
492
|
|
|
432
493
|
if (asJson) {
|
|
433
|
-
|
|
494
|
+
printJoinResultJson(result, nextActions);
|
|
434
495
|
} else {
|
|
435
|
-
|
|
496
|
+
renderJoinResultText(result, nextActions);
|
|
436
497
|
}
|
|
437
498
|
} catch (err) {
|
|
438
499
|
reportSdkError(err);
|
|
439
500
|
}
|
|
440
501
|
}
|
|
441
502
|
|
|
442
|
-
function
|
|
503
|
+
function printJoinResultJson(result, nextActions) {
|
|
443
504
|
console.log(JSON.stringify({ ...result, next_actions: nextActions }, null, 2));
|
|
444
505
|
}
|
|
445
506
|
|
|
446
|
-
function
|
|
507
|
+
function renderJoinResultText(result, nextActions) {
|
|
447
508
|
// The note is plain text, not a structured schema — printed verbatim as
|
|
448
509
|
// Markdown (it may already contain Markdown formatting the inviter wrote).
|
|
449
510
|
if (result.note) {
|
|
@@ -452,9 +513,9 @@ function renderClaimResultText(result, nextActions) {
|
|
|
452
513
|
}
|
|
453
514
|
console.error(`joined org ${result.org_id}, room ${result.room.room_key} — role ${result.membership.role}`);
|
|
454
515
|
if (result.deduplicated) {
|
|
455
|
-
console.error("note: this key was already
|
|
516
|
+
console.error("note: this key was already redeemed by this same payer — no second payment was made (safe replay)");
|
|
456
517
|
}
|
|
457
|
-
console.error(`
|
|
518
|
+
console.error(`charge: $${(result.seat.amount_usd_micros / 1_000_000).toFixed(2)} on ${result.seat.network}${result.seat.charge_id ? ` (charge ${result.seat.charge_id})` : ""}`);
|
|
458
519
|
if (result.inviter) {
|
|
459
520
|
const labels = [result.inviter.program, result.inviter.model].filter(Boolean).join("/");
|
|
460
521
|
const liveness = result.inviter.state === "active" ? "live" : result.inviter.state;
|
|
@@ -485,7 +546,7 @@ export async function run(sub, args) {
|
|
|
485
546
|
switch (sub) {
|
|
486
547
|
case "join": {
|
|
487
548
|
// `run402 rooms join <kri1_…>` (add-room-invite design D9): a
|
|
488
|
-
// positional key form
|
|
549
|
+
// positional key form redeems the key and arrives; no positional keeps
|
|
489
550
|
// the existing arrive-and-look behavior unchanged.
|
|
490
551
|
const a = normalizeArgv(argv);
|
|
491
552
|
const positionals = positionalArgs(a, ["--name", "--task", ...ROOM_FLAGS]);
|
|
@@ -506,39 +567,14 @@ export async function run(sub, args) {
|
|
|
506
567
|
await leave(argv);
|
|
507
568
|
break;
|
|
508
569
|
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
// them as `rooms.list` / `rooms.get`. These two SPELLINGS are reserved —
|
|
512
|
-
// a freed spelling stays dead for one major before anything reuses it.
|
|
513
|
-
// Reissuing them now with room semantics would never fail — an agent
|
|
514
|
-
// holding `rooms list` would get a successful response containing
|
|
515
|
-
// different data and nothing would tell it the world moved.
|
|
516
|
-
// Retired here, and NOT aliased: each answers with its successor so one
|
|
517
|
-
// failed call teaches the new model, where an alias would teach the old
|
|
518
|
-
// one forever.
|
|
519
|
-
case "who":
|
|
520
|
-
fail({
|
|
521
|
-
code: "COMMAND_REMOVED",
|
|
522
|
-
message: "`run402 rooms who` was renamed to `run402 rooms join`.",
|
|
523
|
-
hint: "run402 rooms join --name <name> --task <text>",
|
|
524
|
-
details: { was: "rooms who", now: "rooms join", why: "an interrogative must not name a write — it registers a presence" },
|
|
525
|
-
});
|
|
570
|
+
case "list": {
|
|
571
|
+
await list(argv);
|
|
526
572
|
break;
|
|
527
|
-
|
|
528
|
-
case "get":
|
|
529
|
-
|
|
530
|
-
case "ack":
|
|
531
|
-
fail({
|
|
532
|
-
code: "COMMAND_REMOVED",
|
|
533
|
-
message: `\`run402 rooms ${sub}\` moved to \`run402 messages ${sub}\`.`,
|
|
534
|
-
hint: `run402 messages ${sub}`,
|
|
535
|
-
details: {
|
|
536
|
-
was: `rooms ${sub}`,
|
|
537
|
-
now: `messages ${sub}`,
|
|
538
|
-
why: "the verb acts on a message, not on the room that contains it",
|
|
539
|
-
},
|
|
540
|
-
});
|
|
573
|
+
}
|
|
574
|
+
case "get": {
|
|
575
|
+
await get(argv);
|
|
541
576
|
break;
|
|
577
|
+
}
|
|
542
578
|
default:
|
|
543
579
|
failUnknownSubcommand("rooms", sub, {
|
|
544
580
|
hint: "Run `run402 rooms --help` for usage.",
|
package/lib/sdk-errors.mjs
CHANGED
|
@@ -73,8 +73,8 @@ const ROOM_INVITE_TERMINAL_REFUSAL_CODES = new Set([
|
|
|
73
73
|
"ROOM_INVITE_KEY_INVALID",
|
|
74
74
|
"ROOM_INVITE_KEY_EXPIRED",
|
|
75
75
|
"ROOM_INVITE_KEY_REVOKED",
|
|
76
|
-
"
|
|
77
|
-
"
|
|
76
|
+
"ROOM_INVITE_KEY_ALREADY_REDEEMED",
|
|
77
|
+
"ROOM_INVITE_REDEEM_REQUIRES_WALLET",
|
|
78
78
|
]);
|
|
79
79
|
|
|
80
80
|
export function reportSdkError(err) {
|
|
@@ -148,6 +148,11 @@ export function reportSdkError(err) {
|
|
|
148
148
|
"Returned as 403 even when the project does not exist, so verify the project id too.";
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
// A `device`-grade sign-in session (`run402 login --device`) is read-only.
|
|
152
|
+
if (payload.code === "SESSION_READ_ONLY" && payload.hint === undefined) {
|
|
153
|
+
payload.hint = "This sign-in session came from `run402 login --device` and can only read. Run `run402 login` (the browser passkey sign-in) to write.";
|
|
154
|
+
}
|
|
155
|
+
|
|
151
156
|
if (typeof payload.code === "string" && payload.code.startsWith("PROJECT_CREDENTIAL_") && payload.hint === undefined) {
|
|
152
157
|
if (payload.code === "PROJECT_CREDENTIAL_PROJECT_MISMATCH") {
|
|
153
158
|
payload.hint =
|
|
@@ -158,7 +163,7 @@ export function reportSdkError(err) {
|
|
|
158
163
|
}
|
|
159
164
|
}
|
|
160
165
|
|
|
161
|
-
// Live-proof defect B: a terminal room-invite
|
|
166
|
+
// Live-proof defect B: a terminal room-invite redeem refusal is the
|
|
162
167
|
// gateway's OWN typed envelope by the time it reaches here (the SDK's
|
|
163
168
|
// default paid fetch classifies these five codes as `"failed"`, never
|
|
164
169
|
// `"ambiguous"` — see `sdk/src/node/paid-fetch.ts` and
|
|
@@ -166,7 +171,7 @@ export function reportSdkError(err) {
|
|
|
166
171
|
// no payment was charged — is worth stating explicitly rather than
|
|
167
172
|
// leaving the caller to infer it from `mutation_state`.
|
|
168
173
|
if (ROOM_INVITE_TERMINAL_REFUSAL_CODES.has(payload.code) && payload.hint === undefined) {
|
|
169
|
-
payload.hint = "This key
|
|
174
|
+
payload.hint = "This key could not be redeemed (already used, expired, revoked, or this route requires a wallet, not a session) — you were not charged; no payment was made for this attempt.";
|
|
170
175
|
}
|
|
171
176
|
|
|
172
177
|
if (Array.isArray(payload.unacknowledged_warning_codes)) {
|
package/lib/sdk.mjs
CHANGED
|
@@ -8,14 +8,14 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { run402 } from "#sdk/node";
|
|
11
|
-
import {
|
|
11
|
+
import { walletFile, profileStateFile, projectCredentialsFile } from "./config.mjs";
|
|
12
12
|
|
|
13
13
|
export function getSdk(opts = {}) {
|
|
14
14
|
// surface: "cli" opts the default credential resolution into `auto` — wallet
|
|
15
|
-
// if present, else the
|
|
15
|
+
// if present, else the sign-in session + a matched write approval.
|
|
16
16
|
return run402({
|
|
17
17
|
surface: "cli",
|
|
18
|
-
|
|
18
|
+
walletPath: walletFile(),
|
|
19
19
|
keystorePath: projectCredentialsFile(),
|
|
20
20
|
profileStatePath: profileStateFile(),
|
|
21
21
|
...opts,
|
package/lib/service.mjs
CHANGED
|
@@ -9,8 +9,8 @@ Usage:
|
|
|
9
9
|
run402 service health Liveness check (per-dependency status + version)
|
|
10
10
|
|
|
11
11
|
Notes:
|
|
12
|
-
- Both endpoints are unauthenticated and free. No
|
|
13
|
-
- This is the Run402 SERVICE status. For your ACCOUNT status (
|
|
12
|
+
- Both endpoints are unauthenticated and free. No local wallet required.
|
|
13
|
+
- This is the Run402 SERVICE status. For your ACCOUNT status (wallet,
|
|
14
14
|
balance, tier, projects), use 'run402 status'.
|
|
15
15
|
`;
|
|
16
16
|
|
|
@@ -21,9 +21,9 @@ Usage:
|
|
|
21
21
|
run402 service status
|
|
22
22
|
|
|
23
23
|
Notes:
|
|
24
|
-
- Unauthenticated and free; no
|
|
24
|
+
- Unauthenticated and free; no local wallet required
|
|
25
25
|
- Returns uptime, supported capabilities, operator, and deployment info
|
|
26
|
-
- For organization state (
|
|
26
|
+
- For organization state (wallet, allowance, tier, projects), use
|
|
27
27
|
'run402 status' instead
|
|
28
28
|
|
|
29
29
|
Examples:
|
|
@@ -35,7 +35,7 @@ Usage:
|
|
|
35
35
|
run402 service health
|
|
36
36
|
|
|
37
37
|
Notes:
|
|
38
|
-
- Unauthenticated and free; no
|
|
38
|
+
- Unauthenticated and free; no local wallet required
|
|
39
39
|
- Returns per-dependency status and the deployed version
|
|
40
40
|
|
|
41
41
|
Examples:
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* one-passkey-per-person: the two proofs a wallet-authenticated notification
|
|
3
|
+
* mutation carries when the person behind this wallet is signed in.
|
|
4
|
+
*
|
|
5
|
+
* `run402 login` caches a passkey-fresh sign-in session. The gateway accepts
|
|
6
|
+
* that session — sent as `Authorization: Bearer` beside the wallet's
|
|
7
|
+
* `SIGN-IN-WITH-X` on the SAME request — as passkey assurance for a wallet
|
|
8
|
+
* contact whose verified email the session's principal holds. So a person who
|
|
9
|
+
* signed in once is never asked to enroll a second passkey. With no live cached
|
|
10
|
+
* session this returns `undefined` and the verb uses wallet auth alone.
|
|
11
|
+
*/
|
|
12
|
+
import { walletAuthHeaders } from "./config.mjs";
|
|
13
|
+
import { loadLiveControlPlaneSession } from "../core-dist/control-plane-session.js";
|
|
14
|
+
|
|
15
|
+
/** `{ siwx, token }` for `path`, or `undefined` when no live sign-in session is cached. */
|
|
16
|
+
export function sessionProofs(path) {
|
|
17
|
+
const siwx = walletAuthHeaders(path)["SIGN-IN-WITH-X"];
|
|
18
|
+
const live = loadLiveControlPlaneSession();
|
|
19
|
+
if (!live || !live.control_plane_session_token) return undefined;
|
|
20
|
+
return { siwx, token: live.control_plane_session_token };
|
|
21
|
+
}
|