run402 4.97.0 → 4.99.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 (163) hide show
  1. package/README.md +1 -1
  2. package/core-dist/binding-file.js +28 -1
  3. package/core-dist/wallet.js +3 -3
  4. package/lib/auth.mjs +1 -2
  5. package/lib/buzz-doctor.mjs +21 -806
  6. package/lib/buzz-doctor.test.mjs +7 -6
  7. package/lib/buzz.test.mjs +13 -2
  8. package/lib/doctor.mjs +72 -811
  9. package/lib/init.mjs +35 -576
  10. package/lib/org-context.mjs +39 -376
  11. package/lib/orgs.mjs +22 -106
  12. package/lib/projects.mjs +1 -2
  13. package/lib/repos.mjs +6 -6
  14. package/lib/rooms.mjs +1 -2
  15. package/lib/sdk-errors.mjs +26 -0
  16. package/lib/status.mjs +14 -154
  17. package/lib/up.mjs +1 -2
  18. package/lib/vault-scaffold.mjs +2 -3
  19. package/lib/vault-target.mjs +14 -141
  20. package/lib/wallet-context.mjs +61 -227
  21. package/lib/wallets.mjs +47 -310
  22. package/package.json +1 -2
  23. package/sdk/core-dist/binding-file.js +28 -1
  24. package/sdk/core-dist/wallet.js +3 -3
  25. package/sdk/dist/errors.d.ts +24 -2
  26. package/sdk/dist/errors.d.ts.map +1 -1
  27. package/sdk/dist/errors.js +44 -2
  28. package/sdk/dist/errors.js.map +1 -1
  29. package/sdk/dist/index.d.ts +18 -3
  30. package/sdk/dist/index.d.ts.map +1 -1
  31. package/sdk/dist/index.js +12 -1
  32. package/sdk/dist/index.js.map +1 -1
  33. package/sdk/dist/kernel.d.ts +34 -0
  34. package/sdk/dist/kernel.d.ts.map +1 -1
  35. package/sdk/dist/kernel.js +15 -1
  36. package/sdk/dist/kernel.js.map +1 -1
  37. package/sdk/dist/namespaces/admin.d.ts.map +1 -1
  38. package/sdk/dist/namespaces/admin.js +8 -0
  39. package/sdk/dist/namespaces/admin.js.map +1 -1
  40. package/sdk/dist/namespaces/agent.d.ts +2 -2
  41. package/sdk/dist/namespaces/agent.d.ts.map +1 -1
  42. package/sdk/dist/namespaces/agent.js +4 -0
  43. package/sdk/dist/namespaces/agent.js.map +1 -1
  44. package/sdk/dist/namespaces/agent.types.d.ts +5 -0
  45. package/sdk/dist/namespaces/agent.types.d.ts.map +1 -1
  46. package/sdk/dist/namespaces/apps.d.ts.map +1 -1
  47. package/sdk/dist/namespaces/apps.js +2 -0
  48. package/sdk/dist/namespaces/apps.js.map +1 -1
  49. package/sdk/dist/namespaces/auth.d.ts.map +1 -1
  50. package/sdk/dist/namespaces/auth.js +9 -0
  51. package/sdk/dist/namespaces/auth.js.map +1 -1
  52. package/sdk/dist/namespaces/branches.d.ts.map +1 -1
  53. package/sdk/dist/namespaces/branches.js +2 -0
  54. package/sdk/dist/namespaces/branches.js.map +1 -1
  55. package/sdk/dist/namespaces/ci.d.ts.map +1 -1
  56. package/sdk/dist/namespaces/ci.js +2 -0
  57. package/sdk/dist/namespaces/ci.js.map +1 -1
  58. package/sdk/dist/namespaces/credentials.d.ts +0 -23
  59. package/sdk/dist/namespaces/credentials.d.ts.map +1 -1
  60. package/sdk/dist/namespaces/credentials.js +29 -0
  61. package/sdk/dist/namespaces/credentials.js.map +1 -1
  62. package/sdk/dist/namespaces/escalations.d.ts.map +1 -1
  63. package/sdk/dist/namespaces/escalations.js +2 -0
  64. package/sdk/dist/namespaces/escalations.js.map +1 -1
  65. package/sdk/dist/namespaces/grants.d.ts.map +1 -1
  66. package/sdk/dist/namespaces/grants.js +4 -0
  67. package/sdk/dist/namespaces/grants.js.map +1 -1
  68. package/sdk/dist/namespaces/live.d.ts +0 -16
  69. package/sdk/dist/namespaces/live.d.ts.map +1 -1
  70. package/sdk/dist/namespaces/live.js +19 -0
  71. package/sdk/dist/namespaces/live.js.map +1 -1
  72. package/sdk/dist/namespaces/me.d.ts.map +1 -1
  73. package/sdk/dist/namespaces/me.js +3 -0
  74. package/sdk/dist/namespaces/me.js.map +1 -1
  75. package/sdk/dist/namespaces/org.d.ts.map +1 -1
  76. package/sdk/dist/namespaces/org.js +3 -0
  77. package/sdk/dist/namespaces/org.js.map +1 -1
  78. package/sdk/dist/namespaces/projects.d.ts.map +1 -1
  79. package/sdk/dist/namespaces/projects.js +5 -0
  80. package/sdk/dist/namespaces/projects.js.map +1 -1
  81. package/sdk/dist/namespaces/repos.d.ts.map +1 -1
  82. package/sdk/dist/namespaces/repos.js +5 -0
  83. package/sdk/dist/namespaces/repos.js.map +1 -1
  84. package/sdk/dist/namespaces/rooms.d.ts.map +1 -1
  85. package/sdk/dist/namespaces/rooms.js +3 -0
  86. package/sdk/dist/namespaces/rooms.js.map +1 -1
  87. package/sdk/dist/namespaces/session.d.ts.map +1 -1
  88. package/sdk/dist/namespaces/session.js +18 -0
  89. package/sdk/dist/namespaces/session.js.map +1 -1
  90. package/sdk/dist/namespaces/transfers.d.ts.map +1 -1
  91. package/sdk/dist/namespaces/transfers.js +3 -0
  92. package/sdk/dist/namespaces/transfers.js.map +1 -1
  93. package/sdk/dist/namespaces/wallets.d.ts.map +1 -1
  94. package/sdk/dist/namespaces/wallets.js +2 -0
  95. package/sdk/dist/namespaces/wallets.js.map +1 -1
  96. package/sdk/dist/namespaces/write-approval.d.ts.map +1 -1
  97. package/sdk/dist/namespaces/write-approval.js +3 -0
  98. package/sdk/dist/namespaces/write-approval.js.map +1 -1
  99. package/sdk/dist/node/actions-node.d.ts.map +1 -1
  100. package/sdk/dist/node/actions-node.js +3 -0
  101. package/sdk/dist/node/actions-node.js.map +1 -1
  102. package/sdk/dist/node/buzz-doctor-contract.d.ts +155 -0
  103. package/sdk/dist/node/buzz-doctor-contract.d.ts.map +1 -0
  104. package/sdk/dist/node/buzz-doctor-contract.js +385 -0
  105. package/sdk/dist/node/buzz-doctor-contract.js.map +1 -0
  106. package/sdk/dist/node/buzz-doctor.d.ts +155 -0
  107. package/sdk/dist/node/buzz-doctor.d.ts.map +1 -0
  108. package/sdk/dist/node/buzz-doctor.js +810 -0
  109. package/sdk/dist/node/buzz-doctor.js.map +1 -0
  110. package/sdk/dist/node/credentials.d.ts +9 -4
  111. package/sdk/dist/node/credentials.d.ts.map +1 -1
  112. package/sdk/dist/node/credentials.js +1 -1
  113. package/sdk/dist/node/credentials.js.map +1 -1
  114. package/sdk/dist/node/diagnostics.d.ts +45 -0
  115. package/sdk/dist/node/diagnostics.d.ts.map +1 -0
  116. package/sdk/dist/node/diagnostics.js +67 -0
  117. package/sdk/dist/node/diagnostics.js.map +1 -0
  118. package/sdk/dist/node/doctor.d.ts +101 -0
  119. package/sdk/dist/node/doctor.d.ts.map +1 -0
  120. package/sdk/dist/node/doctor.js +618 -0
  121. package/sdk/dist/node/doctor.js.map +1 -0
  122. package/sdk/dist/node/index.d.ts +57 -5
  123. package/sdk/dist/node/index.d.ts.map +1 -1
  124. package/sdk/dist/node/index.js +36 -1
  125. package/sdk/dist/node/index.js.map +1 -1
  126. package/sdk/dist/node/init.d.ts +137 -0
  127. package/sdk/dist/node/init.d.ts.map +1 -0
  128. package/sdk/dist/node/init.js +507 -0
  129. package/sdk/dist/node/init.js.map +1 -0
  130. package/sdk/dist/node/lightning-wallet.d.ts +61 -0
  131. package/sdk/dist/node/lightning-wallet.d.ts.map +1 -0
  132. package/sdk/dist/node/lightning-wallet.js +126 -0
  133. package/sdk/dist/node/lightning-wallet.js.map +1 -0
  134. package/sdk/dist/node/local-actions.d.ts +23 -0
  135. package/sdk/dist/node/local-actions.d.ts.map +1 -0
  136. package/sdk/dist/node/local-actions.js +38 -0
  137. package/sdk/dist/node/local-actions.js.map +1 -0
  138. package/sdk/dist/node/org-context.d.ts +169 -0
  139. package/sdk/dist/node/org-context.d.ts.map +1 -0
  140. package/sdk/dist/node/org-context.js +403 -0
  141. package/sdk/dist/node/org-context.js.map +1 -0
  142. package/sdk/dist/node/status.d.ts +62 -0
  143. package/sdk/dist/node/status.d.ts.map +1 -0
  144. package/sdk/dist/node/status.js +132 -0
  145. package/sdk/dist/node/status.js.map +1 -0
  146. package/sdk/dist/node/vault-target.d.ts +49 -0
  147. package/sdk/dist/node/vault-target.d.ts.map +1 -0
  148. package/sdk/dist/node/vault-target.js +109 -0
  149. package/sdk/dist/node/vault-target.js.map +1 -0
  150. package/sdk/dist/node/wallets.d.ts +242 -0
  151. package/sdk/dist/node/wallets.d.ts.map +1 -0
  152. package/sdk/dist/node/wallets.js +484 -0
  153. package/sdk/dist/node/wallets.js.map +1 -0
  154. package/sdk/dist/secret-gate.d.ts +377 -0
  155. package/sdk/dist/secret-gate.d.ts.map +1 -0
  156. package/sdk/dist/secret-gate.js +273 -0
  157. package/sdk/dist/secret-gate.js.map +1 -0
  158. package/vault-surface.json +1 -1
  159. package/fixtures/buzz-v0.5.2-cli-capabilities.json +0 -92
  160. package/fixtures/run402-buzz-doctor-v1-contract.json +0 -140
  161. package/lib/buzz-doctor-contract.mjs +0 -97
  162. package/lib/buzz-doctor-contract.test.mjs +0 -134
  163. package/lib/lightning-wallet.mjs +0 -115
package/lib/repos.mjs CHANGED
@@ -21,7 +21,7 @@ import { reportSdkError, fail } from "./sdk-errors.mjs";
21
21
  import { withAutoApprove } from "./sign-in.mjs";
22
22
  import { walletAuthHeaders, isCoreApiTarget, readWallet, resolveProjectId } from "./config.mjs";
23
23
  import { loadLiveControlPlaneSession } from "../core-dist/control-plane-session.js";
24
- import { resolveOrgId, resolveOwningOrgId } from "./org-context.mjs";
24
+ import { resolveOrgId } from "./org-context.mjs";
25
25
  import { resolveVaultTarget } from "./vault-target.mjs";
26
26
  import { nextAction, setOrgSlugAction, setRepoNameAction } from "./next-actions.mjs";
27
27
  import { resolveHarnessLabels, resolveSessionKey, resolveTaskLabel, persistSessionKey } from "./harness-context.mjs";
@@ -845,7 +845,7 @@ async function printCreateResult({ sdk, projectId, vault, adopted, name, verbose
845
845
  let address = null;
846
846
  let orgSlug = null;
847
847
  try {
848
- const owningOrg = await resolveOwningOrgId(projectId);
848
+ const owningOrg = await getSdk().orgs.owningOrgOf(projectId);
849
849
  const orgRecord = owningOrg ? await sdk.org(owningOrg).get() : null;
850
850
  orgSlug = orgRecord?.slug ?? null;
851
851
  if (orgSlug && name) {
@@ -929,7 +929,7 @@ async function printCreateResult({ sdk, projectId, vault, adopted, name, verbose
929
929
 
930
930
  async function createAdopt(projectId, dir, a) {
931
931
  const sdk = getSdk();
932
- const orgId = flagValue(a, "--org") ?? await resolveOwningOrgId(projectId);
932
+ const orgId = flagValue(a, "--org") ?? await getSdk().orgs.owningOrgOf(projectId);
933
933
  if (!orgId) {
934
934
  fail({
935
935
  code: "VAULT_ORG_UNRESOLVED",
@@ -1010,7 +1010,7 @@ async function createProvision(name, dir, a) {
1010
1010
  }
1011
1011
  }
1012
1012
 
1013
- const effectiveOrgId = orgId ?? (await resolveOwningOrgId(provisioned.project_id));
1013
+ const effectiveOrgId = orgId ?? (await getSdk().orgs.owningOrgOf(provisioned.project_id));
1014
1014
  if (!effectiveOrgId) {
1015
1015
  fail({
1016
1016
  code: "VAULT_ORG_UNRESOLVED",
@@ -1278,7 +1278,7 @@ async function rename(args) {
1278
1278
  const result = await sdk.projects.setRepoName(projectId, repoName);
1279
1279
  let address = null;
1280
1280
  try {
1281
- const owningOrg = await resolveOwningOrgId(projectId);
1281
+ const owningOrg = await getSdk().orgs.owningOrgOf(projectId);
1282
1282
  const orgSlug = owningOrg ? (await sdk.org(owningOrg).get()).slug : null;
1283
1283
  if (orgSlug) address = vaultRemoteUrlForRepo(orgSlug, result.repo_name);
1284
1284
  } catch {
@@ -1554,7 +1554,7 @@ async function capture(args) {
1554
1554
  const repoDir = process.cwd();
1555
1555
  const address = await detectSlugFormRemote(a, repoDir);
1556
1556
  const target = address ? { repo_dir: repoDir } : await vaultTarget(a);
1557
- const orgId = !address && !dryRun && target.project_id ? await resolveOwningOrgId(target.project_id) : null;
1557
+ const orgId = !address && !dryRun && target.project_id ? await getSdk().orgs.owningOrgOf(target.project_id) : null;
1558
1558
  const opts = {
1559
1559
  ...target,
1560
1560
  ...(address ? { address } : {}),
package/lib/rooms.mjs CHANGED
@@ -448,8 +448,7 @@ async function joinWithKey(key, a) {
448
448
  // Arrival state (design D10) — best-effort throughout: the redemption
449
449
  // already succeeded, and none of this may fail a completed join.
450
450
  try {
451
- const { setSelectedOrgId } = await import("./org-context.mjs");
452
- setSelectedOrgId(result.org_id);
451
+ await getSdk().orgs.use(result.org_id);
453
452
  } catch { /* best-effort */ }
454
453
 
455
454
  const cwd = process.cwd();
@@ -44,6 +44,32 @@ export function fail({ message, code, hint, details, next_actions, field, retrya
44
44
  process.exit(exit_code);
45
45
  }
46
46
 
47
+ /**
48
+ * Report a refusal the Node SDK's local-state verbs threw (wallet selection,
49
+ * organization context, doctor/init argument checks): the same envelope
50
+ * `fail()` prints, fields taken from the error. Local refusals carry their
51
+ * own `hint` and `next_actions`; they never went through the HTTP-error
52
+ * rendering `reportSdkError` applies.
53
+ */
54
+ export function failLocal(err) {
55
+ fail({
56
+ code: err?.code,
57
+ message: err?.message,
58
+ hint: err?.hint,
59
+ details: err?.details,
60
+ next_actions: err?.nextActions,
61
+ });
62
+ }
63
+
64
+ /**
65
+ * `failLocal` for a local refusal from a local-state verb, `reportSdkError`
66
+ * for anything else (a gateway error, a network error, a cache miss).
67
+ */
68
+ export function reportLocalOrSdkError(err) {
69
+ if (err?.kind === "local_error" && err?.status === null && err?.code !== "PROJECT_CREDENTIAL_NOT_FOUND") failLocal(err);
70
+ reportSdkError(err);
71
+ }
72
+
47
73
  /**
48
74
  * Parse a JSON-bearing CLI flag value, naming the flag in the failure envelope.
49
75
  *
package/lib/status.mjs CHANGED
@@ -1,17 +1,12 @@
1
- import { readRemoteStatus } from "#sdk/node";
2
- import {
3
- readWallet,
4
- loadKeyStore,
5
- getActiveProjectId,
6
- apiBase,
7
- apiBaseSource,
8
- apiTargetKind,
9
- } from "./config.mjs";
1
+ /**
2
+ * run402 status — the CLI edge of `r.status()` (`@run402/sdk/node`), which
3
+ * assembles the local view (wallet, rail, balances, tier, projects, active
4
+ * project, API target). This module prints it: JSON by default, or the
5
+ * compact `--human` rendering.
6
+ */
10
7
  import { getSdk } from "./sdk.mjs";
11
- import { fail } from "./sdk-errors.mjs";
8
+ import { fail, reportLocalOrSdkError } from "./sdk-errors.mjs";
12
9
  import { assertKnownFlags, hasHelp, normalizeArgv } from "./argparse.mjs";
13
- import { getActiveProfile } from "../core-dist/config.js";
14
- import { readMeta } from "../core-dist/profiles.js";
15
10
 
16
11
  const HELP = `run402 status — Show full organization state in one shot
17
12
 
@@ -35,63 +30,6 @@ Run402 Cloud status requires a wallet; Core target status can still report
35
30
  local project state without one.
36
31
  `;
37
32
 
38
- // USDC / pathUSD constants (match wallet.mjs)
39
- const USDC_ABI = [{ name: "balanceOf", type: "function", stateMutability: "view", inputs: [{ name: "account", type: "address" }], outputs: [{ name: "", type: "uint256" }] }];
40
- const USDC_MAINNET = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
41
- const USDC_SEPOLIA = "0x036CbD53842c5426634e7929541eC2318f3dCF7e";
42
- const PATH_USD = "0x20c0000000000000000000000000000000000000";
43
- const TEMPO_RPC = "https://rpc.moderato.tempo.xyz/";
44
-
45
- /**
46
- * Read the on-chain wallet balance in USD micros for the current rail.
47
- * For x402: read Base mainnet + Base Sepolia USDC and sum funded networks.
48
- * For mpp: read pathUSD on Tempo Moderato.
49
- * Returns null if every read fails (e.g. offline or RPC down).
50
- */
51
- async function readWalletBalanceUsdMicros(rail, address) {
52
- try {
53
- const { createPublicClient, http, defineChain } = await import("viem");
54
- if (rail === "mpp") {
55
- const tempoModerato = defineChain({
56
- id: 42431,
57
- name: "Tempo Moderato",
58
- nativeCurrency: { name: "pathUSD", symbol: "pathUSD", decimals: 6 },
59
- rpcUrls: { default: { http: [TEMPO_RPC] } },
60
- });
61
- const client = createPublicClient({ chain: tempoModerato, transport: http() });
62
- try {
63
- const raw = await client.readContract({ address: PATH_USD, abi: USDC_ABI, functionName: "balanceOf", args: [address] });
64
- return Number(raw);
65
- } catch { return null; }
66
- }
67
- // x402 rail — read Base mainnet + Base Sepolia in parallel; sum any that succeed.
68
- const { base, baseSepolia } = await import("viem/chains");
69
- const mainnetClient = createPublicClient({ chain: base, transport: http() });
70
- const sepoliaClient = createPublicClient({ chain: baseSepolia, transport: http() });
71
- const [mainnet, sepolia] = await Promise.all([
72
- mainnetClient.readContract({ address: USDC_MAINNET, abi: USDC_ABI, functionName: "balanceOf", args: [address] }).then(Number).catch(() => null),
73
- sepoliaClient.readContract({ address: USDC_SEPOLIA, abi: USDC_ABI, functionName: "balanceOf", args: [address] }).then(Number).catch(() => null),
74
- ]);
75
- if (mainnet === null && sepolia === null) return null;
76
- return (mainnet || 0) + (sepolia || 0);
77
- } catch {
78
- return null;
79
- }
80
- }
81
-
82
- /**
83
- * Normalize a project entry to the agreed-on shape: always expose `project_id`
84
- * (matching `projects list`). The remote /wallets/v1/:wallet/projects endpoint
85
- * returns entries keyed as `id`, so we map them here and drop the raw `id`
86
- * field to avoid having two aliases for the same identity.
87
- */
88
- function normalizeProject(raw) {
89
- if (!raw || typeof raw !== "object") return raw;
90
- const projectId = raw.project_id || raw.id;
91
- const { id: _dropId, project_id: _dropPid, ...rest } = raw;
92
- return { project_id: projectId, ...rest };
93
- }
94
-
95
33
  export async function run(args = []) {
96
34
  args = normalizeArgv(args);
97
35
  if (hasHelp(args)) { console.log(HELP); process.exit(0); }
@@ -105,97 +43,19 @@ export async function run(args = []) {
105
43
  hint: "JSON is the default; drop --json to keep it, or keep --human alone for the rendered view.",
106
44
  });
107
45
  }
108
- const localWallet = readWallet();
109
- const target = {
110
- api_base: apiBase(),
111
- api_base_source: apiBaseSource(),
112
- kind: apiTargetKind(),
113
- };
114
- if (!localWallet) {
115
- const store = loadKeyStore();
116
- const bare = {
117
- wallet: null,
118
- target,
119
- projects: Object.keys(store.projects).map(id => ({ project_id: id })),
120
- active_project: getActiveProjectId() || null,
121
- hint: target.kind === "core" ? "Run: run402 projects provision --name my-app" : "Run: run402 init",
122
- };
123
- if (human) {
124
- console.log(formatStatusHuman(bare));
125
- return;
126
- }
127
- console.log(JSON.stringify(bare));
46
+ let result;
47
+ try {
48
+ result = await getSdk().status();
49
+ } catch (err) {
50
+ reportLocalOrSdkError(err);
128
51
  return;
129
52
  }
130
-
131
- const wallet = localWallet.address.toLowerCase();
132
- const rail = localWallet.rail || "x402";
133
-
134
- // Parallel API calls: tier + allowance + server-side projects + on-chain wallet balance.
135
- // projects.list() is the membership-scoped named inventory (project-findability);
136
- // SIWX wallet auth is signed from the wallet. Best-effort — a missing
137
- // wallet yields null and we fall back to the local keystore below.
138
- const [status, walletBalance] = await Promise.all([
139
- readRemoteStatus(getSdk(), wallet),
140
- readWalletBalanceUsdMicros(rail, localWallet.address),
141
- ]);
142
- const { tier, billing, remote } = status;
143
-
144
- // Local keystore
145
- const store = loadKeyStore();
146
- const activeId = getActiveProjectId();
147
-
148
- const projects = remote?.projects
149
- ? remote.projects.map(normalizeProject)
150
- : Object.keys(store.projects).map(id => ({ project_id: id }));
151
-
152
- // Which named wallet this state belongs to. `local_label` is the active
153
- // profile (default for single-wallet installs); `server_label` is the
154
- // cached server-side display name (null until set / when offline).
155
- const walletName = getActiveProfile();
156
- const walletMeta = readMeta(walletName);
157
-
158
- // Balances are grouped under one object so the on-chain (wallet) and
159
- // allowance numbers are unambiguous and rail-legible.
160
- // - on_chain_usd_micros / on_chain_token: on-chain USDC (x402) or pathUSD
161
- // (mpp) in the wallet, null if the RPC read failed
162
- // - allowance_usd_micros / held_usd_micros: the organization's Run402-held
163
- // allowance, rail-independent, null when no organization exists
164
- const hasBilling = billing && billing.exists !== false;
165
- const result = {
166
- wallet: {
167
- local_label: walletName,
168
- server_label: walletMeta?.label ?? null,
169
- address: localWallet.address,
170
- },
171
- rail,
172
- remote_status: status.availability,
173
- next_actions: status.next_actions,
174
- projects_source: remote ? "remote" : "local_cache",
175
- balances: {
176
- on_chain_usd_micros: walletBalance,
177
- on_chain_token: rail === "mpp" ? "pathUSD" : "USDC",
178
- allowance_usd_micros: hasBilling ? billing.allowance_usd_micros : null,
179
- held_usd_micros: hasBilling ? (billing.held_usd_micros ?? 0) : null,
180
- },
181
- tier: tier && tier.tier
182
- ? { name: tier.tier, status: tier.status, expires: tier.lease_perpetual === true ? null : tier.lease_expires_at }
183
- : null,
184
- // v1.57: lifecycle state and the per-organization escape hatch moved to the
185
- // organization. Surface them at the top level so agents don't have to
186
- // dig into the projects array to read them.
187
- organization_lifecycle_state: tier?.organization_lifecycle_state ?? null,
188
- lease_perpetual: tier?.lease_perpetual ?? null,
189
- projects,
190
- active_project: activeId || null,
191
- target,
192
- };
193
-
194
53
  if (human) {
195
54
  console.log(formatStatusHuman(result));
196
55
  return;
197
56
  }
198
- console.log(JSON.stringify(result, null, 2));
57
+ // The no-wallet view has always printed compact; the wallet view pretty.
58
+ console.log(result.wallet ? JSON.stringify(result, null, 2) : JSON.stringify(result));
199
59
  }
200
60
 
201
61
  function usdFromMicros(micros) {
package/lib/up.mjs CHANGED
@@ -689,9 +689,8 @@ async function gitInitIfNeeded(dir) {
689
689
  * never turn an otherwise-successful `up` into a failure.
690
690
  */
691
691
  async function composeRepoPushStep({ sdk, workDir, projectId, createdRepository, nested = false }) {
692
- const { resolveOwningOrgId } = await import("./org-context.mjs");
693
692
  const { scaffoldVaultRemote } = await import("./vault-scaffold.mjs");
694
- const orgId = await resolveOwningOrgId(projectId);
693
+ const orgId = await getSdk().orgs.owningOrgOf(projectId);
695
694
  // `--nested`: an app root INSIDE another repository (a monorepo workspace)
696
695
  // becomes its own repository with the encrypted remote — the enclosing
697
696
  // checkout only gains one local `.git/info/exclude` line. Without it the
@@ -24,13 +24,12 @@
24
24
  * throw.
25
25
  */
26
26
  import { getSdk } from "./sdk.mjs";
27
- import { resolveOwningOrgId } from "./org-context.mjs";
28
27
 
29
28
  /**
30
29
  * @param {object} options
31
30
  * @param {string} [options.repoDir] Working tree to scaffold. Defaults to `process.cwd()`.
32
31
  * @param {string} options.projectId The project the remote should point at.
33
- * @param {string} [options.orgId] Explicit owning org. Resolved via `resolveOwningOrgId` when omitted.
32
+ * @param {string} [options.orgId] Explicit owning org. Resolved via `r.orgs.owningOrgOf` when omitted.
34
33
  * @param {boolean} [options.createRepoIfMissing] Opt into `git init`-ing `repoDir` when it is not a repository yet.
35
34
  * @param {boolean} [options.nested] Scaffold `repoDir` as its OWN repository even when it lies inside another one (`Repos.scaffoldRemote`'s `nested`).
36
35
  * @param {string} [options.nestedCommand] The caller's own `--nested` spelling for the `create_nested_repo` next_action (default: the SDK's `run402 repos create --nested --project <project_id>`).
@@ -51,7 +50,7 @@ export async function scaffoldVaultRemote({ repoDir = process.cwd(), projectId,
51
50
  out.vault_skipped = "not a git repository — re-run with --git-remote to create one and add the remote";
52
51
  return out;
53
52
  }
54
- const resolvedOrgId = orgId ?? (await resolveOwningOrgId(projectId));
53
+ const resolvedOrgId = orgId ?? (await getSdk().orgs.owningOrgOf(projectId));
55
54
  if (!resolvedOrgId) {
56
55
  out.reason = "org_unresolved";
57
56
  out.vault_skipped = `could not resolve the owning org for ${projectId} — the run402 remote was not added`;
@@ -1,148 +1,21 @@
1
1
  /**
2
- * Shared "which vault does this repo mean" resolution for `run402 repos`
3
- * and `run402 doctor`'s vault check. When a verb runs standing inside a
4
- * repository that already names its own vault via a pinned repo id or a
5
- * run402/origin remote, targeting prefers that repo over the active-project
6
- * pointer — git muscle memory says a command run inside a repo acts on THAT
7
- * repo.
8
- *
9
- * Targeting order for a verb run standing inside a git repository, highest
10
- * first:
11
- * 1. an explicit --repo/--project flag (owned by each call site — this
12
- * module supplies only the fallback chain beneath it, plus the
13
- * mismatch warning against tier 3)
14
- * 2. the pinned `r402.repoId` in local git config — addresses the
15
- * vault by repo_id directly, no network read at all
16
- * 3. the repo's run402/origin remote address — id-form is parsed
17
- * directly out of the address string (free, no network); slug-form is
18
- * resolved via the SDK (one read-only network call — `resolveAddress`,
19
- * never a pin, never a push-to-create; this is a TARGETING read, not a
20
- * publish)
21
- * 4. RUN402_PROJECT_ID env
22
- * 5. the profile's active project
23
- *
24
- * Outside a repository (or when repo detection itself fails), only tiers 4
25
- * and 5 apply.
26
- *
27
- * ARCHITECTURAL NOTE: this is CLI-edge policy (which flag/env/file wins),
28
- * not vault protocol behavior — the same class of concern
29
- * `wallet-context.mjs` owns for wallet selection. The protocol reads it
30
- * composes (`readPinnedVaultRepo`, `resolveAddress`) already live once in
31
- * the SDK; this module adds no protocol logic of its own.
2
+ * Vault verb targeting when standing inside a repository: the CLI edge of
3
+ * `resolveVaultTarget` (`@run402/sdk/node`), which owns the order — an
4
+ * explicit `--repo`/`--project` > this checkout's id-pin > its own
5
+ * `run402`/`origin` remote > `RUN402_PROJECT_ID` > the active project.
6
+ * `run402 repos view|snapshot|policy|mirror|fsck|gc|access` and the doctor's
7
+ * vault check compose it; a disagreeing explicit flag still wins, with one
8
+ * stderr warning naming both.
32
9
  */
33
- import { getActiveProjectId } from "./config.mjs";
10
+ import { getSdk } from "./sdk.mjs";
11
+ import { repoOwnVaultTarget as sdkRepoOwnVaultTarget, resolveVaultTarget as sdkResolveVaultTarget } from "#sdk/node";
34
12
 
35
- /**
36
- * Tiers 4/5 (RUN402_PROJECT_ID env, then the active project) — deliberately
37
- * NON-throwing, unlike `config.mjs#resolveProjectId`. `run402 doctor`'s
38
- * vault check needs to report "skipped" gracefully when nothing resolves
39
- * anywhere (its pre-existing, tested behavior); a `fail()`-triggered
40
- * `process.exit()` here would abort doctor's ENTIRE report, not just this
41
- * one check. Callers that DO want the historical PROJECT_REQUIRED failure
42
- * (vault.mjs's own verbs) get it by falling back to `resolveProjectId`
43
- * themselves when this returns `null` — see `vaultTarget` in vault.mjs.
44
- */
45
- function envOrActiveProjectId() {
46
- return (process.env.RUN402_PROJECT_ID || "").trim() || getActiveProjectId() || null;
47
- }
48
-
49
- async function isInsideGitRepo(repoDir) {
50
- try {
51
- const { hardenedGit } = await import("#sdk/node");
52
- await hardenedGit(repoDir, ["rev-parse", "--git-dir"]);
53
- return true;
54
- } catch {
55
- return false;
56
- }
57
- }
58
-
59
- /**
60
- * The repo's OWN target, independent of any flag — the pin first, then the
61
- * `run402`/`origin` remote (in that order, mirroring `scaffoldRemote`'s own
62
- * naming precedence). `null` when the repo has neither, when this is not a
63
- * repository at all, or (slug-form only) the remote fails to resolve over
64
- * the network — a miss here is always ordinary, never thrown.
65
- */
13
+ /** This repository's OWN vault address (pin, else `run402`/`origin` remote), or null. */
66
14
  export async function repoOwnVaultTarget(repoDir) {
67
- if (!(await isInsideGitRepo(repoDir))) return null;
68
-
69
- const { hardenedGit, readPinnedVaultRepo } = await import("#sdk/node");
70
- const pinned = await readPinnedVaultRepo(repoDir);
71
- if (pinned) return { repo_id: pinned.repo_id, project_id: null, source: "pin" };
72
-
73
- const { parseVaultRemoteUrl, vaultRemoteAddressForm } = await import("#sdk");
74
- for (const name of ["run402", "origin"]) {
75
- let url;
76
- try {
77
- url = (await hardenedGit(repoDir, ["remote", "get-url", name])).text().trim();
78
- } catch {
79
- continue; // no such remote — try the other conventional name
80
- }
81
- if (!url) continue;
82
- let address;
83
- try {
84
- address = parseVaultRemoteUrl(url);
85
- } catch {
86
- continue; // another scheme's remote — not a vault address
87
- }
88
- if (!address) continue; // exists, but isn't a run402 address — try the other name
89
- if (vaultRemoteAddressForm(address) === "id") {
90
- // id-form already carries both halves in the address string — no
91
- // network read needed at all.
92
- return { repo_id: null, project_id: address.project_id, source: "remote", remote_name: name };
93
- }
94
- try {
95
- const { getSdk } = await import("./sdk.mjs");
96
- const resolved = await getSdk().repos.resolveAddress(address);
97
- return { repo_id: resolved.repo_id, project_id: resolved.project_id, source: "remote", remote_name: name };
98
- } catch {
99
- // Offline, SLUG_RELEASED, not-found, ... — nothing to target from this
100
- // rung; a caller falls through to env/active project.
101
- return null;
102
- }
103
- }
104
- return null;
15
+ return sdkRepoOwnVaultTarget(getSdk().repos, repoDir);
105
16
  }
106
17
 
107
- /**
108
- * Resolve `{ repo_id?, project_id? }` for a vault verb that addresses a
109
- * vault the same way `--repo`/`--project` already do — pin beats remote
110
- * beats env beats active project, an explicit flag beats all of them.
111
- * `explicitProjectId`/`explicitRepoId` are the already-parsed flag values
112
- * (`undefined`/`null` when absent — this module owns no flag parsing), and
113
- * either, neither, or both may be set (mirroring `--repo`/`--project`
114
- * together being valid on the CLI today). `warn` receives the one-line
115
- * mismatch note when an explicit flag disagrees with the repo's OWN target;
116
- * the flag still wins either way. The pin carries no project_id to compare
117
- * for free, so only `--repo` is checked against it; only the remote tier
118
- * carries a project_id for free (id-form) or resolves one (slug-form), so
119
- * only `--project` is checked against it — matching the task's own wording
120
- * ("a mismatch between an explicit flag and the repo's remote").
121
- */
122
- export async function resolveVaultTarget({
123
- repoDir = process.cwd(),
124
- explicitProjectId,
125
- explicitRepoId,
126
- warn = (line) => console.error(line),
127
- } = {}) {
128
- const needsOwn = explicitRepoId == null || explicitProjectId == null;
129
- const own = needsOwn ? await repoOwnVaultTarget(repoDir) : null;
130
-
131
- if (explicitRepoId != null && own?.source === "pin" && own.repo_id !== explicitRepoId) {
132
- warn(`warning: --repo ${explicitRepoId} does not match this repo's pinned vault ${own.repo_id} — using --repo ${explicitRepoId}.`);
133
- }
134
- if (explicitProjectId != null && own?.source === "remote" && own.project_id && own.project_id !== explicitProjectId) {
135
- warn(`warning: --project ${explicitProjectId} does not match this repo's '${own.remote_name}' remote project ${own.project_id} — using --project ${explicitProjectId}.`);
136
- }
137
-
138
- if (explicitRepoId != null || explicitProjectId != null) {
139
- const result = {};
140
- if (explicitRepoId != null) result.repo_id = explicitRepoId;
141
- if (explicitProjectId != null) result.project_id = explicitProjectId;
142
- return result;
143
- }
144
-
145
- if (own?.source === "pin") return { repo_id: own.repo_id };
146
- if (own?.source === "remote" && own.project_id) return { project_id: own.project_id };
147
- return { project_id: envOrActiveProjectId() };
18
+ /** The vault a verb addresses from `repoDir`: `{ repo_id }` or `{ project_id }`. */
19
+ export async function resolveVaultTarget(opts = {}) {
20
+ return sdkResolveVaultTarget(getSdk().repos, opts);
148
21
  }