run402 3.7.14 → 3.8.1
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 +4 -2
- package/cli.mjs +6 -0
- package/core-dist/config.js +9 -0
- package/core-dist/keystore.js +103 -68
- package/core-dist/profile-state.js +137 -0
- package/lib/config.mjs +31 -20
- package/lib/credentials.mjs +273 -0
- package/lib/deploy-v2.mjs +11 -0
- package/lib/doctor.mjs +18 -2
- package/lib/domains.mjs +40 -20
- package/lib/next-actions.mjs +7 -0
- package/lib/projects.mjs +65 -27
- package/lib/sdk-errors.mjs +24 -3
- package/lib/sdk.mjs +3 -2
- package/lib/up.mjs +19 -0
- package/lib/up.test.mjs +29 -0
- package/lib/update-check.mjs +722 -0
- package/lib/update-check.test.mjs +438 -0
- package/package.json +1 -1
- package/sdk/core-dist/config.js +9 -0
- package/sdk/core-dist/keystore.js +103 -68
- package/sdk/core-dist/profile-state.js +137 -0
- package/sdk/dist/credentials.d.ts +24 -7
- package/sdk/dist/credentials.d.ts.map +1 -1
- package/sdk/dist/credentials.js +4 -4
- package/sdk/dist/errors.d.ts +27 -1
- package/sdk/dist/errors.d.ts.map +1 -1
- package/sdk/dist/errors.js +61 -3
- package/sdk/dist/errors.js.map +1 -1
- package/sdk/dist/index.d.ts +16 -2
- package/sdk/dist/index.d.ts.map +1 -1
- package/sdk/dist/index.js +9 -3
- package/sdk/dist/index.js.map +1 -1
- package/sdk/dist/kernel.d.ts +9 -0
- package/sdk/dist/kernel.d.ts.map +1 -1
- package/sdk/dist/kernel.js +40 -1
- package/sdk/dist/kernel.js.map +1 -1
- package/sdk/dist/namespaces/ai.d.ts.map +1 -1
- package/sdk/dist/namespaces/ai.js +5 -10
- package/sdk/dist/namespaces/ai.js.map +1 -1
- package/sdk/dist/namespaces/apps.d.ts.map +1 -1
- package/sdk/dist/namespaces/apps.js +5 -13
- package/sdk/dist/namespaces/apps.js.map +1 -1
- package/sdk/dist/namespaces/assets.d.ts.map +1 -1
- package/sdk/dist/namespaces/assets.js +9 -22
- package/sdk/dist/namespaces/assets.js.map +1 -1
- package/sdk/dist/namespaces/auth.d.ts.map +1 -1
- package/sdk/dist/namespaces/auth.js +16 -43
- package/sdk/dist/namespaces/auth.js.map +1 -1
- package/sdk/dist/namespaces/contracts.d.ts.map +1 -1
- package/sdk/dist/namespaces/contracts.js +12 -31
- package/sdk/dist/namespaces/contracts.js.map +1 -1
- package/sdk/dist/namespaces/credentials.d.ts +48 -0
- package/sdk/dist/namespaces/credentials.d.ts.map +1 -0
- package/sdk/dist/namespaces/credentials.js +115 -0
- package/sdk/dist/namespaces/credentials.js.map +1 -0
- package/sdk/dist/namespaces/domains.d.ts +8 -2
- package/sdk/dist/namespaces/domains.d.ts.map +1 -1
- package/sdk/dist/namespaces/domains.js +41 -23
- package/sdk/dist/namespaces/domains.js.map +1 -1
- package/sdk/dist/namespaces/email.d.ts.map +1 -1
- package/sdk/dist/namespaces/email.js +8 -19
- package/sdk/dist/namespaces/email.js.map +1 -1
- package/sdk/dist/namespaces/functions.d.ts.map +1 -1
- package/sdk/dist/namespaces/functions.js +14 -37
- package/sdk/dist/namespaces/functions.js.map +1 -1
- package/sdk/dist/namespaces/jobs.d.ts.map +1 -1
- package/sdk/dist/namespaces/jobs.js +8 -19
- package/sdk/dist/namespaces/jobs.js.map +1 -1
- package/sdk/dist/namespaces/projects.d.ts +4 -4
- package/sdk/dist/namespaces/projects.d.ts.map +1 -1
- package/sdk/dist/namespaces/projects.js +18 -42
- package/sdk/dist/namespaces/projects.js.map +1 -1
- package/sdk/dist/namespaces/secrets.d.ts.map +1 -1
- package/sdk/dist/namespaces/secrets.js +5 -10
- package/sdk/dist/namespaces/secrets.js.map +1 -1
- package/sdk/dist/namespaces/sender-domain.d.ts.map +1 -1
- package/sdk/dist/namespaces/sender-domain.js +6 -16
- package/sdk/dist/namespaces/sender-domain.js.map +1 -1
- package/sdk/dist/namespaces/subdomains.d.ts.map +1 -1
- package/sdk/dist/namespaces/subdomains.js +5 -10
- package/sdk/dist/namespaces/subdomains.js.map +1 -1
- package/sdk/dist/node/actions-node.d.ts.map +1 -1
- package/sdk/dist/node/actions-node.js +46 -15
- package/sdk/dist/node/actions-node.js.map +1 -1
- package/sdk/dist/node/credentials.d.ts +18 -4
- package/sdk/dist/node/credentials.d.ts.map +1 -1
- package/sdk/dist/node/credentials.js +37 -9
- package/sdk/dist/node/credentials.js.map +1 -1
- package/sdk/dist/node/index.d.ts +11 -2
- package/sdk/dist/node/index.d.ts.map +1 -1
- package/sdk/dist/node/index.js +26 -1
- package/sdk/dist/node/index.js.map +1 -1
- package/sdk/dist/node/target-profile.d.ts.map +1 -1
- package/sdk/dist/node/target-profile.js +6 -4
- package/sdk/dist/node/target-profile.js.map +1 -1
- package/sdk/dist/project-auth-classification.d.ts +125 -0
- package/sdk/dist/project-auth-classification.d.ts.map +1 -0
- package/sdk/dist/project-auth-classification.js +135 -0
- package/sdk/dist/project-auth-classification.js.map +1 -0
- package/sdk/dist/project-credentials.d.ts +4 -0
- package/sdk/dist/project-credentials.d.ts.map +1 -0
- package/sdk/dist/project-credentials.js +9 -0
- package/sdk/dist/project-credentials.js.map +1 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import {
|
|
4
|
+
activeProfile,
|
|
5
|
+
getProject,
|
|
6
|
+
loadKeyStore,
|
|
7
|
+
projectCredentialsFile,
|
|
8
|
+
removeProject,
|
|
9
|
+
saveProject,
|
|
10
|
+
} from "./config.mjs";
|
|
11
|
+
import { assertKnownFlags, flagValue, normalizeArgv, positionalArgs } from "./argparse.mjs";
|
|
12
|
+
import { fail } from "./sdk-errors.mjs";
|
|
13
|
+
|
|
14
|
+
const HELP = `run402 credentials — Manage local credential material
|
|
15
|
+
|
|
16
|
+
Usage:
|
|
17
|
+
run402 credentials <subcommand> [args...]
|
|
18
|
+
|
|
19
|
+
Subcommands:
|
|
20
|
+
project-keys Manage the local project-key cache
|
|
21
|
+
|
|
22
|
+
Examples:
|
|
23
|
+
run402 credentials project-keys list
|
|
24
|
+
run402 credentials project-keys status --project prj_abc123
|
|
25
|
+
run402 credentials project-keys import --project prj_abc123 --service-key-stdin
|
|
26
|
+
run402 credentials project-keys export --project prj_abc123 --reveal
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
const PROJECT_KEYS_HELP = `run402 credentials project-keys — Manage local project-key cache entries
|
|
30
|
+
|
|
31
|
+
Usage:
|
|
32
|
+
run402 credentials project-keys <subcommand> [options]
|
|
33
|
+
|
|
34
|
+
Subcommands:
|
|
35
|
+
list List cached project-key entries, redacted
|
|
36
|
+
status --project <id> Show one cached entry, redacted
|
|
37
|
+
import --project <id> --service-key-stdin
|
|
38
|
+
import --project <id> --service-key-env <env>
|
|
39
|
+
export --project <id> --reveal Print cached keys, including secrets
|
|
40
|
+
remove --project <id> Remove one cached key entry
|
|
41
|
+
|
|
42
|
+
Notes:
|
|
43
|
+
- This is a LOCAL CACHE surface. It is not project inventory.
|
|
44
|
+
- list/status never reveal full keys.
|
|
45
|
+
- export requires --reveal.
|
|
46
|
+
- import accepts service keys through stdin or an environment variable, not argv.
|
|
47
|
+
`;
|
|
48
|
+
|
|
49
|
+
function parseProjectKeyFlags(args, extraKnown = [], valueFlagsExtra = []) {
|
|
50
|
+
const parsed = normalizeArgv(args);
|
|
51
|
+
const valueFlags = ["--project", ...valueFlagsExtra];
|
|
52
|
+
assertKnownFlags(parsed, ["--project", "--help", "-h", ...extraKnown], valueFlags);
|
|
53
|
+
return {
|
|
54
|
+
projectId: flagValue(parsed, "--project"),
|
|
55
|
+
parsed,
|
|
56
|
+
rest: positionalArgs(parsed, valueFlags),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function fingerprint(value) {
|
|
61
|
+
if (!value) return null;
|
|
62
|
+
return createHash("sha256").update(value).digest("hex").slice(0, 16);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function prefix(value) {
|
|
66
|
+
if (!value) return null;
|
|
67
|
+
return `${value.slice(0, 8)}...`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function provenance() {
|
|
71
|
+
const profile = activeProfile();
|
|
72
|
+
return {
|
|
73
|
+
source: "local_cache",
|
|
74
|
+
cache_path: projectCredentialsFile(),
|
|
75
|
+
wallet: profile,
|
|
76
|
+
profile,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function redactedEntry(projectId, entry) {
|
|
81
|
+
return {
|
|
82
|
+
project_id: projectId,
|
|
83
|
+
configured: Boolean(entry),
|
|
84
|
+
has_anon_key: Boolean(entry?.anon_key),
|
|
85
|
+
has_service_key: Boolean(entry?.service_key),
|
|
86
|
+
anon_key_prefix: prefix(entry?.anon_key),
|
|
87
|
+
service_key_prefix: prefix(entry?.service_key),
|
|
88
|
+
anon_key_fingerprint: fingerprint(entry?.anon_key),
|
|
89
|
+
service_key_fingerprint: fingerprint(entry?.service_key),
|
|
90
|
+
site_url: entry?.site_url ?? null,
|
|
91
|
+
cached_at: entry?.cached_at ?? null,
|
|
92
|
+
...provenance(),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function requireProjectFlag(projectId, usage) {
|
|
97
|
+
if (!projectId) {
|
|
98
|
+
fail({
|
|
99
|
+
code: "BAD_USAGE",
|
|
100
|
+
message: "Missing --project <id>.",
|
|
101
|
+
hint: usage,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
return projectId;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function requireCachedProject(projectId) {
|
|
108
|
+
const entry = getProject(projectId);
|
|
109
|
+
if (!entry) {
|
|
110
|
+
fail({
|
|
111
|
+
code: "PROJECT_CREDENTIAL_NOT_FOUND",
|
|
112
|
+
message: `No local project credentials cached for ${projectId}.`,
|
|
113
|
+
hint: "Import keys with `run402 credentials project-keys import --project <id> --service-key-stdin` if this operation truly requires local project credentials.",
|
|
114
|
+
details: { project_id: projectId, ...provenance() },
|
|
115
|
+
next_actions: [{
|
|
116
|
+
type: "run_command",
|
|
117
|
+
command: `run402 credentials project-keys import --project ${projectId} --service-key-stdin`,
|
|
118
|
+
why: "Import a service key for credential-required operations.",
|
|
119
|
+
}],
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
return entry;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function list(args) {
|
|
126
|
+
const { rest } = parseProjectKeyFlags(args);
|
|
127
|
+
if (rest.length > 0) {
|
|
128
|
+
fail({ code: "BAD_USAGE", message: `Unexpected argument for project-keys list: ${rest[0]}` });
|
|
129
|
+
}
|
|
130
|
+
const store = loadKeyStore(projectCredentialsFile());
|
|
131
|
+
const projects = Object.entries(store.projects ?? {})
|
|
132
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
133
|
+
.map(([projectId, entry]) => redactedEntry(projectId, entry));
|
|
134
|
+
console.log(JSON.stringify({ projects, ...provenance() }, null, 2));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function status(args) {
|
|
138
|
+
const { projectId, rest } = parseProjectKeyFlags(args);
|
|
139
|
+
if (rest.length > 0) {
|
|
140
|
+
fail({ code: "BAD_USAGE", message: `Unexpected argument for project-keys status: ${rest[0]}` });
|
|
141
|
+
}
|
|
142
|
+
const id = requireProjectFlag(projectId, "run402 credentials project-keys status --project <id>");
|
|
143
|
+
console.log(JSON.stringify(redactedEntry(id, getProject(id)), null, 2));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function readSecretInput(parsed) {
|
|
147
|
+
const fromEnv = flagValue(parsed, "--service-key-env");
|
|
148
|
+
const fromStdin = parsed.includes("--service-key-stdin");
|
|
149
|
+
if (fromEnv && fromStdin) {
|
|
150
|
+
fail({ code: "BAD_USAGE", message: "Use either --service-key-env or --service-key-stdin, not both." });
|
|
151
|
+
}
|
|
152
|
+
if (fromEnv) {
|
|
153
|
+
const value = process.env[fromEnv];
|
|
154
|
+
if (!value) {
|
|
155
|
+
fail({
|
|
156
|
+
code: "BAD_ENV",
|
|
157
|
+
message: `Environment variable ${fromEnv} is empty or unset.`,
|
|
158
|
+
details: { env: fromEnv },
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
return value.trim();
|
|
162
|
+
}
|
|
163
|
+
if (fromStdin) return readFileSync(0, "utf-8").trim();
|
|
164
|
+
fail({
|
|
165
|
+
code: "BAD_USAGE",
|
|
166
|
+
message: "Import requires --service-key-stdin or --service-key-env <env>.",
|
|
167
|
+
hint: "Do not pass service keys as command-line values; argv can leak through shell history and process listings.",
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async function importKey(args) {
|
|
172
|
+
const { projectId, parsed, rest } = parseProjectKeyFlags(
|
|
173
|
+
args,
|
|
174
|
+
["--service-key-stdin", "--service-key-env", "--anon-key-env"],
|
|
175
|
+
["--service-key-env", "--anon-key-env"],
|
|
176
|
+
);
|
|
177
|
+
if (rest.length > 0) {
|
|
178
|
+
fail({ code: "BAD_USAGE", message: `Unexpected argument for project-keys import: ${rest[0]}` });
|
|
179
|
+
}
|
|
180
|
+
const id = requireProjectFlag(projectId, "run402 credentials project-keys import --project <id> --service-key-stdin");
|
|
181
|
+
const serviceKey = readSecretInput(parsed);
|
|
182
|
+
if (!serviceKey) {
|
|
183
|
+
fail({ code: "BAD_USAGE", message: "Service key input was empty." });
|
|
184
|
+
}
|
|
185
|
+
const anonEnv = flagValue(parsed, "--anon-key-env");
|
|
186
|
+
const anonKey = anonEnv ? process.env[anonEnv] : undefined;
|
|
187
|
+
if (anonEnv && !anonKey) {
|
|
188
|
+
fail({ code: "BAD_ENV", message: `Environment variable ${anonEnv} is empty or unset.`, details: { env: anonEnv } });
|
|
189
|
+
}
|
|
190
|
+
const existing = getProject(id);
|
|
191
|
+
saveProject(id, {
|
|
192
|
+
anon_key: anonKey ?? existing?.anon_key ?? "",
|
|
193
|
+
service_key: serviceKey,
|
|
194
|
+
site_url: existing?.site_url,
|
|
195
|
+
deployed_at: existing?.deployed_at,
|
|
196
|
+
last_deployment_id: existing?.last_deployment_id,
|
|
197
|
+
source: "manual_import",
|
|
198
|
+
cached_at: new Date().toISOString(),
|
|
199
|
+
});
|
|
200
|
+
console.log(JSON.stringify({ imported: true, ...redactedEntry(id, getProject(id)) }, null, 2));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async function exportKey(args) {
|
|
204
|
+
const { projectId, parsed, rest } = parseProjectKeyFlags(args, ["--reveal"]);
|
|
205
|
+
if (rest.length > 0) {
|
|
206
|
+
fail({ code: "BAD_USAGE", message: `Unexpected argument for project-keys export: ${rest[0]}` });
|
|
207
|
+
}
|
|
208
|
+
const id = requireProjectFlag(projectId, "run402 credentials project-keys export --project <id> --reveal");
|
|
209
|
+
if (!parsed.includes("--reveal")) {
|
|
210
|
+
fail({
|
|
211
|
+
code: "REVEAL_REQUIRED",
|
|
212
|
+
message: "Exporting full project keys requires --reveal.",
|
|
213
|
+
hint: "Use `run402 credentials project-keys status --project <id>` for redacted output.",
|
|
214
|
+
details: { project_id: id, ...provenance() },
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
const entry = requireCachedProject(id);
|
|
218
|
+
console.log(JSON.stringify({ project_id: id, ...entry, ...provenance(), revealed: true }, null, 2));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async function remove(args) {
|
|
222
|
+
const { projectId, rest } = parseProjectKeyFlags(args);
|
|
223
|
+
if (rest.length > 0) {
|
|
224
|
+
fail({ code: "BAD_USAGE", message: `Unexpected argument for project-keys remove: ${rest[0]}` });
|
|
225
|
+
}
|
|
226
|
+
const id = requireProjectFlag(projectId, "run402 credentials project-keys remove --project <id>");
|
|
227
|
+
const existed = Boolean(getProject(id));
|
|
228
|
+
removeProject(id, projectCredentialsFile());
|
|
229
|
+
console.log(JSON.stringify({ project_id: id, removed: existed, ...provenance() }, null, 2));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
async function runProjectKeys(sub, args) {
|
|
233
|
+
if (!sub || sub === "--help" || sub === "-h") {
|
|
234
|
+
console.log(PROJECT_KEYS_HELP);
|
|
235
|
+
process.exit(0);
|
|
236
|
+
}
|
|
237
|
+
if (Array.isArray(args) && (args.includes("--help") || args.includes("-h"))) {
|
|
238
|
+
console.log(PROJECT_KEYS_HELP);
|
|
239
|
+
process.exit(0);
|
|
240
|
+
}
|
|
241
|
+
switch (sub) {
|
|
242
|
+
case "list": await list(args); break;
|
|
243
|
+
case "status": await status(args); break;
|
|
244
|
+
case "import": await importKey(args); break;
|
|
245
|
+
case "export": await exportKey(args); break;
|
|
246
|
+
case "remove": await remove(args); break;
|
|
247
|
+
default:
|
|
248
|
+
fail({
|
|
249
|
+
code: "UNKNOWN_SUBCOMMAND",
|
|
250
|
+
message: `Unknown credentials project-keys subcommand: ${sub}`,
|
|
251
|
+
hint: "Run `run402 credentials project-keys --help` for usage.",
|
|
252
|
+
details: { command: "credentials project-keys", subcommand: sub },
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export async function run(sub, args = []) {
|
|
258
|
+
if (!sub || sub === "--help" || sub === "-h") {
|
|
259
|
+
console.log(HELP);
|
|
260
|
+
process.exit(0);
|
|
261
|
+
}
|
|
262
|
+
if (sub === "project-keys") {
|
|
263
|
+
const [projectKeySub, ...rest] = Array.isArray(args) ? args : [];
|
|
264
|
+
await runProjectKeys(projectKeySub, rest);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
fail({
|
|
268
|
+
code: "UNKNOWN_SUBCOMMAND",
|
|
269
|
+
message: `Unknown credentials subcommand: ${sub}`,
|
|
270
|
+
hint: "Run `run402 credentials --help` for usage.",
|
|
271
|
+
details: { command: "credentials", subcommand: sub },
|
|
272
|
+
});
|
|
273
|
+
}
|
package/lib/deploy-v2.mjs
CHANGED
|
@@ -38,6 +38,7 @@ import { normalizeArgv } from "./argparse.mjs";
|
|
|
38
38
|
import { loadLiveControlPlaneSession } from "../core-dist/control-plane-session.js";
|
|
39
39
|
import { withAutoApprove } from "./operator.mjs";
|
|
40
40
|
import { editRequestAction, nextAction, retryAction } from "./next-actions.mjs";
|
|
41
|
+
import { createUpdateCheckScheduler, emitUpdateNotice } from "./update-check.mjs";
|
|
41
42
|
|
|
42
43
|
const APPLY_HELP = `run402 deploy apply — Unified deploy primitive (v1.34+)
|
|
43
44
|
|
|
@@ -444,6 +445,7 @@ function parsePromoteArgs(args) {
|
|
|
444
445
|
|
|
445
446
|
async function promoteCmd(args) {
|
|
446
447
|
const opts = parsePromoteArgs(args);
|
|
448
|
+
emitDeployUpdateNotice("promote", args, { quiet: opts.quiet });
|
|
447
449
|
const projectId = opts.project ?? resolveProjectId(null);
|
|
448
450
|
|
|
449
451
|
// Preserve the aggressive early-exit when no allowance is configured
|
|
@@ -500,6 +502,13 @@ function makeStderrEventWriter(quiet) {
|
|
|
500
502
|
};
|
|
501
503
|
}
|
|
502
504
|
|
|
505
|
+
function emitDeployUpdateNotice(subcommand, args, { quiet = false } = {}) {
|
|
506
|
+
const scheduler = createUpdateCheckScheduler({
|
|
507
|
+
command: ["run402", "deploy", subcommand, ...args],
|
|
508
|
+
});
|
|
509
|
+
emitUpdateNotice(scheduler.cachedNotice, { quiet });
|
|
510
|
+
}
|
|
511
|
+
|
|
503
512
|
function parseApplyArgs(args) {
|
|
504
513
|
const opts = {
|
|
505
514
|
manifest: null,
|
|
@@ -787,6 +796,7 @@ async function applyCmd(args) {
|
|
|
787
796
|
const opts = parseApplyArgs(args);
|
|
788
797
|
const { source, error: sourceError } = resolveApplySource(opts, hasStdinSource());
|
|
789
798
|
if (sourceError) fail(sourceError);
|
|
799
|
+
emitDeployUpdateNotice("apply", args, { quiet: opts.quiet });
|
|
790
800
|
|
|
791
801
|
let raw;
|
|
792
802
|
let manifestPath = null;
|
|
@@ -1377,6 +1387,7 @@ async function resumeCmd(args) {
|
|
|
1377
1387
|
project: parsed.flags["--project"] ?? null,
|
|
1378
1388
|
quiet: Boolean(parsed.flags["--quiet"]),
|
|
1379
1389
|
};
|
|
1390
|
+
emitDeployUpdateNotice("resume", args, { quiet: opts.quiet });
|
|
1380
1391
|
const project = resolveProjectId(opts.project);
|
|
1381
1392
|
|
|
1382
1393
|
allowanceAuthHeaders("/apply/v1/operations");
|
package/lib/doctor.mjs
CHANGED
|
@@ -19,22 +19,25 @@ import {
|
|
|
19
19
|
scanSourceTree,
|
|
20
20
|
SCAN_SEVERITY,
|
|
21
21
|
} from "./doctor-source-scan.mjs";
|
|
22
|
+
import { doctorUpdateCheck } from "./update-check.mjs";
|
|
22
23
|
|
|
23
24
|
const HELP = `run402 doctor — Health and config diagnostics
|
|
24
25
|
|
|
25
26
|
Usage:
|
|
26
|
-
run402 doctor [--verbose] [--no-scan] [--scan-dir <D>]
|
|
27
|
+
run402 doctor [--verbose] [--refresh] [--no-scan] [--scan-dir <D>]
|
|
27
28
|
|
|
28
29
|
Output:
|
|
29
30
|
Stdout is a JSON report { ok, checks: [{ name, status, value?, hint?, message? }] }.
|
|
30
31
|
|
|
31
32
|
Options:
|
|
32
33
|
--verbose Include extra detail (timing, error messages)
|
|
34
|
+
--refresh Wait for a bounded live npm version check for the run402 CLI
|
|
33
35
|
--no-scan Skip the source-tree scan (config / health checks only)
|
|
34
36
|
--scan-dir D Scan a custom directory instead of \`<cwd>/src\`
|
|
35
37
|
|
|
36
38
|
Checks performed:
|
|
37
39
|
- Config directory exists and is writable
|
|
40
|
+
- Installed run402 CLI version and update guidance
|
|
38
41
|
- Allowance is configured and on a valid rail (x402 / mpp)
|
|
39
42
|
- Keystore has at least one wallet
|
|
40
43
|
- API_BASE is reachable (network check via /health)
|
|
@@ -60,6 +63,7 @@ export async function run(sub, args = []) {
|
|
|
60
63
|
return;
|
|
61
64
|
}
|
|
62
65
|
const verbose = all.includes("--verbose");
|
|
66
|
+
const refresh = all.includes("--refresh");
|
|
63
67
|
const skipScan = all.includes("--no-scan");
|
|
64
68
|
const scanDirArgIdx = all.indexOf("--scan-dir");
|
|
65
69
|
const scanDirOverride = scanDirArgIdx >= 0 ? all[scanDirArgIdx + 1] : null;
|
|
@@ -87,6 +91,18 @@ export async function run(sub, args = []) {
|
|
|
87
91
|
});
|
|
88
92
|
}
|
|
89
93
|
|
|
94
|
+
// 1b. CLI version/update state. This is advisory: stale or unknown version
|
|
95
|
+
// state should help the user, not hide the rest of doctor.
|
|
96
|
+
try {
|
|
97
|
+
checks.push(await doctorUpdateCheck({ refresh }));
|
|
98
|
+
} catch (err) {
|
|
99
|
+
checks.push({
|
|
100
|
+
name: "cli_update",
|
|
101
|
+
status: "unknown",
|
|
102
|
+
message: err instanceof Error ? err.message : String(err),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
90
106
|
// 2. Allowance.
|
|
91
107
|
try {
|
|
92
108
|
const allowance = readAllowance();
|
|
@@ -324,7 +340,7 @@ export async function run(sub, args = []) {
|
|
|
324
340
|
// 'warning' counts as ok for exit-code purposes — gaps are surfaced in
|
|
325
341
|
// output but don't fail the doctor. Only hard 'error' / 'missing' /
|
|
326
342
|
// 'empty' fail.
|
|
327
|
-
const allOk = checks.every((c) => c.status === "ok" || c.status === "warning" || c.status === "skipped");
|
|
343
|
+
const allOk = checks.every((c) => c.status === "ok" || c.status === "warning" || c.status === "skipped" || c.status === "unknown");
|
|
328
344
|
|
|
329
345
|
console.log(JSON.stringify({ ok: allOk, checks }, null, 2));
|
|
330
346
|
process.exit(allOk ? 0 : 1);
|
package/lib/domains.mjs
CHANGED
|
@@ -9,14 +9,15 @@ Usage:
|
|
|
9
9
|
run402 domains <subcommand> [args...]
|
|
10
10
|
|
|
11
11
|
Subcommands:
|
|
12
|
-
add <domain> <subdomain_name> [--project <id>]
|
|
13
|
-
list [--project <id>]
|
|
14
|
-
status <domain> [--project <id>]
|
|
15
|
-
delete <domain> --confirm [--project <id>]
|
|
12
|
+
add <domain> <subdomain_name> [--project <id>] [--auth principal|service-key]
|
|
13
|
+
list [--project <id>] [--auth principal|service-key]
|
|
14
|
+
status <domain> [--project <id>] [--auth principal|service-key]
|
|
15
|
+
delete <domain> --confirm [--project <id>] [--auth principal|service-key]
|
|
16
16
|
|
|
17
17
|
Examples:
|
|
18
18
|
run402 domains add example.com myapp
|
|
19
|
-
|
|
19
|
+
run402 domains add example.com myapp --project prj_123
|
|
20
|
+
run402 domains list --project prj_123 --auth service-key
|
|
20
21
|
run402 domains list
|
|
21
22
|
run402 domains status example.com
|
|
22
23
|
run402 domains delete example.com --confirm
|
|
@@ -31,7 +32,7 @@ const SUB_HELP = {
|
|
|
31
32
|
add: `run402 domains add — Register a custom domain for a project
|
|
32
33
|
|
|
33
34
|
Usage:
|
|
34
|
-
run402 domains add <domain> <subdomain_name> [--project <id>]
|
|
35
|
+
run402 domains add <domain> <subdomain_name> [--project <id>] [--auth principal|service-key]
|
|
35
36
|
|
|
36
37
|
Arguments:
|
|
37
38
|
<domain> Custom domain (e.g. example.com)
|
|
@@ -39,6 +40,8 @@ Arguments:
|
|
|
39
40
|
|
|
40
41
|
Options:
|
|
41
42
|
--project <id> Project ID (defaults to the active project)
|
|
43
|
+
--auth <mode> principal (default, server-authoritative) or service-key
|
|
44
|
+
(uses local project-key cache)
|
|
42
45
|
|
|
43
46
|
Notes:
|
|
44
47
|
- After adding, configure DNS as shown in the response
|
|
@@ -52,10 +55,14 @@ Examples:
|
|
|
52
55
|
list: `run402 domains list — List custom domains for a project
|
|
53
56
|
|
|
54
57
|
Usage:
|
|
55
|
-
run402 domains list [--project <id>]
|
|
58
|
+
run402 domains list [--project <id>] [--auth principal|service-key]
|
|
56
59
|
|
|
57
|
-
Arguments:
|
|
58
|
-
|
|
60
|
+
Arguments:
|
|
61
|
+
<id> Project ID (defaults to the active project)
|
|
62
|
+
|
|
63
|
+
Options:
|
|
64
|
+
--auth <mode> principal (default, server-authoritative) or service-key
|
|
65
|
+
(uses local project-key cache)
|
|
59
66
|
|
|
60
67
|
Examples:
|
|
61
68
|
run402 domains list
|
|
@@ -64,13 +71,15 @@ Examples:
|
|
|
64
71
|
status: `run402 domains status — Check DNS/SSL status of a custom domain
|
|
65
72
|
|
|
66
73
|
Usage:
|
|
67
|
-
run402 domains status <domain> [--project <id>]
|
|
74
|
+
run402 domains status <domain> [--project <id>] [--auth principal|service-key]
|
|
68
75
|
|
|
69
76
|
Arguments:
|
|
70
77
|
<domain> Custom domain to check
|
|
71
78
|
|
|
72
79
|
Options:
|
|
73
80
|
--project <id> Project ID (defaults to the active project)
|
|
81
|
+
--auth <mode> principal (default, server-authoritative) or service-key
|
|
82
|
+
(uses local project-key cache)
|
|
74
83
|
|
|
75
84
|
Examples:
|
|
76
85
|
run402 domains status example.com
|
|
@@ -79,7 +88,7 @@ Examples:
|
|
|
79
88
|
delete: `run402 domains delete — Release a custom domain
|
|
80
89
|
|
|
81
90
|
Usage:
|
|
82
|
-
run402 domains delete <domain> --confirm [--project <id>]
|
|
91
|
+
run402 domains delete <domain> --confirm [--project <id>] [--auth principal|service-key]
|
|
83
92
|
|
|
84
93
|
Arguments:
|
|
85
94
|
<domain> Custom domain to release
|
|
@@ -89,6 +98,8 @@ Options:
|
|
|
89
98
|
project and clears its DNS/SSL configuration
|
|
90
99
|
(irreversible)
|
|
91
100
|
--project <id> Project ID (defaults to the active project)
|
|
101
|
+
--auth <mode> principal (default, server-authoritative) or service-key
|
|
102
|
+
(uses local project-key cache)
|
|
92
103
|
|
|
93
104
|
Examples:
|
|
94
105
|
run402 domains delete example.com --confirm
|
|
@@ -97,17 +108,26 @@ Examples:
|
|
|
97
108
|
|
|
98
109
|
function parseProjectFlag(args, extraKnown = []) {
|
|
99
110
|
const parsedArgs = normalizeArgv(args);
|
|
100
|
-
const valueFlags = ["--project"];
|
|
111
|
+
const valueFlags = ["--project", "--auth"];
|
|
101
112
|
assertKnownFlags(parsedArgs, [...valueFlags, ...extraKnown, "--help", "-h"], valueFlags);
|
|
113
|
+
const auth = flagValue(parsedArgs, "--auth") ?? "principal";
|
|
114
|
+
if (auth !== "principal" && auth !== "service-key") {
|
|
115
|
+
fail({
|
|
116
|
+
code: "BAD_FLAG",
|
|
117
|
+
message: "--auth must be one of: principal, service-key.",
|
|
118
|
+
details: { flag: "--auth", value: auth, allowed: ["principal", "service-key"] },
|
|
119
|
+
});
|
|
120
|
+
}
|
|
102
121
|
return {
|
|
103
122
|
project: flagValue(parsedArgs, "--project"),
|
|
123
|
+
authMode: auth === "service-key" ? "service_key" : "principal",
|
|
104
124
|
rest: positionalArgs(parsedArgs, valueFlags),
|
|
105
125
|
args: parsedArgs,
|
|
106
126
|
};
|
|
107
127
|
}
|
|
108
128
|
|
|
109
129
|
async function add(args) {
|
|
110
|
-
const { project, rest } = parseProjectFlag(args);
|
|
130
|
+
const { project, authMode, rest } = parseProjectFlag(args);
|
|
111
131
|
const domain = rest[0];
|
|
112
132
|
const subdomainName = rest[1];
|
|
113
133
|
if (!domain || !subdomainName) {
|
|
@@ -122,7 +142,7 @@ async function add(args) {
|
|
|
122
142
|
}
|
|
123
143
|
const projectId = resolveProjectId(project);
|
|
124
144
|
try {
|
|
125
|
-
const data = await getSdk().domains.add(projectId, { domain, subdomainName });
|
|
145
|
+
const data = await getSdk().domains.add(projectId, { domain, subdomainName, authMode });
|
|
126
146
|
console.log(JSON.stringify(data, null, 2));
|
|
127
147
|
} catch (err) {
|
|
128
148
|
reportSdkError(err);
|
|
@@ -131,7 +151,7 @@ async function add(args) {
|
|
|
131
151
|
|
|
132
152
|
async function list(args) {
|
|
133
153
|
const argList = Array.isArray(args) ? args : [];
|
|
134
|
-
const { project, rest } = parseProjectFlag(argList);
|
|
154
|
+
const { project, authMode, rest } = parseProjectFlag(argList);
|
|
135
155
|
if (rest.length > 0) {
|
|
136
156
|
fail({
|
|
137
157
|
code: "BAD_USAGE",
|
|
@@ -141,7 +161,7 @@ async function list(args) {
|
|
|
141
161
|
}
|
|
142
162
|
const projectId = resolveProjectId(project);
|
|
143
163
|
try {
|
|
144
|
-
const data = await getSdk().domains.list(projectId);
|
|
164
|
+
const data = await getSdk().domains.list(projectId, { authMode });
|
|
145
165
|
console.log(JSON.stringify(data, null, 2));
|
|
146
166
|
} catch (err) {
|
|
147
167
|
reportSdkError(err);
|
|
@@ -149,7 +169,7 @@ async function list(args) {
|
|
|
149
169
|
}
|
|
150
170
|
|
|
151
171
|
async function status(args) {
|
|
152
|
-
const { project, rest } = parseProjectFlag(args);
|
|
172
|
+
const { project, authMode, rest } = parseProjectFlag(args);
|
|
153
173
|
const domain = rest[0];
|
|
154
174
|
if (!domain) {
|
|
155
175
|
fail({
|
|
@@ -163,7 +183,7 @@ async function status(args) {
|
|
|
163
183
|
}
|
|
164
184
|
const projectId = resolveProjectId(project);
|
|
165
185
|
try {
|
|
166
|
-
const data = await getSdk().domains.status(projectId, domain);
|
|
186
|
+
const data = await getSdk().domains.status(projectId, domain, { authMode });
|
|
167
187
|
console.log(JSON.stringify(data, null, 2));
|
|
168
188
|
} catch (err) {
|
|
169
189
|
reportSdkError(err);
|
|
@@ -171,7 +191,7 @@ async function status(args) {
|
|
|
171
191
|
}
|
|
172
192
|
|
|
173
193
|
async function deleteDomain(args) {
|
|
174
|
-
const { project, rest, args: parsedArgs } = parseProjectFlag(args, ["--confirm"]);
|
|
194
|
+
const { project, authMode, rest, args: parsedArgs } = parseProjectFlag(args, ["--confirm"]);
|
|
175
195
|
const domain = rest[0];
|
|
176
196
|
if (!domain) {
|
|
177
197
|
fail({
|
|
@@ -192,7 +212,7 @@ async function deleteDomain(args) {
|
|
|
192
212
|
}
|
|
193
213
|
const projectId = resolveProjectId(project);
|
|
194
214
|
try {
|
|
195
|
-
await getSdk().domains.remove(domain, { projectId });
|
|
215
|
+
await getSdk().domains.remove(domain, { projectId, authMode });
|
|
196
216
|
console.log(JSON.stringify({ domain, project_id: projectId, released: true }));
|
|
197
217
|
} catch (err) {
|
|
198
218
|
reportSdkError(err);
|
package/lib/next-actions.mjs
CHANGED
|
@@ -46,6 +46,13 @@ export function createProjectAction() {
|
|
|
46
46
|
});
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
export function selectProjectAction() {
|
|
50
|
+
return nextAction("edit_request", {
|
|
51
|
+
command: "run402 projects use <project_id>",
|
|
52
|
+
why: "Select a server-visible project, or pass --project <project_id> on the command.",
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
49
56
|
export function setTierAction(tier = "prototype") {
|
|
50
57
|
return nextAction("renew_tier", {
|
|
51
58
|
command: `run402 tier set ${tier}`,
|