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.
- package/README.md +43 -41
- package/cli.mjs +34 -29
- package/core-dist/config.js +52 -18
- package/core-dist/control-plane-session.js +31 -6
- package/core-dist/keystore.js +7 -7
- package/core-dist/profiles.js +9 -7
- package/core-dist/{allowance-auth.js → wallet-auth.js} +15 -15
- package/{sdk/core-dist/allowance.js → core-dist/wallet.js} +20 -20
- package/{sdk/core-dist/write-auth-session.js → core-dist/write-approvals.js} +19 -19
- package/gitvault-surface.json +1 -1
- package/lib/admin.mjs +1 -1
- package/lib/agent.mjs +20 -18
- package/lib/approve.mjs +81 -0
- package/lib/apps.mjs +3 -3
- package/lib/assets.mjs +2 -2
- package/lib/billing.mjs +6 -6
- package/lib/buzz-doctor.mjs +1 -1
- package/lib/buzz-notifications.mjs +1 -1
- package/lib/buzz.mjs +1 -1
- package/lib/buzz.test.mjs +1 -1
- package/lib/ci.mjs +4 -4
- package/lib/cold-start.mjs +21 -21
- package/lib/command-manifest.mjs +20 -34
- package/lib/config.mjs +24 -24
- package/lib/contacts.mjs +13 -13
- package/lib/contracts.mjs +1 -1
- package/lib/credentials.mjs +2 -2
- package/lib/daemon-path.mjs +1 -1
- package/lib/delegates.mjs +1 -1
- package/lib/deliveries.mjs +4 -4
- package/lib/deploy-rehearse-discovery.test.mjs +7 -7
- package/lib/deploy-v2.mjs +232 -184
- package/lib/deploy.mjs +21 -53
- package/lib/doctor-source-scan.mjs +1 -1
- package/lib/doctor.mjs +57 -57
- package/lib/escalations.mjs +1 -1
- package/lib/feedback.mjs +10 -10
- package/lib/gitvault-daemon.mjs +1 -1
- package/lib/image.mjs +3 -3
- package/lib/init.mjs +66 -66
- package/lib/lightning-wallet.mjs +28 -28
- package/lib/login.mjs +207 -0
- package/lib/logout.mjs +53 -0
- package/lib/next-actions.mjs +15 -15
- package/lib/org.mjs +100 -13
- package/lib/pay.mjs +1 -1
- package/lib/projects.mjs +26 -29
- package/lib/redeem.mjs +7 -8
- package/lib/remote-helper-session.mjs +12 -12
- package/lib/repos.mjs +62 -62
- package/lib/rooms.mjs +103 -67
- package/lib/sdk-errors.mjs +9 -4
- package/lib/sdk.mjs +3 -3
- package/lib/service.mjs +5 -5
- package/lib/session-proofs.mjs +21 -0
- package/lib/sign-in.mjs +296 -0
- package/lib/sites.mjs +8 -8
- package/lib/stats.mjs +2 -2
- package/lib/status.mjs +25 -25
- package/lib/subdomains.mjs +19 -19
- package/lib/subscriptions.mjs +8 -8
- package/lib/tier.mjs +29 -22
- package/lib/transfer.mjs +58 -85
- package/lib/up.mjs +7 -7
- package/lib/wallet-context.mjs +3 -3
- package/lib/wallet-context.test.mjs +3 -3
- package/lib/wallets.mjs +196 -31
- package/lib/webhook-secret.mjs +6 -6
- package/lib/whoami.mjs +75 -0
- package/package.json +1 -1
- package/sdk/core-dist/config.js +52 -18
- package/sdk/core-dist/control-plane-session.js +31 -6
- package/sdk/core-dist/keystore.js +7 -7
- package/sdk/core-dist/profiles.js +9 -7
- package/sdk/core-dist/{allowance-auth.js → wallet-auth.js} +15 -15
- package/{core-dist/allowance.js → sdk/core-dist/wallet.js} +20 -20
- package/{core-dist/write-auth-session.js → sdk/core-dist/write-approvals.js} +19 -19
- package/sdk/dist/actions.d.ts +1 -1
- package/sdk/dist/actions.d.ts.map +1 -1
- package/sdk/dist/control-plane-credentials.d.ts +8 -8
- package/sdk/dist/control-plane-credentials.js +7 -7
- package/sdk/dist/credentials.d.ts +19 -19
- package/sdk/dist/credentials.d.ts.map +1 -1
- package/sdk/dist/credentials.js +1 -1
- package/sdk/dist/errors.d.ts +24 -26
- package/sdk/dist/errors.d.ts.map +1 -1
- package/sdk/dist/errors.js +34 -37
- package/sdk/dist/errors.js.map +1 -1
- package/sdk/dist/index.d.ts +29 -16
- package/sdk/dist/index.d.ts.map +1 -1
- package/sdk/dist/index.js +29 -16
- package/sdk/dist/index.js.map +1 -1
- package/sdk/dist/kernel.d.ts +1 -1
- package/sdk/dist/kernel.d.ts.map +1 -1
- package/sdk/dist/kernel.js +10 -9
- package/sdk/dist/kernel.js.map +1 -1
- package/sdk/dist/namespaces/admin.d.ts +51 -120
- package/sdk/dist/namespaces/admin.d.ts.map +1 -1
- package/sdk/dist/namespaces/admin.js +41 -45
- package/sdk/dist/namespaces/admin.js.map +1 -1
- package/sdk/dist/namespaces/agent.d.ts +2 -2
- package/sdk/dist/namespaces/agent.d.ts.map +1 -1
- package/sdk/dist/namespaces/agent.js +2 -2
- package/sdk/dist/namespaces/agent.js.map +1 -1
- package/sdk/dist/namespaces/ai.d.ts +1 -1
- package/sdk/dist/namespaces/ai.js +1 -1
- package/sdk/dist/namespaces/billing.d.ts +6 -3
- package/sdk/dist/namespaces/billing.d.ts.map +1 -1
- package/sdk/dist/namespaces/billing.js.map +1 -1
- package/sdk/dist/namespaces/buzz-notifications.types.d.ts +1 -1
- package/sdk/dist/namespaces/buzz.d.ts +2 -2
- package/sdk/dist/namespaces/buzz.d.ts.map +1 -1
- package/sdk/dist/namespaces/buzz.js +2 -2
- package/sdk/dist/namespaces/buzz.js.map +1 -1
- package/sdk/dist/namespaces/buzz.types.d.ts +2 -2
- package/sdk/dist/namespaces/buzz.types.d.ts.map +1 -1
- package/sdk/dist/namespaces/credentials.types.d.ts +1 -1
- package/sdk/dist/namespaces/deploy.d.ts +1 -1
- package/sdk/dist/namespaces/deploy.js +5 -5
- package/sdk/dist/namespaces/deploy.js.map +1 -1
- package/sdk/dist/namespaces/deploy.types.d.ts +4 -4
- package/sdk/dist/namespaces/escalations.d.ts +1 -1
- package/sdk/dist/namespaces/escalations.js +1 -1
- package/sdk/dist/namespaces/escalations.types.d.ts +2 -2
- package/sdk/dist/namespaces/escalations.types.d.ts.map +1 -1
- package/sdk/dist/namespaces/events.types.d.ts +1 -1
- package/sdk/dist/namespaces/functions.d.ts +1 -1
- package/sdk/dist/namespaces/functions.js +1 -1
- package/sdk/dist/namespaces/gitvault.crypto.d.ts +1 -1
- package/sdk/dist/namespaces/gitvault.d.ts +24 -24
- package/sdk/dist/namespaces/gitvault.d.ts.map +1 -1
- package/sdk/dist/namespaces/gitvault.js +109 -109
- package/sdk/dist/namespaces/gitvault.js.map +1 -1
- package/sdk/dist/namespaces/me.d.ts +120 -0
- package/sdk/dist/namespaces/me.d.ts.map +1 -0
- package/sdk/dist/namespaces/me.js +34 -0
- package/sdk/dist/namespaces/me.js.map +1 -0
- package/sdk/dist/namespaces/org.d.ts +108 -6
- package/sdk/dist/namespaces/org.d.ts.map +1 -1
- package/sdk/dist/namespaces/org.js +63 -7
- package/sdk/dist/namespaces/org.js.map +1 -1
- package/sdk/dist/namespaces/org.types.d.ts +29 -9
- package/sdk/dist/namespaces/org.types.d.ts.map +1 -1
- package/sdk/dist/namespaces/pay.js +3 -3
- package/sdk/dist/namespaces/pay.js.map +1 -1
- package/sdk/dist/namespaces/projects.d.ts +9 -9
- package/sdk/dist/namespaces/projects.js +15 -15
- package/sdk/dist/namespaces/projects.js.map +1 -1
- package/sdk/dist/namespaces/projects.types.d.ts +28 -12
- package/sdk/dist/namespaces/projects.types.d.ts.map +1 -1
- package/sdk/dist/namespaces/rooms.d.ts +7 -7
- package/sdk/dist/namespaces/rooms.js +17 -17
- package/sdk/dist/namespaces/rooms.js.map +1 -1
- package/sdk/dist/namespaces/rooms.types.d.ts +9 -9
- package/sdk/dist/namespaces/rooms.types.d.ts.map +1 -1
- package/sdk/dist/namespaces/service.d.ts +1 -1
- package/sdk/dist/namespaces/service.js +1 -1
- package/sdk/dist/namespaces/{operator-session.d.ts → session.d.ts} +135 -51
- package/sdk/dist/namespaces/session.d.ts.map +1 -0
- package/sdk/dist/namespaces/{operator-session.js → session.js} +118 -30
- package/sdk/dist/namespaces/session.js.map +1 -0
- package/sdk/dist/namespaces/subdomains.d.ts +5 -5
- package/sdk/dist/namespaces/subdomains.d.ts.map +1 -1
- package/sdk/dist/namespaces/subdomains.js +6 -6
- package/sdk/dist/namespaces/subdomains.js.map +1 -1
- package/sdk/dist/namespaces/tier.d.ts +43 -20
- package/sdk/dist/namespaces/tier.d.ts.map +1 -1
- package/sdk/dist/namespaces/tier.js +7 -7
- package/sdk/dist/namespaces/tier.js.map +1 -1
- package/sdk/dist/namespaces/transfers.d.ts +74 -71
- package/sdk/dist/namespaces/transfers.d.ts.map +1 -1
- package/sdk/dist/namespaces/transfers.js +31 -38
- package/sdk/dist/namespaces/transfers.js.map +1 -1
- package/sdk/dist/namespaces/vouchers.d.ts +9 -10
- package/sdk/dist/namespaces/vouchers.d.ts.map +1 -1
- package/sdk/dist/namespaces/vouchers.js +4 -5
- package/sdk/dist/namespaces/vouchers.js.map +1 -1
- package/sdk/dist/namespaces/wallets.d.ts +72 -3
- package/sdk/dist/namespaces/wallets.d.ts.map +1 -1
- package/sdk/dist/namespaces/wallets.js +141 -3
- package/sdk/dist/namespaces/wallets.js.map +1 -1
- package/sdk/dist/namespaces/write-approval.d.ts +101 -0
- package/sdk/dist/namespaces/write-approval.d.ts.map +1 -0
- package/sdk/dist/namespaces/write-approval.js +62 -0
- package/sdk/dist/namespaces/write-approval.js.map +1 -0
- package/sdk/dist/node/actions-node.d.ts +3 -3
- package/sdk/dist/node/actions-node.d.ts.map +1 -1
- package/sdk/dist/node/actions-node.js +36 -36
- package/sdk/dist/node/actions-node.js.map +1 -1
- package/sdk/dist/node/{bearer-claim-key.d.ts → bearer-redeem-key.d.ts} +12 -12
- package/sdk/dist/node/bearer-redeem-key.d.ts.map +1 -0
- package/sdk/dist/node/{bearer-claim-key.js → bearer-redeem-key.js} +21 -21
- package/sdk/dist/node/bearer-redeem-key.js.map +1 -0
- package/sdk/dist/node/ci.d.ts +1 -1
- package/sdk/dist/node/ci.d.ts.map +1 -1
- package/sdk/dist/node/ci.js +6 -6
- package/sdk/dist/node/ci.js.map +1 -1
- package/sdk/dist/node/credentials.d.ts +17 -16
- package/sdk/dist/node/credentials.d.ts.map +1 -1
- package/sdk/dist/node/credentials.js +28 -27
- package/sdk/dist/node/credentials.js.map +1 -1
- package/sdk/dist/node/funding-recovery.d.ts +1 -1
- package/sdk/dist/node/funding-recovery.d.ts.map +1 -1
- package/sdk/dist/node/funding-recovery.js +1 -1
- package/sdk/dist/node/funding-recovery.js.map +1 -1
- package/sdk/dist/node/gitvault-apply.d.ts +1 -1
- package/sdk/dist/node/gitvault-apply.d.ts.map +1 -1
- package/sdk/dist/node/gitvault-handoff.d.ts +22 -22
- package/sdk/dist/node/gitvault-handoff.d.ts.map +1 -1
- package/sdk/dist/node/gitvault-handoff.js +45 -45
- package/sdk/dist/node/gitvault-handoff.js.map +1 -1
- package/sdk/dist/node/gitvault-prewarm.js +7 -7
- package/sdk/dist/node/gitvault-prewarm.js.map +1 -1
- package/sdk/dist/node/gitvault-restore.d.ts +1 -1
- package/sdk/dist/node/gitvault-restore.js +1 -1
- package/sdk/dist/node/index.d.ts +16 -16
- package/sdk/dist/node/index.d.ts.map +1 -1
- package/sdk/dist/node/index.js +15 -15
- package/sdk/dist/node/index.js.map +1 -1
- package/sdk/dist/node/org-adopt.d.ts +49 -0
- package/sdk/dist/node/org-adopt.d.ts.map +1 -0
- package/sdk/dist/node/org-adopt.js +77 -0
- package/sdk/dist/node/org-adopt.js.map +1 -0
- package/sdk/dist/node/paid-fetch.d.ts +7 -7
- package/sdk/dist/node/paid-fetch.d.ts.map +1 -1
- package/sdk/dist/node/paid-fetch.js +54 -54
- package/sdk/dist/node/paid-fetch.js.map +1 -1
- package/sdk/dist/node/preflight.d.ts +1 -1
- package/sdk/dist/node/preflight.d.ts.map +1 -1
- package/sdk/dist/node/preflight.js +1 -1
- package/sdk/dist/node/preflight.js.map +1 -1
- package/sdk/dist/node/source-scan.d.ts +1 -1
- package/sdk/dist/node/source-scan.js +2 -2
- package/sdk/dist/node/source-scan.js.map +1 -1
- package/sdk/dist/scoped.d.ts +3 -3
- package/sdk/dist/scoped.d.ts.map +1 -1
- package/sdk/dist/scoped.js +2 -2
- package/sdk/dist/scoped.js.map +1 -1
- package/core-dist/operator-session.js +0 -154
- package/lib/allowance.mjs +0 -355
- package/lib/operator-proofs.mjs +0 -22
- package/lib/operator.mjs +0 -771
- package/sdk/core-dist/operator-session.js +0 -154
- package/sdk/dist/namespaces/allowance.d.ts +0 -79
- package/sdk/dist/namespaces/allowance.d.ts.map +0 -1
- package/sdk/dist/namespaces/allowance.js +0 -149
- package/sdk/dist/namespaces/allowance.js.map +0 -1
- package/sdk/dist/namespaces/operator-session.d.ts.map +0 -1
- package/sdk/dist/namespaces/operator-session.js.map +0 -1
- package/sdk/dist/namespaces/operator.d.ts +0 -359
- package/sdk/dist/namespaces/operator.d.ts.map +0 -1
- package/sdk/dist/namespaces/operator.js +0 -272
- package/sdk/dist/namespaces/operator.js.map +0 -1
- package/sdk/dist/node/bearer-claim-key.d.ts.map +0 -1
- package/sdk/dist/node/bearer-claim-key.js.map +0 -1
- package/sdk/dist/node/operator-claim.d.ts +0 -49
- package/sdk/dist/node/operator-claim.d.ts.map +0 -1
- package/sdk/dist/node/operator-claim.js +0 -77
- package/sdk/dist/node/operator-claim.js.map +0 -1
package/lib/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/next-actions.mjs
CHANGED
|
@@ -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
|
|
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: "
|
|
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
|
|
66
|
-
why: "
|
|
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
|
-
*
|
|
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
|
|
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
|
|
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
|
|
92
|
-
return nextAction("
|
|
91
|
+
export function setOrgSlugAction() {
|
|
92
|
+
return nextAction("set_org_slug", {
|
|
93
93
|
command: "run402 org slug <slug>",
|
|
94
|
-
why: "This organization has no
|
|
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
|
|
101
|
-
* some other reason) — point at the explicit
|
|
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
|
|
105
|
-
return nextAction("
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 —
|
|
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,
|
|
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
|
|
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
|
-
|
|
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>` —
|
|
559
|
-
* Owner-only, and a genesis
|
|
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
|
|
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).
|
|
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}"
|
|
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
|
|
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 {
|
|
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 "./
|
|
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
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
|
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
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
|
201
|
-
Against a configured Run402 Core target, no Cloud tier/
|
|
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
|
|
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
|
|
365
|
-
// there's also no
|
|
366
|
-
//
|
|
367
|
-
if (!isCoreApiTarget() && !loadLiveControlPlaneSession())
|
|
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)
|
|
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
|
|
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
|
|
533
|
-
//
|
|
534
|
-
//
|
|
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 =
|
|
540
|
-
if (session) opts.token = session.
|
|
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
|
|
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
|
|