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.
Files changed (259) hide show
  1. package/README.md +43 -41
  2. package/cli.mjs +34 -29
  3. package/core-dist/config.js +52 -18
  4. package/core-dist/control-plane-session.js +31 -6
  5. package/core-dist/keystore.js +7 -7
  6. package/core-dist/profiles.js +9 -7
  7. package/core-dist/{allowance-auth.js → wallet-auth.js} +15 -15
  8. package/{sdk/core-dist/allowance.js → core-dist/wallet.js} +20 -20
  9. package/{sdk/core-dist/write-auth-session.js → core-dist/write-approvals.js} +19 -19
  10. package/gitvault-surface.json +1 -1
  11. package/lib/admin.mjs +1 -1
  12. package/lib/agent.mjs +20 -18
  13. package/lib/approve.mjs +81 -0
  14. package/lib/apps.mjs +3 -3
  15. package/lib/assets.mjs +2 -2
  16. package/lib/billing.mjs +6 -6
  17. package/lib/buzz-doctor.mjs +1 -1
  18. package/lib/buzz-notifications.mjs +1 -1
  19. package/lib/buzz.mjs +1 -1
  20. package/lib/buzz.test.mjs +1 -1
  21. package/lib/ci.mjs +4 -4
  22. package/lib/cold-start.mjs +21 -21
  23. package/lib/command-manifest.mjs +20 -34
  24. package/lib/config.mjs +24 -24
  25. package/lib/contacts.mjs +13 -13
  26. package/lib/contracts.mjs +1 -1
  27. package/lib/credentials.mjs +2 -2
  28. package/lib/daemon-path.mjs +1 -1
  29. package/lib/delegates.mjs +1 -1
  30. package/lib/deliveries.mjs +4 -4
  31. package/lib/deploy-rehearse-discovery.test.mjs +7 -7
  32. package/lib/deploy-v2.mjs +232 -184
  33. package/lib/deploy.mjs +21 -53
  34. package/lib/doctor-source-scan.mjs +1 -1
  35. package/lib/doctor.mjs +57 -57
  36. package/lib/escalations.mjs +1 -1
  37. package/lib/feedback.mjs +10 -10
  38. package/lib/gitvault-daemon.mjs +1 -1
  39. package/lib/image.mjs +3 -3
  40. package/lib/init.mjs +66 -66
  41. package/lib/lightning-wallet.mjs +28 -28
  42. package/lib/login.mjs +207 -0
  43. package/lib/logout.mjs +53 -0
  44. package/lib/next-actions.mjs +15 -15
  45. package/lib/org.mjs +100 -13
  46. package/lib/pay.mjs +1 -1
  47. package/lib/projects.mjs +26 -29
  48. package/lib/redeem.mjs +7 -8
  49. package/lib/remote-helper-session.mjs +12 -12
  50. package/lib/repos.mjs +62 -62
  51. package/lib/rooms.mjs +103 -67
  52. package/lib/sdk-errors.mjs +9 -4
  53. package/lib/sdk.mjs +3 -3
  54. package/lib/service.mjs +5 -5
  55. package/lib/session-proofs.mjs +21 -0
  56. package/lib/sign-in.mjs +296 -0
  57. package/lib/sites.mjs +8 -8
  58. package/lib/stats.mjs +2 -2
  59. package/lib/status.mjs +25 -25
  60. package/lib/subdomains.mjs +19 -19
  61. package/lib/subscriptions.mjs +8 -8
  62. package/lib/tier.mjs +29 -22
  63. package/lib/transfer.mjs +58 -85
  64. package/lib/up.mjs +7 -7
  65. package/lib/wallet-context.mjs +3 -3
  66. package/lib/wallet-context.test.mjs +3 -3
  67. package/lib/wallets.mjs +196 -31
  68. package/lib/webhook-secret.mjs +6 -6
  69. package/lib/whoami.mjs +75 -0
  70. package/package.json +1 -1
  71. package/sdk/core-dist/config.js +52 -18
  72. package/sdk/core-dist/control-plane-session.js +31 -6
  73. package/sdk/core-dist/keystore.js +7 -7
  74. package/sdk/core-dist/profiles.js +9 -7
  75. package/sdk/core-dist/{allowance-auth.js → wallet-auth.js} +15 -15
  76. package/{core-dist/allowance.js → sdk/core-dist/wallet.js} +20 -20
  77. package/{core-dist/write-auth-session.js → sdk/core-dist/write-approvals.js} +19 -19
  78. package/sdk/dist/actions.d.ts +1 -1
  79. package/sdk/dist/actions.d.ts.map +1 -1
  80. package/sdk/dist/control-plane-credentials.d.ts +8 -8
  81. package/sdk/dist/control-plane-credentials.js +7 -7
  82. package/sdk/dist/credentials.d.ts +19 -19
  83. package/sdk/dist/credentials.d.ts.map +1 -1
  84. package/sdk/dist/credentials.js +1 -1
  85. package/sdk/dist/errors.d.ts +24 -26
  86. package/sdk/dist/errors.d.ts.map +1 -1
  87. package/sdk/dist/errors.js +34 -37
  88. package/sdk/dist/errors.js.map +1 -1
  89. package/sdk/dist/index.d.ts +29 -16
  90. package/sdk/dist/index.d.ts.map +1 -1
  91. package/sdk/dist/index.js +29 -16
  92. package/sdk/dist/index.js.map +1 -1
  93. package/sdk/dist/kernel.d.ts +1 -1
  94. package/sdk/dist/kernel.d.ts.map +1 -1
  95. package/sdk/dist/kernel.js +10 -9
  96. package/sdk/dist/kernel.js.map +1 -1
  97. package/sdk/dist/namespaces/admin.d.ts +51 -120
  98. package/sdk/dist/namespaces/admin.d.ts.map +1 -1
  99. package/sdk/dist/namespaces/admin.js +41 -45
  100. package/sdk/dist/namespaces/admin.js.map +1 -1
  101. package/sdk/dist/namespaces/agent.d.ts +2 -2
  102. package/sdk/dist/namespaces/agent.d.ts.map +1 -1
  103. package/sdk/dist/namespaces/agent.js +2 -2
  104. package/sdk/dist/namespaces/agent.js.map +1 -1
  105. package/sdk/dist/namespaces/ai.d.ts +1 -1
  106. package/sdk/dist/namespaces/ai.js +1 -1
  107. package/sdk/dist/namespaces/billing.d.ts +6 -3
  108. package/sdk/dist/namespaces/billing.d.ts.map +1 -1
  109. package/sdk/dist/namespaces/billing.js.map +1 -1
  110. package/sdk/dist/namespaces/buzz-notifications.types.d.ts +1 -1
  111. package/sdk/dist/namespaces/buzz.d.ts +2 -2
  112. package/sdk/dist/namespaces/buzz.d.ts.map +1 -1
  113. package/sdk/dist/namespaces/buzz.js +2 -2
  114. package/sdk/dist/namespaces/buzz.js.map +1 -1
  115. package/sdk/dist/namespaces/buzz.types.d.ts +2 -2
  116. package/sdk/dist/namespaces/buzz.types.d.ts.map +1 -1
  117. package/sdk/dist/namespaces/credentials.types.d.ts +1 -1
  118. package/sdk/dist/namespaces/deploy.d.ts +1 -1
  119. package/sdk/dist/namespaces/deploy.js +5 -5
  120. package/sdk/dist/namespaces/deploy.js.map +1 -1
  121. package/sdk/dist/namespaces/deploy.types.d.ts +4 -4
  122. package/sdk/dist/namespaces/escalations.d.ts +1 -1
  123. package/sdk/dist/namespaces/escalations.js +1 -1
  124. package/sdk/dist/namespaces/escalations.types.d.ts +2 -2
  125. package/sdk/dist/namespaces/escalations.types.d.ts.map +1 -1
  126. package/sdk/dist/namespaces/events.types.d.ts +1 -1
  127. package/sdk/dist/namespaces/functions.d.ts +1 -1
  128. package/sdk/dist/namespaces/functions.js +1 -1
  129. package/sdk/dist/namespaces/gitvault.crypto.d.ts +1 -1
  130. package/sdk/dist/namespaces/gitvault.d.ts +24 -24
  131. package/sdk/dist/namespaces/gitvault.d.ts.map +1 -1
  132. package/sdk/dist/namespaces/gitvault.js +109 -109
  133. package/sdk/dist/namespaces/gitvault.js.map +1 -1
  134. package/sdk/dist/namespaces/me.d.ts +120 -0
  135. package/sdk/dist/namespaces/me.d.ts.map +1 -0
  136. package/sdk/dist/namespaces/me.js +34 -0
  137. package/sdk/dist/namespaces/me.js.map +1 -0
  138. package/sdk/dist/namespaces/org.d.ts +108 -6
  139. package/sdk/dist/namespaces/org.d.ts.map +1 -1
  140. package/sdk/dist/namespaces/org.js +63 -7
  141. package/sdk/dist/namespaces/org.js.map +1 -1
  142. package/sdk/dist/namespaces/org.types.d.ts +29 -9
  143. package/sdk/dist/namespaces/org.types.d.ts.map +1 -1
  144. package/sdk/dist/namespaces/pay.js +3 -3
  145. package/sdk/dist/namespaces/pay.js.map +1 -1
  146. package/sdk/dist/namespaces/projects.d.ts +9 -9
  147. package/sdk/dist/namespaces/projects.js +15 -15
  148. package/sdk/dist/namespaces/projects.js.map +1 -1
  149. package/sdk/dist/namespaces/projects.types.d.ts +28 -12
  150. package/sdk/dist/namespaces/projects.types.d.ts.map +1 -1
  151. package/sdk/dist/namespaces/rooms.d.ts +7 -7
  152. package/sdk/dist/namespaces/rooms.js +17 -17
  153. package/sdk/dist/namespaces/rooms.js.map +1 -1
  154. package/sdk/dist/namespaces/rooms.types.d.ts +9 -9
  155. package/sdk/dist/namespaces/rooms.types.d.ts.map +1 -1
  156. package/sdk/dist/namespaces/service.d.ts +1 -1
  157. package/sdk/dist/namespaces/service.js +1 -1
  158. package/sdk/dist/namespaces/{operator-session.d.ts → session.d.ts} +135 -51
  159. package/sdk/dist/namespaces/session.d.ts.map +1 -0
  160. package/sdk/dist/namespaces/{operator-session.js → session.js} +118 -30
  161. package/sdk/dist/namespaces/session.js.map +1 -0
  162. package/sdk/dist/namespaces/subdomains.d.ts +5 -5
  163. package/sdk/dist/namespaces/subdomains.d.ts.map +1 -1
  164. package/sdk/dist/namespaces/subdomains.js +6 -6
  165. package/sdk/dist/namespaces/subdomains.js.map +1 -1
  166. package/sdk/dist/namespaces/tier.d.ts +43 -20
  167. package/sdk/dist/namespaces/tier.d.ts.map +1 -1
  168. package/sdk/dist/namespaces/tier.js +7 -7
  169. package/sdk/dist/namespaces/tier.js.map +1 -1
  170. package/sdk/dist/namespaces/transfers.d.ts +74 -71
  171. package/sdk/dist/namespaces/transfers.d.ts.map +1 -1
  172. package/sdk/dist/namespaces/transfers.js +31 -38
  173. package/sdk/dist/namespaces/transfers.js.map +1 -1
  174. package/sdk/dist/namespaces/vouchers.d.ts +9 -10
  175. package/sdk/dist/namespaces/vouchers.d.ts.map +1 -1
  176. package/sdk/dist/namespaces/vouchers.js +4 -5
  177. package/sdk/dist/namespaces/vouchers.js.map +1 -1
  178. package/sdk/dist/namespaces/wallets.d.ts +72 -3
  179. package/sdk/dist/namespaces/wallets.d.ts.map +1 -1
  180. package/sdk/dist/namespaces/wallets.js +141 -3
  181. package/sdk/dist/namespaces/wallets.js.map +1 -1
  182. package/sdk/dist/namespaces/write-approval.d.ts +101 -0
  183. package/sdk/dist/namespaces/write-approval.d.ts.map +1 -0
  184. package/sdk/dist/namespaces/write-approval.js +62 -0
  185. package/sdk/dist/namespaces/write-approval.js.map +1 -0
  186. package/sdk/dist/node/actions-node.d.ts +3 -3
  187. package/sdk/dist/node/actions-node.d.ts.map +1 -1
  188. package/sdk/dist/node/actions-node.js +36 -36
  189. package/sdk/dist/node/actions-node.js.map +1 -1
  190. package/sdk/dist/node/{bearer-claim-key.d.ts → bearer-redeem-key.d.ts} +12 -12
  191. package/sdk/dist/node/bearer-redeem-key.d.ts.map +1 -0
  192. package/sdk/dist/node/{bearer-claim-key.js → bearer-redeem-key.js} +21 -21
  193. package/sdk/dist/node/bearer-redeem-key.js.map +1 -0
  194. package/sdk/dist/node/ci.d.ts +1 -1
  195. package/sdk/dist/node/ci.d.ts.map +1 -1
  196. package/sdk/dist/node/ci.js +6 -6
  197. package/sdk/dist/node/ci.js.map +1 -1
  198. package/sdk/dist/node/credentials.d.ts +17 -16
  199. package/sdk/dist/node/credentials.d.ts.map +1 -1
  200. package/sdk/dist/node/credentials.js +28 -27
  201. package/sdk/dist/node/credentials.js.map +1 -1
  202. package/sdk/dist/node/funding-recovery.d.ts +1 -1
  203. package/sdk/dist/node/funding-recovery.d.ts.map +1 -1
  204. package/sdk/dist/node/funding-recovery.js +1 -1
  205. package/sdk/dist/node/funding-recovery.js.map +1 -1
  206. package/sdk/dist/node/gitvault-apply.d.ts +1 -1
  207. package/sdk/dist/node/gitvault-apply.d.ts.map +1 -1
  208. package/sdk/dist/node/gitvault-handoff.d.ts +22 -22
  209. package/sdk/dist/node/gitvault-handoff.d.ts.map +1 -1
  210. package/sdk/dist/node/gitvault-handoff.js +45 -45
  211. package/sdk/dist/node/gitvault-handoff.js.map +1 -1
  212. package/sdk/dist/node/gitvault-prewarm.js +7 -7
  213. package/sdk/dist/node/gitvault-prewarm.js.map +1 -1
  214. package/sdk/dist/node/gitvault-restore.d.ts +1 -1
  215. package/sdk/dist/node/gitvault-restore.js +1 -1
  216. package/sdk/dist/node/index.d.ts +16 -16
  217. package/sdk/dist/node/index.d.ts.map +1 -1
  218. package/sdk/dist/node/index.js +15 -15
  219. package/sdk/dist/node/index.js.map +1 -1
  220. package/sdk/dist/node/org-adopt.d.ts +49 -0
  221. package/sdk/dist/node/org-adopt.d.ts.map +1 -0
  222. package/sdk/dist/node/org-adopt.js +77 -0
  223. package/sdk/dist/node/org-adopt.js.map +1 -0
  224. package/sdk/dist/node/paid-fetch.d.ts +7 -7
  225. package/sdk/dist/node/paid-fetch.d.ts.map +1 -1
  226. package/sdk/dist/node/paid-fetch.js +54 -54
  227. package/sdk/dist/node/paid-fetch.js.map +1 -1
  228. package/sdk/dist/node/preflight.d.ts +1 -1
  229. package/sdk/dist/node/preflight.d.ts.map +1 -1
  230. package/sdk/dist/node/preflight.js +1 -1
  231. package/sdk/dist/node/preflight.js.map +1 -1
  232. package/sdk/dist/node/source-scan.d.ts +1 -1
  233. package/sdk/dist/node/source-scan.js +2 -2
  234. package/sdk/dist/node/source-scan.js.map +1 -1
  235. package/sdk/dist/scoped.d.ts +3 -3
  236. package/sdk/dist/scoped.d.ts.map +1 -1
  237. package/sdk/dist/scoped.js +2 -2
  238. package/sdk/dist/scoped.js.map +1 -1
  239. package/core-dist/operator-session.js +0 -154
  240. package/lib/allowance.mjs +0 -355
  241. package/lib/operator-proofs.mjs +0 -22
  242. package/lib/operator.mjs +0 -771
  243. package/sdk/core-dist/operator-session.js +0 -154
  244. package/sdk/dist/namespaces/allowance.d.ts +0 -79
  245. package/sdk/dist/namespaces/allowance.d.ts.map +0 -1
  246. package/sdk/dist/namespaces/allowance.js +0 -149
  247. package/sdk/dist/namespaces/allowance.js.map +0 -1
  248. package/sdk/dist/namespaces/operator-session.d.ts.map +0 -1
  249. package/sdk/dist/namespaces/operator-session.js.map +0 -1
  250. package/sdk/dist/namespaces/operator.d.ts +0 -359
  251. package/sdk/dist/namespaces/operator.d.ts.map +0 -1
  252. package/sdk/dist/namespaces/operator.js +0 -272
  253. package/sdk/dist/namespaces/operator.js.map +0 -1
  254. package/sdk/dist/node/bearer-claim-key.d.ts.map +0 -1
  255. package/sdk/dist/node/bearer-claim-key.js.map +0 -1
  256. package/sdk/dist/node/operator-claim.d.ts +0 -49
  257. package/sdk/dist/node/operator-claim.d.ts.map +0 -1
  258. package/sdk/dist/node/operator-claim.js +0 -77
  259. 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 claims it
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 (allowance → faucet if
69
- empty → briefly wait for settlement) and pays a $0.01 testnet seat via
70
- x402 to claim it — the payment IS the join, so a joiner with no funds
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 gives up THIS session's seat: its presence stops reading as live and
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 seat
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 seat from the cache as a side effect.
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 fact naming the invite id (never the key), echo the
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 fact failure is reported on the result
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 fact AFTER the mint succeeds — never before
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 roomFact = { posted: false, reason: "inviter presence was not registered" };
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
- roomFact = { posted: true, message_id: sent.message_id, cursor: sent.cursor };
321
- // The inviter's own fact must not wake the inviter's next `messages
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
- roomFact = { posted: false, reason: e instanceof Error ? e.message : String(e) };
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 is claimable`);
390
+ console.error(`note: your own presence was not registered (${inviterPresenceReport.error}) — the invite still mints and can be redeemed`);
330
391
  }
331
- if (roomFact.posted === false && inviterPresence) {
332
- console.error(`note: the room fact was not posted (${roomFact.reason}) — the invite still mints and remains claimable`);
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, room_fact: roomFact };
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` (allowance
360
- * → faucet-if-empty → brief settlement poll, announced on stderr), claim
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 claim already
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 CLAIM ITSELF registered,
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
- printClaimResultJson(result, nextActions);
494
+ printJoinResultJson(result, nextActions);
434
495
  } else {
435
- renderClaimResultText(result, nextActions);
496
+ renderJoinResultText(result, nextActions);
436
497
  }
437
498
  } catch (err) {
438
499
  reportSdkError(err);
439
500
  }
440
501
  }
441
502
 
442
- function printClaimResultJson(result, nextActions) {
503
+ function printJoinResultJson(result, nextActions) {
443
504
  console.log(JSON.stringify({ ...result, next_actions: nextActions }, null, 2));
444
505
  }
445
506
 
446
- function renderClaimResultText(result, nextActions) {
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 claimed by this same payer — no second payment was made (safe replay)");
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(`seat: $${(result.seat.amount_usd_micros / 1_000_000).toFixed(2)} on ${result.seat.network}${result.seat.charge_id ? ` (charge ${result.seat.charge_id})` : ""}`);
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 claims a seat and arrives; no positional keeps
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
- // `list` and `get` are NOT here, and this is deliberate rather than
510
- // missing: the ROUTES exist (agent-room-lifecycle) and the SDK exposes
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
- case "list":
528
- case "get":
529
- case "send":
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.",
@@ -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
- "ROOM_INVITE_KEY_ALREADY_CLAIMED",
77
- "ROOM_INVITE_CLAIM_REQUIRES_WALLET",
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 claim refusal is the
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 was not claimable (already used, expired, revoked, or this route requires a wallet, not a session) — you were not charged; no payment was made for this attempt.";
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 { allowanceFile, profileStateFile, projectCredentialsFile } from "./config.mjs";
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 operator (control-plane) session + matched approval.
15
+ // if present, else the sign-in session + a matched write approval.
16
16
  return run402({
17
17
  surface: "cli",
18
- allowancePath: allowanceFile(),
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 allowance required.
13
- - This is the Run402 SERVICE status. For your ACCOUNT status (allowance,
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 allowance required
24
+ - Unauthenticated and free; no local wallet required
25
25
  - Returns uptime, supported capabilities, operator, and deployment info
26
- - For organization state (allowance, balance, tier, projects), use
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 allowance required
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
+ }