run402 4.93.2 → 4.95.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 (316) hide show
  1. package/README.md +61 -59
  2. package/cli.mjs +40 -47
  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 +2 -2
  11. package/lib/admin.mjs +6 -6
  12. package/lib/agent.mjs +20 -18
  13. package/lib/ai.mjs +5 -5
  14. package/lib/approve.mjs +81 -0
  15. package/lib/apps.mjs +12 -12
  16. package/lib/archives.mjs +371 -8
  17. package/lib/argparse.mjs +5 -5
  18. package/lib/assets.mjs +14 -14
  19. package/lib/auth.mjs +34 -34
  20. package/lib/billing.mjs +6 -6
  21. package/lib/branches.mjs +7 -7
  22. package/lib/buzz-doctor.mjs +2 -2
  23. package/lib/buzz-notifications.mjs +5 -5
  24. package/lib/buzz.mjs +3 -3
  25. package/lib/buzz.test.mjs +1 -1
  26. package/lib/cache.mjs +1 -1
  27. package/lib/cdn.mjs +2 -2
  28. package/lib/ci.mjs +12 -12
  29. package/lib/claims.mjs +2 -2
  30. package/lib/cold-start.mjs +21 -21
  31. package/lib/command-manifest.mjs +55 -70
  32. package/lib/config.mjs +28 -28
  33. package/lib/contacts.mjs +16 -16
  34. package/lib/contracts.mjs +12 -12
  35. package/lib/credentials.mjs +33 -33
  36. package/lib/daemon-path.mjs +1 -1
  37. package/lib/delegates.mjs +14 -14
  38. package/lib/deliveries.mjs +6 -6
  39. package/lib/deploy-rehearse-discovery.test.mjs +7 -7
  40. package/lib/deploy-v2.mjs +241 -193
  41. package/lib/deploy.mjs +21 -53
  42. package/lib/dev.mjs +3 -3
  43. package/lib/doctor-source-scan.mjs +1 -1
  44. package/lib/doctor.mjs +63 -63
  45. package/lib/domains.mjs +27 -27
  46. package/lib/email.mjs +38 -38
  47. package/lib/errors.mjs +40 -24
  48. package/lib/escalations.mjs +5 -5
  49. package/lib/events.mjs +32 -25
  50. package/lib/feedback.mjs +12 -12
  51. package/lib/functions.mjs +34 -34
  52. package/lib/gitvault-daemon.mjs +1 -1
  53. package/lib/gitvault-scaffold.mjs +2 -2
  54. package/lib/gitvault.mjs +2 -2
  55. package/lib/grants.mjs +7 -7
  56. package/lib/image.mjs +10 -10
  57. package/lib/init.mjs +69 -69
  58. package/lib/jobs.mjs +15 -15
  59. package/lib/lightning-wallet.mjs +28 -28
  60. package/lib/live.mjs +1 -1
  61. package/lib/login.mjs +207 -0
  62. package/lib/logout.mjs +53 -0
  63. package/lib/logs.mjs +8 -8
  64. package/lib/messages.mjs +5 -5
  65. package/lib/next-actions.mjs +16 -16
  66. package/lib/org-context.mjs +13 -13
  67. package/lib/{org.mjs → orgs.mjs} +215 -167
  68. package/lib/pay.mjs +1 -1
  69. package/lib/projects.mjs +70 -84
  70. package/lib/redeem.mjs +7 -8
  71. package/lib/remote-helper-session.mjs +12 -12
  72. package/lib/repos.mjs +83 -83
  73. package/lib/rooms.mjs +105 -69
  74. package/lib/sdk-errors.mjs +10 -5
  75. package/lib/sdk.mjs +3 -3
  76. package/lib/secrets.mjs +13 -13
  77. package/lib/service.mjs +5 -5
  78. package/lib/session-proofs.mjs +21 -0
  79. package/lib/sign-in.mjs +296 -0
  80. package/lib/sites.mjs +22 -22
  81. package/lib/snapshots.mjs +10 -10
  82. package/lib/stats.mjs +2 -2
  83. package/lib/status.mjs +25 -25
  84. package/lib/subdomains.mjs +30 -30
  85. package/lib/subscriptions.mjs +10 -10
  86. package/lib/tier.mjs +29 -22
  87. package/lib/transfer.mjs +59 -86
  88. package/lib/up.mjs +14 -14
  89. package/lib/wallet-context.mjs +4 -4
  90. package/lib/wallet-context.test.mjs +3 -3
  91. package/lib/wallets.mjs +197 -32
  92. package/lib/webhook-secret.mjs +6 -6
  93. package/lib/webhooks.mjs +8 -8
  94. package/lib/whoami.mjs +102 -0
  95. package/package.json +1 -1
  96. package/sdk/core-dist/config.js +52 -18
  97. package/sdk/core-dist/control-plane-session.js +31 -6
  98. package/sdk/core-dist/keystore.js +7 -7
  99. package/sdk/core-dist/profiles.js +9 -7
  100. package/sdk/core-dist/{allowance-auth.js → wallet-auth.js} +15 -15
  101. package/{core-dist/allowance.js → sdk/core-dist/wallet.js} +20 -20
  102. package/{core-dist/write-auth-session.js → sdk/core-dist/write-approvals.js} +19 -19
  103. package/sdk/dist/actions.d.ts +4 -4
  104. package/sdk/dist/actions.d.ts.map +1 -1
  105. package/sdk/dist/app-up.d.ts +2 -2
  106. package/sdk/dist/app-up.d.ts.map +1 -1
  107. package/sdk/dist/config.d.ts +1 -2
  108. package/sdk/dist/config.d.ts.map +1 -1
  109. package/sdk/dist/config.js.map +1 -1
  110. package/sdk/dist/control-plane-credentials.d.ts +8 -8
  111. package/sdk/dist/control-plane-credentials.js +7 -7
  112. package/sdk/dist/credentials.d.ts +20 -20
  113. package/sdk/dist/credentials.d.ts.map +1 -1
  114. package/sdk/dist/credentials.js +1 -1
  115. package/sdk/dist/errors.d.ts +26 -28
  116. package/sdk/dist/errors.d.ts.map +1 -1
  117. package/sdk/dist/errors.js +35 -38
  118. package/sdk/dist/errors.js.map +1 -1
  119. package/sdk/dist/index.d.ts +29 -16
  120. package/sdk/dist/index.d.ts.map +1 -1
  121. package/sdk/dist/index.js +30 -17
  122. package/sdk/dist/index.js.map +1 -1
  123. package/sdk/dist/kernel.d.ts +1 -1
  124. package/sdk/dist/kernel.d.ts.map +1 -1
  125. package/sdk/dist/kernel.js +10 -9
  126. package/sdk/dist/kernel.js.map +1 -1
  127. package/sdk/dist/namespaces/admin.d.ts +52 -121
  128. package/sdk/dist/namespaces/admin.d.ts.map +1 -1
  129. package/sdk/dist/namespaces/admin.js +41 -45
  130. package/sdk/dist/namespaces/admin.js.map +1 -1
  131. package/sdk/dist/namespaces/agent.d.ts +2 -2
  132. package/sdk/dist/namespaces/agent.d.ts.map +1 -1
  133. package/sdk/dist/namespaces/agent.js +2 -2
  134. package/sdk/dist/namespaces/agent.js.map +1 -1
  135. package/sdk/dist/namespaces/ai.d.ts +2 -2
  136. package/sdk/dist/namespaces/ai.js +3 -3
  137. package/sdk/dist/namespaces/ai.js.map +1 -1
  138. package/sdk/dist/namespaces/assets.js +1 -1
  139. package/sdk/dist/namespaces/assets.js.map +1 -1
  140. package/sdk/dist/namespaces/billing.d.ts +6 -3
  141. package/sdk/dist/namespaces/billing.d.ts.map +1 -1
  142. package/sdk/dist/namespaces/billing.js.map +1 -1
  143. package/sdk/dist/namespaces/buzz-notifications.types.d.ts +1 -1
  144. package/sdk/dist/namespaces/buzz.d.ts +2 -2
  145. package/sdk/dist/namespaces/buzz.d.ts.map +1 -1
  146. package/sdk/dist/namespaces/buzz.js +2 -2
  147. package/sdk/dist/namespaces/buzz.js.map +1 -1
  148. package/sdk/dist/namespaces/buzz.types.d.ts +2 -2
  149. package/sdk/dist/namespaces/buzz.types.d.ts.map +1 -1
  150. package/sdk/dist/namespaces/ci.js +3 -3
  151. package/sdk/dist/namespaces/ci.js.map +1 -1
  152. package/sdk/dist/namespaces/credentials.types.d.ts +1 -1
  153. package/sdk/dist/namespaces/deploy.d.ts +1 -1
  154. package/sdk/dist/namespaces/deploy.d.ts.map +1 -1
  155. package/sdk/dist/namespaces/deploy.js +31 -32
  156. package/sdk/dist/namespaces/deploy.js.map +1 -1
  157. package/sdk/dist/namespaces/deploy.types.d.ts +6 -6
  158. package/sdk/dist/namespaces/deploy.types.d.ts.map +1 -1
  159. package/sdk/dist/namespaces/domains.d.ts +10 -2
  160. package/sdk/dist/namespaces/domains.d.ts.map +1 -1
  161. package/sdk/dist/namespaces/domains.js +10 -5
  162. package/sdk/dist/namespaces/domains.js.map +1 -1
  163. package/sdk/dist/namespaces/errors.js +1 -1
  164. package/sdk/dist/namespaces/escalations.d.ts +1 -1
  165. package/sdk/dist/namespaces/escalations.js +1 -1
  166. package/sdk/dist/namespaces/escalations.types.d.ts +2 -2
  167. package/sdk/dist/namespaces/escalations.types.d.ts.map +1 -1
  168. package/sdk/dist/namespaces/events.types.d.ts +1 -1
  169. package/sdk/dist/namespaces/functions.d.ts +1 -1
  170. package/sdk/dist/namespaces/functions.js +2 -2
  171. package/sdk/dist/namespaces/functions.js.map +1 -1
  172. package/sdk/dist/namespaces/gitvault.crypto.d.ts +1 -1
  173. package/sdk/dist/namespaces/gitvault.d.ts +26 -26
  174. package/sdk/dist/namespaces/gitvault.d.ts.map +1 -1
  175. package/sdk/dist/namespaces/gitvault.js +113 -113
  176. package/sdk/dist/namespaces/gitvault.js.map +1 -1
  177. package/sdk/dist/namespaces/me.d.ts +120 -0
  178. package/sdk/dist/namespaces/me.d.ts.map +1 -0
  179. package/sdk/dist/namespaces/me.js +34 -0
  180. package/sdk/dist/namespaces/me.js.map +1 -0
  181. package/sdk/dist/namespaces/org.d.ts +108 -6
  182. package/sdk/dist/namespaces/org.d.ts.map +1 -1
  183. package/sdk/dist/namespaces/org.js +63 -7
  184. package/sdk/dist/namespaces/org.js.map +1 -1
  185. package/sdk/dist/namespaces/org.types.d.ts +29 -9
  186. package/sdk/dist/namespaces/org.types.d.ts.map +1 -1
  187. package/sdk/dist/namespaces/pay.js +3 -3
  188. package/sdk/dist/namespaces/pay.js.map +1 -1
  189. package/sdk/dist/namespaces/projects.d.ts +9 -9
  190. package/sdk/dist/namespaces/projects.js +15 -15
  191. package/sdk/dist/namespaces/projects.js.map +1 -1
  192. package/sdk/dist/namespaces/projects.types.d.ts +28 -12
  193. package/sdk/dist/namespaces/projects.types.d.ts.map +1 -1
  194. package/sdk/dist/namespaces/rooms.d.ts +7 -7
  195. package/sdk/dist/namespaces/rooms.js +17 -17
  196. package/sdk/dist/namespaces/rooms.js.map +1 -1
  197. package/sdk/dist/namespaces/rooms.types.d.ts +9 -9
  198. package/sdk/dist/namespaces/rooms.types.d.ts.map +1 -1
  199. package/sdk/dist/namespaces/service.d.ts +1 -1
  200. package/sdk/dist/namespaces/service.js +1 -1
  201. package/sdk/dist/namespaces/{operator-session.d.ts → session.d.ts} +135 -51
  202. package/sdk/dist/namespaces/session.d.ts.map +1 -0
  203. package/sdk/dist/namespaces/{operator-session.js → session.js} +118 -30
  204. package/sdk/dist/namespaces/session.js.map +1 -0
  205. package/sdk/dist/namespaces/subdomains.d.ts +5 -5
  206. package/sdk/dist/namespaces/subdomains.d.ts.map +1 -1
  207. package/sdk/dist/namespaces/subdomains.js +6 -6
  208. package/sdk/dist/namespaces/subdomains.js.map +1 -1
  209. package/sdk/dist/namespaces/tier.d.ts +44 -21
  210. package/sdk/dist/namespaces/tier.d.ts.map +1 -1
  211. package/sdk/dist/namespaces/tier.js +7 -7
  212. package/sdk/dist/namespaces/tier.js.map +1 -1
  213. package/sdk/dist/namespaces/transfers.d.ts +119 -85
  214. package/sdk/dist/namespaces/transfers.d.ts.map +1 -1
  215. package/sdk/dist/namespaces/transfers.js +31 -38
  216. package/sdk/dist/namespaces/transfers.js.map +1 -1
  217. package/sdk/dist/namespaces/vouchers.d.ts +9 -10
  218. package/sdk/dist/namespaces/vouchers.d.ts.map +1 -1
  219. package/sdk/dist/namespaces/vouchers.js +4 -5
  220. package/sdk/dist/namespaces/vouchers.js.map +1 -1
  221. package/sdk/dist/namespaces/wallets.d.ts +72 -3
  222. package/sdk/dist/namespaces/wallets.d.ts.map +1 -1
  223. package/sdk/dist/namespaces/wallets.js +141 -3
  224. package/sdk/dist/namespaces/wallets.js.map +1 -1
  225. package/sdk/dist/namespaces/write-approval.d.ts +101 -0
  226. package/sdk/dist/namespaces/write-approval.d.ts.map +1 -0
  227. package/sdk/dist/namespaces/write-approval.js +62 -0
  228. package/sdk/dist/namespaces/write-approval.js.map +1 -0
  229. package/sdk/dist/node/actions-node.d.ts +3 -3
  230. package/sdk/dist/node/actions-node.d.ts.map +1 -1
  231. package/sdk/dist/node/actions-node.js +44 -44
  232. package/sdk/dist/node/actions-node.js.map +1 -1
  233. package/sdk/dist/node/assets-node.js +6 -6
  234. package/sdk/dist/node/assets-node.js.map +1 -1
  235. package/sdk/dist/node/{bearer-claim-key.d.ts → bearer-redeem-key.d.ts} +12 -12
  236. package/sdk/dist/node/bearer-redeem-key.d.ts.map +1 -0
  237. package/sdk/dist/node/{bearer-claim-key.js → bearer-redeem-key.js} +21 -21
  238. package/sdk/dist/node/bearer-redeem-key.js.map +1 -0
  239. package/sdk/dist/node/ci.d.ts +1 -1
  240. package/sdk/dist/node/ci.d.ts.map +1 -1
  241. package/sdk/dist/node/ci.js +6 -6
  242. package/sdk/dist/node/ci.js.map +1 -1
  243. package/sdk/dist/node/credentials.d.ts +18 -17
  244. package/sdk/dist/node/credentials.d.ts.map +1 -1
  245. package/sdk/dist/node/credentials.js +29 -28
  246. package/sdk/dist/node/credentials.js.map +1 -1
  247. package/sdk/dist/node/deploy-manifest.d.ts +3 -5
  248. package/sdk/dist/node/deploy-manifest.d.ts.map +1 -1
  249. package/sdk/dist/node/deploy-manifest.js +3 -12
  250. package/sdk/dist/node/deploy-manifest.js.map +1 -1
  251. package/sdk/dist/node/funding-recovery.d.ts +1 -1
  252. package/sdk/dist/node/funding-recovery.d.ts.map +1 -1
  253. package/sdk/dist/node/funding-recovery.js +1 -1
  254. package/sdk/dist/node/funding-recovery.js.map +1 -1
  255. package/sdk/dist/node/gitvault-apply.d.ts +1 -1
  256. package/sdk/dist/node/gitvault-apply.d.ts.map +1 -1
  257. package/sdk/dist/node/gitvault-apply.js +1 -1
  258. package/sdk/dist/node/gitvault-apply.js.map +1 -1
  259. package/sdk/dist/node/gitvault-handoff.d.ts +22 -22
  260. package/sdk/dist/node/gitvault-handoff.d.ts.map +1 -1
  261. package/sdk/dist/node/gitvault-handoff.js +45 -45
  262. package/sdk/dist/node/gitvault-handoff.js.map +1 -1
  263. package/sdk/dist/node/gitvault-prewarm.js +7 -7
  264. package/sdk/dist/node/gitvault-prewarm.js.map +1 -1
  265. package/sdk/dist/node/gitvault-restore.d.ts +1 -1
  266. package/sdk/dist/node/gitvault-restore.js +1 -1
  267. package/sdk/dist/node/index.d.ts +16 -16
  268. package/sdk/dist/node/index.d.ts.map +1 -1
  269. package/sdk/dist/node/index.js +15 -15
  270. package/sdk/dist/node/index.js.map +1 -1
  271. package/sdk/dist/node/manifest-export.js +2 -2
  272. package/sdk/dist/node/manifest-export.js.map +1 -1
  273. package/sdk/dist/node/org-adopt.d.ts +49 -0
  274. package/sdk/dist/node/org-adopt.d.ts.map +1 -0
  275. package/sdk/dist/node/org-adopt.js +77 -0
  276. package/sdk/dist/node/org-adopt.js.map +1 -0
  277. package/sdk/dist/node/paid-fetch.d.ts +7 -7
  278. package/sdk/dist/node/paid-fetch.d.ts.map +1 -1
  279. package/sdk/dist/node/paid-fetch.js +54 -54
  280. package/sdk/dist/node/paid-fetch.js.map +1 -1
  281. package/sdk/dist/node/preflight.d.ts +1 -1
  282. package/sdk/dist/node/preflight.d.ts.map +1 -1
  283. package/sdk/dist/node/preflight.js +1 -1
  284. package/sdk/dist/node/preflight.js.map +1 -1
  285. package/sdk/dist/node/sites-node.js +1 -1
  286. package/sdk/dist/node/sites-node.js.map +1 -1
  287. package/sdk/dist/node/source-scan.d.ts +1 -1
  288. package/sdk/dist/node/source-scan.js +2 -2
  289. package/sdk/dist/node/source-scan.js.map +1 -1
  290. package/sdk/dist/scoped.d.ts +11 -11
  291. package/sdk/dist/scoped.d.ts.map +1 -1
  292. package/sdk/dist/scoped.js +5 -5
  293. package/sdk/dist/scoped.js.map +1 -1
  294. package/core-dist/operator-session.js +0 -154
  295. package/lib/allowance.mjs +0 -355
  296. package/lib/cloud.mjs +0 -256
  297. package/lib/core.mjs +0 -123
  298. package/lib/operator-proofs.mjs +0 -22
  299. package/lib/operator.mjs +0 -771
  300. package/sdk/core-dist/operator-session.js +0 -154
  301. package/sdk/dist/namespaces/allowance.d.ts +0 -79
  302. package/sdk/dist/namespaces/allowance.d.ts.map +0 -1
  303. package/sdk/dist/namespaces/allowance.js +0 -149
  304. package/sdk/dist/namespaces/allowance.js.map +0 -1
  305. package/sdk/dist/namespaces/operator-session.d.ts.map +0 -1
  306. package/sdk/dist/namespaces/operator-session.js.map +0 -1
  307. package/sdk/dist/namespaces/operator.d.ts +0 -359
  308. package/sdk/dist/namespaces/operator.d.ts.map +0 -1
  309. package/sdk/dist/namespaces/operator.js +0 -272
  310. package/sdk/dist/namespaces/operator.js.map +0 -1
  311. package/sdk/dist/node/bearer-claim-key.d.ts.map +0 -1
  312. package/sdk/dist/node/bearer-claim-key.js.map +0 -1
  313. package/sdk/dist/node/operator-claim.d.ts +0 -49
  314. package/sdk/dist/node/operator-claim.d.ts.map +0 -1
  315. package/sdk/dist/node/operator-claim.js +0 -77
  316. 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
+ }
package/lib/logs.mjs CHANGED
@@ -33,11 +33,11 @@ import {
33
33
  const HELP = `run402 logs — Fetch function logs by request id
34
34
 
35
35
  Usage:
36
- run402 logs --request-id <id> [<function> | --function <name>] [--project <id>] [options]
37
- run402 logs --function <name> [--project <id>] [options]
36
+ run402 logs --request-id <request_id> [<function> | --function <name>] [--project <project_id>] [options]
37
+ run402 logs --function <name> [--project <project_id>] [options]
38
38
 
39
39
  Required (one of):
40
- --request-id <id> A req_... id (the x-run402-request-id response header),
40
+ --request-id <request_id> A req_... id (the x-run402-request-id response header),
41
41
  or a fnrun_... / fnatt_... durable-run id. Without
42
42
  --function, every function in the project is scanned.
43
43
  --function <name> Limit to one function. Alone (no --request-id) this
@@ -45,7 +45,7 @@ Required (one of):
45
45
  <function> means the same thing.
46
46
 
47
47
  Optional:
48
- --project <id> Project id (default: \$RUN402_PROJECT_ID, else the active project)
48
+ --project <project_id> Project id (default: \$RUN402_PROJECT_ID, else the active project)
49
49
  --tail <n> Max entries per function BEFORE the origin filter (default 100, max 1000)
50
50
  --since <ts> ISO timestamp or epoch ms; only entries at or after this
51
51
  --app Only the function's own output (default). Lambda runtime
@@ -77,7 +77,7 @@ Tip: the request id appears in:
77
77
  - The 'x-run402-request-id' response header on every function response
78
78
  - The 'requestId' field of any R402_SSR_RUNTIME_ERROR envelope
79
79
  - The 'request_id' field in deploy / cache invalidate result envelopes
80
- - The 'samples' of 'run402 errors' (each sample names a runnable logs command)
80
+ - The 'samples' of 'run402 errors list' (each sample names a runnable logs command)
81
81
  `;
82
82
 
83
83
  const VALUE_FLAGS = ["--request-id", "--function", "--project", "--tail", "--since"];
@@ -103,7 +103,7 @@ export async function run(sub, args = []) {
103
103
  fail({
104
104
  code: "BAD_USAGE",
105
105
  message: `Unexpected argument: ${positionals[1]}`,
106
- hint: "run402 logs [<function>] --request-id <id> [--project <id>]",
106
+ hint: "run402 logs [<function>] --request-id <request_id> [--project <project_id>]",
107
107
  details: { argument: positionals[1] },
108
108
  });
109
109
  }
@@ -119,7 +119,7 @@ export async function run(sub, args = []) {
119
119
  if (!requestId && !fnName) {
120
120
  fail({
121
121
  code: "BAD_USAGE",
122
- message: "Missing --request-id <id> (or --function <name> for a plain tail).",
122
+ message: "Missing --request-id <request_id> (or --function <name> for a plain tail).",
123
123
  hint: "Pass the request id from the 'x-run402-request-id' response header.",
124
124
  });
125
125
  }
@@ -130,7 +130,7 @@ export async function run(sub, args = []) {
130
130
  fail({
131
131
  code: "BAD_USAGE",
132
132
  message: "Missing project id.",
133
- hint: "Pass --project <id>, set RUN402_PROJECT_ID, or 'run402 projects use <id>'.",
133
+ hint: "Pass --project <project_id>, set RUN402_PROJECT_ID, or 'run402 projects use <project_id>'.",
134
134
  });
135
135
  }
136
136
 
package/lib/messages.mjs CHANGED
@@ -42,15 +42,15 @@ const ROOM_FLAGS = ["--project", "--org", "--room"];
42
42
  const HELP = `run402 messages — room-visible messages between agents
43
43
 
44
44
  Usage:
45
- run402 messages send <body> [--to <names>] [--ack] [--thread <id>]
46
- run402 messages list [--unread] [--cursor <mcr_...>] [--thread <id>]
47
- run402 messages wait [--addressed-to me] [--thread <id>] [--timeout <s>] [--cursor <mcr_...>]
45
+ run402 messages send <body> [--to <names>] [--ack] [--thread <thread>]
46
+ run402 messages list [--unread] [--cursor <mcr_...>] [--thread <thread>]
47
+ run402 messages wait [--addressed-to me] [--thread <thread>] [--timeout <s>] [--cursor <mcr_...>]
48
48
  run402 messages get <message_id>
49
49
  run402 messages ack <message_id>
50
50
 
51
51
  Addressing (all subcommands):
52
- --project <id> That project's DEFAULT room (the room key IS the project id)
53
- --org <id> --room <key> A named org room
52
+ --project <project_id> That project's DEFAULT room (the room key IS the project id)
53
+ --org <org_id> --room <key> A named org room
54
54
  (omit both) Resolved from RUN402_ROOM, a .run402.json binding, or the
55
55
  wallet profile's selected org
56
56
 
@@ -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", {
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.",
91
+ export function setOrgSlugAction() {
92
+ return nextAction("set_org_slug", {
93
+ command: "run402 orgs slug <slug>",
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
  }
@@ -11,7 +11,7 @@
11
11
  * organization named directly outranks one derived from a project named in that
12
12
  * same class:
13
13
  *
14
- * 1. flag --org <org_id> else --project <id> -> its org
14
+ * 1. flag --org <org_id> else --project <project_id> -> its org
15
15
  * 2. environment RUN402_ORG, else the org else RUN402_PROJECT_ID -> its org
16
16
  * half of RUN402_ROOM
17
17
  * 3. binding the `org` key of the nearest .run402(.local).json
@@ -57,7 +57,7 @@ export const ROOM_ENV = "RUN402_ROOM";
57
57
  export const PROJECT_ENV = "RUN402_PROJECT_ID";
58
58
 
59
59
  /** Command groups that must stay usable while selection is ambiguous. */
60
- const CONFLICT_EXEMPT = new Set(["org", "wallets", "doctor"]);
60
+ const CONFLICT_EXEMPT = new Set(["orgs", "wallets", "doctor"]);
61
61
 
62
62
  /** `org_id` is a UUID at every API boundary (`uuidParam` on every route). */
63
63
  const ORG_ID_RE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
@@ -78,7 +78,7 @@ function assertOrgIdShape(orgId, origin) {
78
78
  fail({
79
79
  code: "BAD_ORG_ID",
80
80
  message: `Invalid organization id ${JSON.stringify(describeRejectedValue(orgId))} (from ${origin}).`,
81
- hint: "An org_id is a UUID. Run 'run402 org list' to see the organizations you belong to.",
81
+ hint: "An org_id is a UUID. Run 'run402 orgs list' to see the organizations you belong to.",
82
82
  details: { org_id: describeRejectedValue(orgId), origin },
83
83
  next_actions: [listOrgsAction()],
84
84
  });
@@ -118,7 +118,7 @@ async function readGitvaultPinnedOrgId(cwd) {
118
118
 
119
119
  function listOrgsAction() {
120
120
  return nextAction("edit_request", {
121
- command: "run402 org list",
121
+ command: "run402 orgs list",
122
122
  why: "List the organizations this wallet belongs to.",
123
123
  });
124
124
  }
@@ -126,7 +126,7 @@ function listOrgsAction() {
126
126
  function orgRequiredActions() {
127
127
  return [
128
128
  nextAction("edit_request", {
129
- command: "run402 org use <org_id>",
129
+ command: "run402 orgs use <org_id>",
130
130
  why: "Select a current organization for this wallet profile.",
131
131
  }),
132
132
  listOrgsAction(),
@@ -266,7 +266,7 @@ export async function resolveOrg(input = {}, opts = {}) {
266
266
  // --- Class 4: profile state ----------------------------------------------
267
267
  const selected = trimmed(coreGetActiveOrgId());
268
268
  if (selected) {
269
- return { orgId: assertOrgIdShape(selected, "org use"), source: "profile", sourceDetail: "org use" };
269
+ return { orgId: assertOrgIdShape(selected, "orgs use"), source: "profile", sourceDetail: "orgs use" };
270
270
  }
271
271
  const activeProject = trimmed(getActiveProjectId());
272
272
  if (activeProject) {
@@ -278,7 +278,7 @@ export async function resolveOrg(input = {}, opts = {}) {
278
278
  fail({
279
279
  code: "ORG_REQUIRED",
280
280
  message: "No organization specified and no current organization set.",
281
- hint: `Pass --org <org_id>, set ${ORG_ENV}, bind this directory in .run402.json, or run: run402 org use <org_id>`,
281
+ hint: `Pass --org <org_id>, set ${ORG_ENV}, bind this directory in .run402.json, or run: run402 orgs use <org_id>`,
282
282
  next_actions: orgRequiredActions(),
283
283
  });
284
284
  return null; // unreachable — fail() exits
@@ -301,17 +301,17 @@ export async function resolveOrgId(a, opts = {}) {
301
301
  * Every Org-Scoped Command").
302
302
  *
303
303
  * `<org_id>` is optional sugar on every verb that acts on an organization —
304
- * `org get`, `org member add`, `billing link-wallet`, … A leading positional
304
+ * `orgs get`, `orgs members add`, `billing link-wallet`, … A leading positional
305
305
  * that IS an org id (a UUID) addresses that org; anything else is the verb's
306
306
  * own next positional (a wallet, an email, a principal) and the org comes from
307
307
  * the shared chain: `--org`, then `RUN402_ORG`, then the `.run402.json`
308
- * binding, then `org use`. So inside a bound checkout the two-agent case reads
309
- * `run402 org member add 0xB… --role developer` with nothing else to know.
308
+ * binding, then `orgs use`. So inside a bound checkout the two-agent case reads
309
+ * `run402 orgs members add 0xB… --role developer` with nothing else to know.
310
310
  *
311
311
  * Naming the org twice with different values (positional AND `--org`) is
312
312
  * `AMBIGUOUS_ORG`, never a silent pick — the same refusal the env-vs-binding
313
313
  * pair gets. A non-org first positional with no chain answer fails
314
- * `ORG_REQUIRED` and NAMES the rejected value, so `org get foo` says why `foo`
314
+ * `ORG_REQUIRED` and NAMES the rejected value, so `orgs get foo` says why `foo`
315
315
  * did not count rather than reporting a bare "no organization".
316
316
  *
317
317
  * Returns the org id with provenance plus the REMAINING positionals; bound
@@ -350,7 +350,7 @@ export async function takeOrgPositional(a, valueFlags = [], opts = {}) {
350
350
  message: typeof first === "string"
351
351
  ? `No organization specified and no current organization set (${JSON.stringify(first)} is not an org_id — an org_id is a UUID).`
352
352
  : "No organization specified and no current organization set.",
353
- hint: `Pass --org <org_id> (or a leading <org_id> positional), set ${ORG_ENV}, bind this directory in .run402.json, or run: run402 org use <org_id>`,
353
+ hint: `Pass --org <org_id> (or a leading <org_id> positional), set ${ORG_ENV}, bind this directory in .run402.json, or run: run402 orgs use <org_id>`,
354
354
  ...(typeof first === "string" ? { details: { rejected_positional: first } } : {}),
355
355
  next_actions: orgRequiredActions(),
356
356
  });
@@ -388,7 +388,7 @@ export function getSelectedOrgId() {
388
388
  }
389
389
 
390
390
  /** Record the profile's selected organization. */
391
- export function setSelectedOrgId(orgId, origin = "org use") {
391
+ export function setSelectedOrgId(orgId, origin = "orgs use") {
392
392
  coreSetActiveOrgId(assertOrgIdShape(orgId, origin));
393
393
  }
394
394