run402 4.93.1 → 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/login.mjs ADDED
@@ -0,0 +1,207 @@
1
+ /**
2
+ * `run402 login [--device] [--no-open]` — sign a person in. See sign-in.mjs.
3
+ */
4
+
5
+ import { setTimeout as sleep } from "node:timers/promises";
6
+ import { fail, reportSdkError } from "./sdk-errors.mjs";
7
+ import { getSdk } from "./sdk.mjs";
8
+ import { normalizeArgv, hasHelp, assertKnownFlags, positionalArgs } from "./argparse.mjs";
9
+ import {
10
+ saveControlPlaneSession,
11
+ controlPlaneSessionFromTokenResponse,
12
+ clearRetiredSessionCaches,
13
+ } from "../core-dist/control-plane-session.js";
14
+ import { clearApprovals } from "../core-dist/write-approvals.js";
15
+ import { openBrowser, pkce, sessionView, startLoopbackServer } from "./sign-in.mjs";
16
+
17
+ const HELP = `run402 login — sign in as a person (your sign-in session)
18
+
19
+ Usage:
20
+ run402 login [--no-open] browser passkey sign-in (full session)
21
+ run402 login --device [--no-open] device code for a terminal with no browser (read-only)
22
+
23
+ A person signs in; an agent's wallet is a separate identity ('run402 status').
24
+ 'run402 login' opens the browser, where you sign in with your passkey, and
25
+ catches the redirect on a 127.0.0.1 port. The session it caches can write,
26
+ and step-up works on it. 'run402 login --device' prints a code you approve in
27
+ the console from any device; that session can only read (every write answers
28
+ SESSION_READ_ONLY).
29
+
30
+ Provisioning, deploying, and writing secrets without a wallet also need a
31
+ write approval: 'run402 approve --action <capability> (--org <org_id> |
32
+ --project <project_id>)'. From a terminal the CLI opens it for you.
33
+
34
+ Options:
35
+ --device Use the device-code flow (RFC 8628) instead of the browser redirect.
36
+ --no-open Do not open the browser; print the URL only.
37
+
38
+ Notes:
39
+ - The session is cached at the base config dir, shared across named wallets.
40
+ - JSON to stdout; the URL and code to open go to stderr.
41
+ - 'run402 whoami' shows the session's grade; 'run402 logout' ends it.
42
+ `;
43
+
44
+ const CLIENT_NAME = "run402 CLI";
45
+
46
+ /** Cache a freshly minted session; a new session invalidates every approval bound to the old one. */
47
+ function cacheSession(tokenResponse) {
48
+ const cached = controlPlaneSessionFromTokenResponse(tokenResponse);
49
+ clearApprovals();
50
+ clearRetiredSessionCaches();
51
+ saveControlPlaneSession(cached);
52
+ return cached;
53
+ }
54
+
55
+ /** Best-effort: surface org memberships (auto-redeemed invites land here). Never fails the login. */
56
+ async function attachMemberships(view, token) {
57
+ try {
58
+ const who = await getSdk().session.whoami({ token });
59
+ const memberships = Array.isArray(who?.memberships) ? who.memberships : [];
60
+ view.memberships = memberships;
61
+ if (memberships.length) {
62
+ process.stderr.write(
63
+ `Member of ${memberships.length} org(s):\n` +
64
+ memberships.map((m) => ` - ${m.display_name || m.org_id || "unknown"} (${m.role}, ${m.status})`).join("\n") +
65
+ "\n",
66
+ );
67
+ }
68
+ } catch {
69
+ /* best-effort — the session is valid regardless of the whoami result */
70
+ }
71
+ }
72
+
73
+ async function loopbackLogin({ noOpen }) {
74
+ const sdk = getSdk();
75
+ const { codeVerifier, codeChallenge, state, nonce } = pkce();
76
+ const { ready, codePromise, close } = startLoopbackServer({ expectedState: state, timeoutMs: 300_000 });
77
+
78
+ let port;
79
+ try {
80
+ port = await ready;
81
+ } catch (err) {
82
+ close();
83
+ return fail({ code: "LOOPBACK_FAILED", message: `Could not start the loopback server: ${err.message}` });
84
+ }
85
+ const redirectUri = `http://127.0.0.1:${port}/callback`;
86
+ const authorizeUrl = sdk.session.buildCliAuthorizeUrl({ redirectUri, codeChallenge, state, nonce });
87
+
88
+ process.stderr.write(`\nTo sign in, open:\n ${authorizeUrl}\n\n`);
89
+ if (!noOpen && process.stderr.isTTY) {
90
+ openBrowser(authorizeUrl);
91
+ process.stderr.write("(opening your browser…)\n\n");
92
+ }
93
+ process.stderr.write("Waiting for approval…\n");
94
+
95
+ let code;
96
+ try {
97
+ code = await codePromise;
98
+ } catch (err) {
99
+ close();
100
+ return fail({ code: "LOGIN_FAILED", message: err.message, hint: "Run 'run402 login' to try again." });
101
+ }
102
+
103
+ let session;
104
+ try {
105
+ session = await sdk.session.exchangeCliToken({ code, codeVerifier, redirectUri, state });
106
+ } catch (err) {
107
+ close();
108
+ return reportSdkError(err);
109
+ }
110
+ // The loopback server has done its job. Tear it down (destroying any
111
+ // keep-alive socket) so Node's event loop drains and the CLI exits instead
112
+ // of hanging until Ctrl+C.
113
+ close();
114
+
115
+ const cached = cacheSession(session);
116
+ process.stderr.write(`\nSigned in (grade: ${cached.grade}).\n`);
117
+ const view = sessionView(cached);
118
+ await attachMemberships(view, session.control_plane_session_token);
119
+ console.log(JSON.stringify(view));
120
+ }
121
+
122
+ async function deviceLogin({ noOpen }) {
123
+ const sdk = getSdk();
124
+ let start;
125
+ try {
126
+ start = await sdk.session.deviceStart();
127
+ } catch (err) {
128
+ return reportSdkError(err);
129
+ }
130
+
131
+ // Human-in-the-loop prompt → stderr, so stdout stays clean for the final JSON.
132
+ const target = start.verification_uri_complete || start.verification_uri;
133
+ process.stderr.write(
134
+ `\nTo authorize the ${CLIENT_NAME}, open:\n ${start.verification_uri}\n` +
135
+ `and enter the code: ${start.user_code}\n\n`,
136
+ );
137
+ if (!noOpen && process.stderr.isTTY) {
138
+ openBrowser(target);
139
+ process.stderr.write("(opening your browser…)\n\n");
140
+ }
141
+ process.stderr.write("Waiting for approval…\n");
142
+
143
+ // Poll loop — honor the server interval, back off on slow_down, and stop at
144
+ // the device-code deadline. if/else (not switch) so the sync scanner doesn't
145
+ // mistake the poll states for CLI subcommands.
146
+ let intervalMs = Math.max(1, Number(start.interval) || 5) * 1000;
147
+ const deadline = Date.now() + Math.max(1, Number(start.expires_in) || 600) * 1000;
148
+
149
+ while (Date.now() < deadline) {
150
+ await sleep(intervalMs);
151
+ let result;
152
+ try {
153
+ result = await sdk.session.devicePoll(start.device_code);
154
+ } catch (err) {
155
+ return reportSdkError(err);
156
+ }
157
+ if (result.kind === "approved") {
158
+ const cached = cacheSession(result.session);
159
+ process.stderr.write(`\nSigned in (grade: ${cached.grade}, read-only).\n`);
160
+ const view = sessionView(cached);
161
+ await attachMemberships(view, result.session.control_plane_session_token);
162
+ console.log(JSON.stringify(view));
163
+ return;
164
+ }
165
+ if (result.kind === "authorization_pending") continue;
166
+ if (result.kind === "slow_down") {
167
+ intervalMs += 5000;
168
+ continue;
169
+ }
170
+ if (result.kind === "access_denied") {
171
+ fail({
172
+ code: "LOGIN_DENIED",
173
+ message: "Authorization was denied in the browser.",
174
+ hint: "Run 'run402 login --device' to try again.",
175
+ });
176
+ }
177
+ if (result.kind === "expired_token") {
178
+ fail({
179
+ code: "LOGIN_EXPIRED",
180
+ message: "The device code expired before approval.",
181
+ hint: "Run 'run402 login --device' to get a fresh code.",
182
+ });
183
+ }
184
+ fail({ code: "LOGIN_FAILED", message: `Unexpected device poll result: ${result.kind}` });
185
+ }
186
+ fail({
187
+ code: "LOGIN_TIMEOUT",
188
+ message: "Timed out waiting for browser approval.",
189
+ hint: "Run 'run402 login --device' to try again.",
190
+ });
191
+ }
192
+
193
+ export async function run(args = []) {
194
+ args = normalizeArgv(args);
195
+ if (hasHelp(args)) {
196
+ console.log(HELP);
197
+ process.exit(0);
198
+ }
199
+ assertKnownFlags(args, ["--help", "-h", "--no-open", "--device"]);
200
+ const extra = positionalArgs(args, []);
201
+ if (extra.length > 0) {
202
+ fail({ code: "BAD_USAGE", message: `Unexpected argument for login: ${extra[0]}`, hint: "Use `run402 login [--device] [--no-open]`." });
203
+ }
204
+ const noOpen = args.includes("--no-open");
205
+ if (args.includes("--device")) return deviceLogin({ noOpen });
206
+ return loopbackLogin({ noOpen });
207
+ }
package/lib/logout.mjs ADDED
@@ -0,0 +1,53 @@
1
+ /**
2
+ * `run402 logout` — end the sign-in session server-side and clear the local
3
+ * session and write-approval caches. See sign-in.mjs.
4
+ */
5
+
6
+ import { fail } from "./sdk-errors.mjs";
7
+ import { getSdk } from "./sdk.mjs";
8
+ import { normalizeArgv, hasHelp, assertKnownFlags, positionalArgs } from "./argparse.mjs";
9
+ import {
10
+ loadLiveControlPlaneSession,
11
+ clearControlPlaneSession,
12
+ clearRetiredSessionCaches,
13
+ } from "../core-dist/control-plane-session.js";
14
+ import { clearApprovals } from "../core-dist/write-approvals.js";
15
+
16
+ const HELP = `run402 logout — end your sign-in session
17
+
18
+ Usage:
19
+ run402 logout
20
+
21
+ Revokes the cached sign-in session on the gateway, then deletes it and every
22
+ cached write approval from this machine. The local cache is cleared even when
23
+ the revoke cannot reach the gateway. A wallet is not affected.
24
+ `;
25
+
26
+ export async function run(args = []) {
27
+ args = normalizeArgv(args);
28
+ if (hasHelp(args)) {
29
+ console.log(HELP);
30
+ process.exit(0);
31
+ }
32
+ assertKnownFlags(args, ["--help", "-h"]);
33
+ const extra = positionalArgs(args, []);
34
+ if (extra.length > 0) {
35
+ fail({ code: "BAD_USAGE", message: `Unexpected argument for logout: ${extra[0]}`, hint: "Use `run402 logout`." });
36
+ }
37
+ const session = loadLiveControlPlaneSession();
38
+ let revoked = false;
39
+ if (session) {
40
+ try {
41
+ await getSdk().session.revoke({ token: session.control_plane_session_token });
42
+ revoked = true;
43
+ } catch {
44
+ // Best-effort: a failed server revoke (expired token, offline) must not
45
+ // block clearing the local cache. The local token is removed regardless.
46
+ revoked = false;
47
+ }
48
+ }
49
+ clearControlPlaneSession();
50
+ clearApprovals();
51
+ clearRetiredSessionCaches();
52
+ console.log(JSON.stringify({ revoked, cleared: true }));
53
+ }
@@ -35,7 +35,7 @@ export function retryAction(command, why) {
35
35
  export function initializeWalletAction() {
36
36
  return nextAction("initialize_wallet", {
37
37
  command: "run402 init",
38
- why: "Create and fund an agent allowance, then retry.",
38
+ why: "Create and fund a local wallet, then retry.",
39
39
  });
40
40
  }
41
41
 
@@ -56,54 +56,54 @@ export function selectProjectAction() {
56
56
  export function setTierAction(tier = "prototype") {
57
57
  return nextAction("renew_tier", {
58
58
  command: `run402 tier set ${tier}`,
59
- why: "Subscribe the account to a tier (free on testnet), then retry.",
59
+ why: "Set a tier for the organization (prototype is free), then retry.",
60
60
  });
61
61
  }
62
62
 
63
63
  export function deployAction() {
64
64
  return nextAction("deploy", {
65
- command: "run402 deploy apply --manifest app.json",
66
- why: "Apply your release manifest to deploy.",
65
+ command: "run402 deploy --manifest app.json",
66
+ why: "Deploy your release manifest.",
67
67
  });
68
68
  }
69
69
 
70
70
  /**
71
71
  * The cold-start deploy when the account holds no tier yet: `run402 up -y`
72
- * subscribes the prototype tier itself as part of the first deploy, so `init`
72
+ * sets the prototype tier itself as part of the first deploy, so `init`
73
73
  * hands the agent ONE command instead of `tier set` + a separate deploy.
74
74
  * `tier set prototype` stays named as the standalone alternative.
75
75
  */
76
76
  export function upDeployAction() {
77
77
  return nextAction("deploy", {
78
78
  command: "run402 up -y",
79
- why: "Deploy with run402 up -y — it subscribes the prototype tier (free on testnet) as part of the first deploy. Or subscribe separately: run402 tier set prototype.",
79
+ why: "Deploy with run402 up -y — it sets the prototype tier (free on testnet) as part of the first deploy. Or set it separately: run402 tier set prototype.",
80
80
  });
81
81
  }
82
82
 
83
83
  /**
84
- * `repos create` (and `gitvault init`) on an org with no claimed slug: the
84
+ * `repos create` (and `gitvault 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
88
88
  * $1 — a gateway change lands the fee separately, so this stays true under
89
89
  * both the old and new pricing rule.
90
90
  */
91
- export function claimOrgSlugAction() {
92
- return nextAction("claim_org_slug", {
91
+ export function setOrgSlugAction() {
92
+ return nextAction("set_org_slug", {
93
93
  command: "run402 org slug <slug>",
94
- why: "This organization has no claimed slug yet, so its repos have no run402::<slug>/<name> address. Owner-only.",
94
+ why: "This organization has no slug set yet, so its repos have no run402::<slug>/<name> address. Owner-only.",
95
95
  });
96
96
  }
97
97
 
98
98
  /**
99
99
  * The org already has a slug, but this project's address-form repo name was
100
- * not claimed this time (a collision, or the best-effort claim failed for
101
- * some other reason) — point at the explicit claim verb instead of leaving
100
+ * not set this time (a collision, or the best-effort set failed for
101
+ * some other reason) — point at the explicit set verb instead of leaving
102
102
  * `address: null` unexplained.
103
103
  */
104
- export function claimRepoNameAction(projectId) {
105
- return nextAction("claim_repo_name", {
104
+ export function setRepoNameAction(projectId) {
105
+ return nextAction("set_repo_name", {
106
106
  command: `run402 repos name <name> --project ${projectId}`,
107
- why: "The owning organization has a slug, but this project has no claimed address-form name yet.",
107
+ why: "The owning organization has a slug, but this project has no address-form name set yet.",
108
108
  });
109
109
  }
package/lib/org.mjs CHANGED
@@ -33,6 +33,7 @@ Usage:
33
33
  run402 org rename [<org_id>] --name <display_name> (or: --clear to remove the label)
34
34
  run402 org payout-wallet [<org_id>] <wallet_address> (or: --clear to remove the explicit default)
35
35
  run402 org slug <slug> [--org <org_id>]
36
+ run402 org adopt [--org <org_id>] [--name <label>]
36
37
  run402 org whoami
37
38
  run402 org whoami --set-name <name>
38
39
  run402 org use <org_id>
@@ -59,11 +60,11 @@ The second attribute may also be passed positionally (run402 org member add <wal
59
60
 
60
61
  Subcommands:
61
62
  create Create an empty org on the prototype tier (you become owner)
62
- list Orgs you are a member of
63
+ list Your orgs: role, tier, lifecycle, quotas, allowance, advisories
63
64
  get Read one org (label + tier/lease + your role)
64
65
  rename Set or clear an org's display label (owner-only)
65
66
  slug Claim or rename the org's globally-unique, address-form slug
66
- (owner-only). A genesis claim spends a one-time claim fee; a
67
+ (owner-only). A genesis set spends a one-time fee; a
67
68
  rename releases the old slug into a ~90-day cooldown.
68
69
  use Select the current org for this wallet profile
69
70
  current Report the resolved current org and where it came from
@@ -110,10 +111,19 @@ created org's prototype tier plus lease_started_at / lease_expires_at. Paid
110
111
  tiers are a separate flow. Step-up gated for control-plane sessions; the
111
112
  free-org cap may apply.
112
113
  `,
113
- list: `run402 org list — orgs you are a member of
114
+ list: `run402 org list — your orgs, with the account overview
114
115
 
115
116
  Usage:
116
117
  run402 org list
118
+
119
+ Lists every org you are a member of (GET /orgs/v1) and joins each row with
120
+ the account overview (GET /agent/v1/me/overview): tier, lifecycle state,
121
+ lease, quotas, allowance. The output also carries the overview's wallets,
122
+ rollup, and advisories, plus scope and session (the sign-in session's grade,
123
+ or null when a wallet reads). Both reads use the CLI's credential: the active
124
+ wallet when there is one, else the sign-in session from 'run402 login'. When the overview
125
+ read is refused (a grant key, for one), the memberships still print and
126
+ 'overview' is null.
117
127
  `,
118
128
  get: `run402 org get — read one org (label + tier/lease + your role)
119
129
 
@@ -135,17 +145,17 @@ Legacy (still supported):
135
145
  Owner-only + step-up gated. Pass --clear (or an empty display_name) to remove
136
146
  the label. Output includes the updated tier and lease timestamps.
137
147
  `,
138
- slug: `run402 org slug — claim or rename the org's address-form slug
148
+ slug: `run402 org slug — set or rename the org's address-form slug
139
149
 
140
150
  Usage:
141
151
  run402 org slug <slug> [--org <org_id>] [--idempotency-key <key>]
142
152
 
143
- The slug is a globally-unique, claimable, address-form handle for the org
153
+ The slug is a globally-unique, address-form handle for the org
144
154
  (repo-first-onramp design D6) — the <org-slug> half of a named repo address
145
155
  run402::<org-slug>/<name>. Grammar: lowercase [a-z0-9-], no leading/trailing/
146
156
  double hyphen, max 39 chars. Owner-only.
147
157
 
148
- A genesis claim (the org had no prior slug) spends a one-time claim fee off
158
+ A genesis set (the org had no prior slug) spends a one-time fee off
149
159
  the org's balance. A rename is free but releases the OLD slug into a ~90-day
150
160
  cooldown: it stops resolving, with a typed SLUG_RELEASED refusal naming the
151
161
  new slug as successor — there is no redirect, so update every remote and
@@ -330,11 +340,35 @@ async function list(args) {
330
340
  const a = normalizeArgv(args);
331
341
  assertKnownFlags(a, ["--help", "-h"]);
332
342
  requirePositionalCount(a, [], { min: 0, max: 0, command: "run402 org list" });
343
+ const sdk = getSdk();
344
+ let orgs;
333
345
  try {
334
- console.log(JSON.stringify({ orgs: await getSdk().orgs.list() }, null, 2));
346
+ orgs = await sdk.orgs.list();
335
347
  } catch (err) {
336
- reportSdkError(err);
348
+ return reportSdkError(err);
349
+ }
350
+ // The account overview is the richer read, made with the same credential as
351
+ // the membership list; a caller the overview does not accept (a grant key)
352
+ // still gets its memberships.
353
+ let overview = null;
354
+ try {
355
+ overview = await sdk.me.overview();
356
+ } catch {
357
+ overview = null;
337
358
  }
359
+ const byId = new Map((overview?.organizations ?? []).map((o) => [o.id, o]));
360
+ console.log(JSON.stringify({
361
+ orgs: orgs.map((m) => (byId.has(m.org_id) ? { ...m, overview: byId.get(m.org_id) } : m)),
362
+ ...(overview
363
+ ? {
364
+ scope: overview.scope ?? null,
365
+ session: overview.session ?? null,
366
+ rollup: overview.rollup ?? null,
367
+ wallets: overview.wallets ?? [],
368
+ advisories: overview.advisories ?? [],
369
+ }
370
+ : { overview: null }),
371
+ }, null, 2));
338
372
  }
339
373
 
340
374
  async function whoami(args) {
@@ -555,13 +589,65 @@ async function payoutWallet(args) {
555
589
  }
556
590
 
557
591
  /**
558
- * `run402 org slug <slug>` — claim or rename the org's address-form slug.
559
- * Owner-only, and a genesis claim spends a one-time claim fee — this is a
592
+ * `run402 org slug <slug>` — set or rename the org's address-form slug.
593
+ * Owner-only, and a genesis set spends a one-time fee — this is a
560
594
  * PAID, side-effecting mutation, so it
561
595
  * requires `Idempotency-Key`; the SDK generates one client-side when
562
596
  * `--idempotency-key` is omitted, so a retried call after a dropped response
563
597
  * can never double-bill.
564
598
  */
599
+ /**
600
+ * `run402 org adopt` — become the owner of the org your wallet's agent
601
+ * created (an ownership transfer on org memberships; the wallet stays on the
602
+ * agent, which is downgraded to developer). Dual proof: the write-capable
603
+ * sign-in session + a fresh signature from the active wallet over a server
604
+ * nonce. The Node convenience runs the whole dance (read session → challenge
605
+ * → sign → submit). Step-up failures and the multi-org `select_org`
606
+ * round-trip are surfaced here, not thrown at the user.
607
+ */
608
+ async function adopt(args) {
609
+ const a = normalizeArgv(args);
610
+ const valueFlags = ["--org", "--name"];
611
+ assertKnownFlags(a, [...valueFlags, "--help", "-h"], valueFlags);
612
+ requirePositionalCount(a, valueFlags, { min: 0, max: 0, command: "run402 org adopt [--org <org_id>] [--name <label>]" });
613
+ const orgId = flagValue(a, "--org");
614
+ const name = flagValue(a, "--name");
615
+ try {
616
+ const { adoptOrg } = await import("#sdk/node");
617
+ const result = await adoptOrg(getSdk(), {
618
+ orgId: orgId ?? undefined,
619
+ displayName: name ?? undefined,
620
+ });
621
+ // CLI output contract (cli-output-shape): no top-level `status` envelope.
622
+ // The gateway's discriminator becomes an explicit `adopted` boolean.
623
+ if (result && result.status === "select_org") {
624
+ console.log(JSON.stringify({
625
+ adopted: false,
626
+ selectable_orgs: result.selectable_orgs,
627
+ hint: "Your wallet's agent owns more than one org. Re-run with --org <org_id> to adopt one.",
628
+ }, null, 2));
629
+ return;
630
+ }
631
+ console.log(JSON.stringify({
632
+ adopted: true,
633
+ org_id: result.org_id,
634
+ display_name: result.display_name,
635
+ role: result.role,
636
+ already_owned: result.already_owned ?? false,
637
+ }, null, 2));
638
+ } catch (err) {
639
+ const { isStepUpRequired } = await import("#sdk/node");
640
+ if (isStepUpRequired(err)) {
641
+ return fail({
642
+ code: "STEP_UP_REQUIRED",
643
+ message: "Adopting an org needs a fresh passkey step-up.",
644
+ hint: "Run 'run402 login' (a fresh passkey sign-in), then re-run 'run402 org adopt'.",
645
+ });
646
+ }
647
+ reportSdkError(err);
648
+ }
649
+ }
650
+
565
651
  async function slug(args) {
566
652
  const a = normalizeArgv(args);
567
653
  const valueFlags = ["--org", "--idempotency-key"];
@@ -576,16 +662,16 @@ async function slug(args) {
576
662
  if (!org) {
577
663
  fail({
578
664
  code: "ORG_UNRESOLVED",
579
- message: "Could not resolve which organization to claim this slug for.",
665
+ message: "Could not resolve which organization to set this slug for.",
580
666
  hint: "Pass --org <org_id>, or select one first with `run402 org use <id>`.",
581
667
  });
582
668
  }
583
669
  const idempotencyKey = flagValue(a, "--idempotency-key");
584
670
  try {
585
- const result = await getSdk().org(org.orgId).claimSlug(newSlug, idempotencyKey != null ? { idempotencyKey } : {});
671
+ const result = await getSdk().org(org.orgId).setSlug(newSlug, idempotencyKey != null ? { idempotencyKey } : {});
586
672
  console.log(JSON.stringify(result, null, 2));
587
673
  if (result.created) {
588
- console.error(`slug "${result.slug}" claimed for ${org.orgId} — a one-time claim fee was debited from the org's balance.`);
674
+ console.error(`slug "${result.slug}" set for ${org.orgId} — a one-time fee was debited from the org's balance.`);
589
675
  } else if (result.previous_slug && result.previous_slug !== result.slug) {
590
676
  console.error(
591
677
  `org ${org.orgId} renamed from "${result.previous_slug}" to "${result.slug}" — no fee. ` +
@@ -894,6 +980,7 @@ export async function run(sub, args) {
894
980
  case "rename": await rename(args); break;
895
981
  case "payout-wallet": await payoutWallet(args); break;
896
982
  case "slug": await slug(args); break;
983
+ case "adopt": await adopt(args); break;
897
984
  case "whoami": await whoami(args); break;
898
985
  case "use": await use(args); break;
899
986
  case "current": await current(args); break;
package/lib/pay.mjs CHANGED
@@ -30,7 +30,7 @@ Examples:
30
30
  run402 pay https://seller.example/translate --method POST \
31
31
  --body '{"text":"hello"}' --max-usd 0.05 --idempotency-key translation:1
32
32
 
33
- The command uses the same allowance wallet and bounded x402 buyer as the SDK.
33
+ The command uses the same wallet and bounded x402 buyer as the SDK.
34
34
  An unpriced URL is passed through with payment: null.
35
35
  On trusted Run402 PAYMENT_INTENT_PENDING, wait for Retry-After and repeat this
36
36
  identical command with the same payer and --idempotency-key. Never change the key.
package/lib/projects.mjs CHANGED
@@ -1,8 +1,7 @@
1
1
  import { readFileSync } from "fs";
2
- import { allowanceAuthHeaders, resolveProjectId, getActiveProjectId, isCoreApiTarget } from "./config.mjs";
3
- import { loadLiveOperatorSession } from "../core-dist/operator-session.js";
2
+ import { walletAuthHeaders, resolveProjectId, getActiveProjectId, isCoreApiTarget } from "./config.mjs";
4
3
  import { loadLiveControlPlaneSession } from "../core-dist/control-plane-session.js";
5
- import { withAutoApprove } from "./operator.mjs";
4
+ import { withAutoApprove } from "./sign-in.mjs";
6
5
  import { getSdk } from "./sdk.mjs";
7
6
  import { reportSdkError, fail, parseFlagJson } from "./sdk-errors.mjs";
8
7
  import { stampOrgFromProject } from "./org-context.mjs";
@@ -76,15 +75,15 @@ Notes:
76
75
  Local project keys live under 'run402 credentials project-keys ...'.
77
76
  - 'list' is a SERVER read, not the local key cache: it shows every project the
78
77
  active wallet can reach (membership-scoped), with name, site_url, custom
79
- domains, and org_id. '--org <id>' filters to one org; '--all' reads the
80
- cross-wallet inventory for every wallet controlling your operator email
81
- (run 'run402 operator login' first for the union, else it falls back to the
82
- current wallet's slice). The 'active' marker still comes from local state.
78
+ domains, and org_id. '--org <id>' filters to one org; '--all' reads every
79
+ project you can reach across all your orgs (as your sign-in session after
80
+ 'run402 login', else as the current wallet). The 'active' marker still
81
+ comes from local state.
83
82
  - 'rename' fixes a project's display name. You must be an org admin (or hold a
84
83
  project:write grant) on the owning org; it works even if the project was
85
84
  never provisioned from this machine.
86
85
  - 'rest' uses PostgREST query syntax (table name + optional query string)
87
- - 'provision' requires a funded allowance on Run402 Cloud. Against a
86
+ - 'provision' requires a funded wallet on Run402 Cloud. Against a
88
87
  configured Run402 Core target, it creates a local Core project without
89
88
  payment.
90
89
  - 'apply-expose' declares the full authorization surface (tables, views, RPCs)
@@ -115,11 +114,10 @@ Options:
115
114
  --org <id> Filter to projects owned by one org (organization).
116
115
  Authorize-before-reveal: a non-member or guessed id is a
117
116
  403; a non-UUID id is a 400.
118
- --all Read the cross-wallet inventory across every wallet
119
- controlling your operator email. Run 'run402 operator
120
- login' first for the union; without a session it falls
121
- back to the current wallet's slice. Mutually exclusive
122
- with --org.
117
+ --all Read every project you can reach across all your orgs
118
+ (GET /agent/v1/me/projects). After 'run402 login' it
119
+ reads as your sign-in session; without one, as the
120
+ current wallet. Mutually exclusive with --org.
123
121
 
124
122
  Notes:
125
123
  - This is a SERVER read (membership-scoped), not the local keystore. Each row
@@ -197,8 +195,8 @@ Options:
197
195
  from --name when omitted; an unnamed provision stays un-keyed.
198
196
 
199
197
  Notes:
200
- - Payment is automatic via x402 on Run402 Cloud; requires a funded allowance.
201
- Against a configured Run402 Core target, no Cloud tier/allowance/payment is
198
+ - Payment is automatic via x402 on Run402 Cloud; requires a funded wallet.
199
+ Against a configured Run402 Core target, no Cloud tier/wallet/payment is
202
200
  required.
203
201
  - The new project becomes the active project after provisioning
204
202
 
@@ -246,7 +244,7 @@ Environment:
246
244
  RUN402_ADMIN_COOKIE Optional admin OAuth cookie header, e.g. "run402_admin=..."
247
245
 
248
246
  Notes:
249
- - Platform-admin only. The configured allowance wallet must be an admin
247
+ - Platform-admin only. The configured wallet must be an admin
250
248
  wallet, or RUN402_ADMIN_COOKIE must contain an admin OAuth cookie.
251
249
  - Project service keys are not enough for this endpoint.
252
250
  - Output is the gateway's per-project finance JSON: revenue, direct cost,
@@ -361,10 +359,10 @@ async function provision(args) {
361
359
  });
362
360
  }
363
361
  }
364
- // Aggressive early exit when no agent allowance is configured — but only when
365
- // there's also no operator (control-plane) session, since a wallet-less human
366
- // provisions into an org via their operator approval instead of a wallet.
367
- if (!isCoreApiTarget() && !loadLiveControlPlaneSession()) allowanceAuthHeaders("/projects/v1");
362
+ // Aggressive early exit when no local wallet is configured — but only when
363
+ // there's also no sign-in session, since a wallet-less person provisions
364
+ // into an org via a write approval instead of a wallet.
365
+ if (!isCoreApiTarget() && !loadLiveControlPlaneSession()) walletAuthHeaders("/projects/v1");
368
366
 
369
367
  const activeBefore = getActiveProjectId();
370
368
  try {
@@ -495,7 +493,7 @@ async function validateExpose(args = []) {
495
493
  }
496
494
  const activeProjectId = getActiveProjectId();
497
495
  const project = projectFlag || projectId || activeProjectId || undefined;
498
- if (!project) allowanceAuthHeaders("/projects/v1/expose/validate");
496
+ if (!project) walletAuthHeaders("/projects/v1/expose/validate");
499
497
  const migration = migrationFile ? readFileSync(migrationFile, "utf-8") : migrationSql;
500
498
 
501
499
  try {
@@ -525,25 +523,24 @@ async function list(args = []) {
525
523
  fail({
526
524
  code: "BAD_USAGE",
527
525
  message: "--all and --org are mutually exclusive.",
528
- hint: "--all reads the cross-wallet operator inventory; --org filters the membership-scoped list to one org.",
526
+ hint: "--all reads every project across all your orgs; --org filters the membership-scoped list to one org.",
529
527
  });
530
528
  }
531
529
 
532
- // `--all` reads the operator email-union inventory across every wallet
533
- // controlling your verified email. Pass the cached operator-session token
534
- // when present (cross-wallet union); otherwise the SDK falls back to SIWX
535
- // wallet auth and the gateway returns just this wallet's slice.
530
+ // `--all` reads every project the caller can reach across all its orgs.
531
+ // Pass the cached sign-in session token when present; otherwise the SDK
532
+ // uses the credential provider and the gateway returns this wallet's slice.
536
533
  const opts = {};
537
534
  if (all) {
538
535
  opts.all = true;
539
- const session = loadLiveOperatorSession();
540
- if (session) opts.token = session.operator_session_token;
536
+ const session = loadLiveControlPlaneSession();
537
+ if (session) opts.token = session.control_plane_session_token;
541
538
  } else if (org) {
542
539
  opts.org = org;
543
540
  }
544
541
 
545
542
  // Active marker comes from local state; the inventory itself is the server
546
- // read (NOT the keystore), so it surfaces every project the wallet/email can
543
+ // read (NOT the keystore), so it surfaces every project the principal can
547
544
  // reach — including ones never provisioned from this machine.
548
545
  const activeId = getActiveProjectId();
549
546