run402 4.94.0 → 4.95.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -20
- package/cli.mjs +7 -19
- package/gitvault-surface.json +2 -2
- package/lib/admin.mjs +5 -5
- package/lib/ai.mjs +5 -5
- package/lib/apps.mjs +9 -9
- package/lib/archives.mjs +371 -8
- package/lib/argparse.mjs +5 -5
- package/lib/assets.mjs +12 -12
- package/lib/auth.mjs +34 -34
- package/lib/branches.mjs +7 -7
- package/lib/buzz-doctor.mjs +1 -1
- package/lib/buzz-notifications.mjs +4 -4
- package/lib/buzz.mjs +2 -2
- package/lib/cache.mjs +1 -1
- package/lib/cdn.mjs +2 -2
- package/lib/ci.mjs +8 -8
- package/lib/claims.mjs +2 -2
- package/lib/command-manifest.mjs +35 -36
- package/lib/config.mjs +4 -4
- package/lib/contacts.mjs +3 -3
- package/lib/contracts.mjs +11 -11
- package/lib/credentials.mjs +31 -31
- package/lib/delegates.mjs +13 -13
- package/lib/deliveries.mjs +2 -2
- package/lib/deploy-v2.mjs +9 -9
- package/lib/dev.mjs +3 -3
- package/lib/doctor.mjs +6 -6
- package/lib/domains.mjs +27 -27
- package/lib/email.mjs +38 -38
- package/lib/errors.mjs +40 -24
- package/lib/escalations.mjs +4 -4
- package/lib/events.mjs +32 -25
- package/lib/feedback.mjs +2 -2
- package/lib/functions.mjs +34 -34
- package/lib/gitvault-scaffold.mjs +2 -2
- package/lib/gitvault.mjs +2 -2
- package/lib/grants.mjs +7 -7
- package/lib/image.mjs +7 -7
- package/lib/init.mjs +3 -3
- package/lib/jobs.mjs +15 -15
- package/lib/live.mjs +1 -1
- package/lib/logs.mjs +8 -8
- package/lib/messages.mjs +5 -5
- package/lib/next-actions.mjs +1 -1
- package/lib/org-context.mjs +13 -13
- package/lib/{org.mjs → orgs.mjs} +124 -163
- package/lib/projects.mjs +45 -56
- package/lib/repos.mjs +23 -23
- package/lib/rooms.mjs +2 -2
- package/lib/sdk-errors.mjs +1 -1
- package/lib/secrets.mjs +13 -13
- package/lib/sites.mjs +14 -14
- package/lib/snapshots.mjs +10 -10
- package/lib/subdomains.mjs +12 -12
- package/lib/subscriptions.mjs +2 -2
- package/lib/transfer.mjs +1 -1
- package/lib/up.mjs +7 -7
- package/lib/wallet-context.mjs +1 -1
- package/lib/wallets.mjs +1 -1
- package/lib/webhooks.mjs +8 -8
- package/lib/whoami.mjs +32 -5
- package/package.json +1 -1
- package/sdk/dist/actions.d.ts +3 -3
- package/sdk/dist/actions.d.ts.map +1 -1
- package/sdk/dist/app-up.d.ts +2 -2
- package/sdk/dist/app-up.d.ts.map +1 -1
- package/sdk/dist/config.d.ts +1 -2
- package/sdk/dist/config.d.ts.map +1 -1
- package/sdk/dist/config.js.map +1 -1
- package/sdk/dist/credentials.d.ts +1 -1
- package/sdk/dist/errors.d.ts +2 -2
- package/sdk/dist/errors.d.ts.map +1 -1
- package/sdk/dist/errors.js +1 -1
- package/sdk/dist/errors.js.map +1 -1
- package/sdk/dist/index.js +1 -1
- package/sdk/dist/index.js.map +1 -1
- package/sdk/dist/namespaces/admin.d.ts +1 -1
- package/sdk/dist/namespaces/admin.d.ts.map +1 -1
- package/sdk/dist/namespaces/ai.d.ts +1 -1
- package/sdk/dist/namespaces/ai.js +2 -2
- package/sdk/dist/namespaces/ai.js.map +1 -1
- package/sdk/dist/namespaces/assets.js +1 -1
- package/sdk/dist/namespaces/assets.js.map +1 -1
- package/sdk/dist/namespaces/ci.js +3 -3
- package/sdk/dist/namespaces/ci.js.map +1 -1
- package/sdk/dist/namespaces/deploy.d.ts.map +1 -1
- package/sdk/dist/namespaces/deploy.js +26 -27
- package/sdk/dist/namespaces/deploy.js.map +1 -1
- package/sdk/dist/namespaces/deploy.types.d.ts +2 -2
- package/sdk/dist/namespaces/deploy.types.d.ts.map +1 -1
- package/sdk/dist/namespaces/domains.d.ts +10 -2
- package/sdk/dist/namespaces/domains.d.ts.map +1 -1
- package/sdk/dist/namespaces/domains.js +10 -5
- package/sdk/dist/namespaces/domains.js.map +1 -1
- package/sdk/dist/namespaces/errors.js +1 -1
- package/sdk/dist/namespaces/functions.js +1 -1
- package/sdk/dist/namespaces/functions.js.map +1 -1
- package/sdk/dist/namespaces/gitvault.d.ts +2 -2
- package/sdk/dist/namespaces/gitvault.js +4 -4
- package/sdk/dist/namespaces/gitvault.js.map +1 -1
- package/sdk/dist/namespaces/tier.d.ts +1 -1
- package/sdk/dist/namespaces/transfers.d.ts +46 -15
- package/sdk/dist/namespaces/transfers.d.ts.map +1 -1
- package/sdk/dist/namespaces/transfers.js.map +1 -1
- package/sdk/dist/node/actions-node.js +8 -8
- package/sdk/dist/node/actions-node.js.map +1 -1
- package/sdk/dist/node/assets-node.js +6 -6
- package/sdk/dist/node/assets-node.js.map +1 -1
- package/sdk/dist/node/ci.js +1 -1
- package/sdk/dist/node/ci.js.map +1 -1
- package/sdk/dist/node/credentials.d.ts +1 -1
- package/sdk/dist/node/credentials.js +1 -1
- package/sdk/dist/node/deploy-manifest.d.ts +3 -5
- package/sdk/dist/node/deploy-manifest.d.ts.map +1 -1
- package/sdk/dist/node/deploy-manifest.js +3 -12
- package/sdk/dist/node/deploy-manifest.js.map +1 -1
- package/sdk/dist/node/gitvault-apply.js +1 -1
- package/sdk/dist/node/gitvault-apply.js.map +1 -1
- package/sdk/dist/node/manifest-export.js +2 -2
- package/sdk/dist/node/manifest-export.js.map +1 -1
- package/sdk/dist/node/org-adopt.js +1 -1
- package/sdk/dist/node/org-adopt.js.map +1 -1
- package/sdk/dist/node/sites-node.js +1 -1
- package/sdk/dist/node/sites-node.js.map +1 -1
- package/sdk/dist/scoped.d.ts +8 -8
- package/sdk/dist/scoped.d.ts.map +1 -1
- package/sdk/dist/scoped.js +3 -3
- package/sdk/dist/scoped.js.map +1 -1
- package/lib/cloud.mjs +0 -256
- package/lib/core.mjs +0 -123
package/lib/archives.mjs
CHANGED
|
@@ -1,18 +1,106 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { assertKnownFlags, hasHelp, normalizeArgv, positionalArgs, failUnknownSubcommand } from "./argparse.mjs";
|
|
1
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
4
3
|
|
|
5
|
-
|
|
4
|
+
import { importArchiveToCore, inspectArchive, verifyArchive } from "#sdk/node";
|
|
5
|
+
import { getSdk } from "./sdk.mjs";
|
|
6
|
+
import { fail, reportSdkError } from "./sdk-errors.mjs";
|
|
7
|
+
import { assertAllowedValue, assertKnownFlags, flagValue, hasHelp, normalizeArgv, parseIntegerFlag, positionalArgs, resolveProjectSelector, failUnknownSubcommand } from "./argparse.mjs";
|
|
8
|
+
|
|
9
|
+
const HELP = `run402 archives — portable Run402 project archives
|
|
6
10
|
|
|
7
11
|
Usage:
|
|
8
|
-
run402 archives
|
|
9
|
-
run402 archives
|
|
12
|
+
run402 archives create [<project_id>] [--project <project_id>] [--target cloud] [options]
|
|
13
|
+
run402 archives status [<project_id>] <archive_id> [--project <project_id>] [--target cloud] [--json]
|
|
14
|
+
run402 archives download [<project_id>] <archive_id> --output <file> [--project <project_id>] [--target cloud] [--json]
|
|
15
|
+
run402 archives inspect <archive_path> [--json]
|
|
16
|
+
run402 archives verify <archive_path> [--json]
|
|
17
|
+
run402 archives import <archive_path> --name <project_name> [--target core] [options]
|
|
18
|
+
|
|
19
|
+
--target names the Run402 deployment the command talks to: create, status, and
|
|
20
|
+
download export from Run402 Cloud (--target cloud, the default); import loads an
|
|
21
|
+
archive into a local Run402 Core (--target core, the default). inspect and verify
|
|
22
|
+
are local and offline and take no target. A leading prj_... positional selects
|
|
23
|
+
the project; --project defaults to the active project.
|
|
24
|
+
|
|
25
|
+
Canonical agent path:
|
|
26
|
+
run402 archives create <project_id> --target cloud \\
|
|
27
|
+
--scope portable-runtime-v1 --auth stubs --consistency pause-writes \\
|
|
28
|
+
--wait --output ./project.r402ar --json
|
|
29
|
+
run402 archives verify ./project.r402ar --json
|
|
30
|
+
run402 archives import ./project.r402ar --target core --name imported-project --env-file ./required.env --json
|
|
31
|
+
|
|
32
|
+
Options for create:
|
|
33
|
+
--scope <scope> Archive scope. v1 supports portable-runtime-v1.
|
|
34
|
+
--auth <mode> Auth export mode: stubs (default) or none.
|
|
35
|
+
--consistency <mode> pause-writes (default) or cloud_write_pause_v1.
|
|
36
|
+
--idempotency-key <key> Retry-safe creation key.
|
|
37
|
+
--wait Poll until the archive is ready.
|
|
38
|
+
--output <file> Save archive bytes. Implies --wait.
|
|
39
|
+
--poll-interval <ms> Poll interval while waiting (default 1000).
|
|
40
|
+
--timeout <ms> Wait timeout (default 600000).
|
|
41
|
+
--json Emit final JSON on stdout.
|
|
42
|
+
--json-stream Emit NDJSON progress events on stdout.
|
|
43
|
+
|
|
44
|
+
Options for import:
|
|
45
|
+
--name <name> New Core project name (default imported-project)
|
|
46
|
+
--env-file <path> Env file satisfying required archive secrets.
|
|
47
|
+
--secret KEY=VALUE Inline secret value; repeatable. Overrides --env-file.
|
|
48
|
+
--core-url <url> Core gateway URL (default RUN402_CORE_URL or http://127.0.0.1:4020)
|
|
49
|
+
--dry-run Verify and plan without creating a Core project.
|
|
50
|
+
--require-runnable Block import unless required secrets are supplied.
|
|
51
|
+
--json Emit final JSON.
|
|
52
|
+
--json-stream Emit NDJSON progress events and final result event.
|
|
10
53
|
|
|
11
54
|
Notes:
|
|
12
55
|
- Verification is local and offline. It does not require Cloud credentials.
|
|
13
56
|
- Archives are untrusted input; verify checks integrity and compatibility, not trust.
|
|
14
57
|
`;
|
|
15
58
|
|
|
59
|
+
const TARGETS = ["cloud", "core"];
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Read --target and require the one deployment kind this verb talks to:
|
|
63
|
+
* Run402 Cloud exports archives, Run402 Core imports them.
|
|
64
|
+
*/
|
|
65
|
+
function requireTarget(args, verb, expected) {
|
|
66
|
+
const target = flagValue(args, "--target") ?? expected;
|
|
67
|
+
assertAllowedValue(target, TARGETS, "--target");
|
|
68
|
+
if (target !== expected) {
|
|
69
|
+
fail({
|
|
70
|
+
code: "BAD_FLAG",
|
|
71
|
+
message: `archives ${verb} runs against --target ${expected}; --target ${target} does not ${expected === "cloud" ? "export archives" : "import archives"}.`,
|
|
72
|
+
details: { flag: "--target", value: target, expected },
|
|
73
|
+
hint: expected === "cloud"
|
|
74
|
+
? "Export with --target cloud, then load the file with: run402 archives import <archive_path> --target core"
|
|
75
|
+
: "Import into Run402 Core with --target core; export from Run402 Cloud with: run402 archives create --target cloud",
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return target;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const CREATE_FLAG_VALUES = [
|
|
82
|
+
"--project",
|
|
83
|
+
"--target",
|
|
84
|
+
"--scope",
|
|
85
|
+
"--auth",
|
|
86
|
+
"--consistency",
|
|
87
|
+
"--idempotency-key",
|
|
88
|
+
"--output",
|
|
89
|
+
"--poll-interval",
|
|
90
|
+
"--timeout",
|
|
91
|
+
];
|
|
92
|
+
const CREATE_FLAGS = new Set([
|
|
93
|
+
...CREATE_FLAG_VALUES,
|
|
94
|
+
"--wait",
|
|
95
|
+
"--json",
|
|
96
|
+
"--json-stream",
|
|
97
|
+
"--help",
|
|
98
|
+
"-h",
|
|
99
|
+
]);
|
|
100
|
+
|
|
101
|
+
const IMPORT_FLAG_VALUES = ["--target", "--name", "--env-file", "--secret", "--core-url"];
|
|
102
|
+
const IMPORT_FLAGS = new Set([...IMPORT_FLAG_VALUES, "--dry-run", "--require-runnable", "--json", "--json-stream", "--help", "-h"]);
|
|
103
|
+
|
|
16
104
|
export async function run(sub, rawArgs = []) {
|
|
17
105
|
const all = [sub, ...rawArgs].filter(Boolean);
|
|
18
106
|
if (hasHelp(all)) {
|
|
@@ -20,18 +108,206 @@ export async function run(sub, rawArgs = []) {
|
|
|
20
108
|
return;
|
|
21
109
|
}
|
|
22
110
|
switch (sub) {
|
|
111
|
+
case "create": return create(rawArgs);
|
|
112
|
+
case "status": return status(rawArgs);
|
|
113
|
+
case "download": return download(rawArgs);
|
|
23
114
|
case "inspect": return inspect(rawArgs);
|
|
24
115
|
case "verify": return verify(rawArgs);
|
|
116
|
+
case "import": return importProject(rawArgs);
|
|
25
117
|
default:
|
|
26
118
|
failUnknownSubcommand("archives", sub);
|
|
27
119
|
}
|
|
28
120
|
}
|
|
29
121
|
|
|
122
|
+
async function create(rawArgs) {
|
|
123
|
+
const args = normalizeArgv(rawArgs);
|
|
124
|
+
assertKnownFlags(args, [...CREATE_FLAGS], CREATE_FLAG_VALUES);
|
|
125
|
+
requireTarget(args, "create", "cloud");
|
|
126
|
+
const { projectId, rest } = resolveProjectSelector(args, { valueFlags: CREATE_FLAG_VALUES });
|
|
127
|
+
const extraPos = positionalArgs(rest, CREATE_FLAG_VALUES);
|
|
128
|
+
if (extraPos.length > 0) {
|
|
129
|
+
fail({ code: "BAD_USAGE", message: `Unexpected argument for archives create: ${extraPos[0]}` });
|
|
130
|
+
}
|
|
131
|
+
const scope = flagValue(args, "--scope") ?? "portable-runtime-v1";
|
|
132
|
+
const auth = flagValue(args, "--auth") ?? "stubs";
|
|
133
|
+
const consistency = flagValue(args, "--consistency") ?? "pause-writes";
|
|
134
|
+
assertAllowedValue(scope, ["portable-runtime-v1"], "--scope");
|
|
135
|
+
assertAllowedValue(auth, ["stubs", "none"], "--auth");
|
|
136
|
+
assertAllowedValue(consistency, ["pause-writes", "cloud_write_pause_v1"], "--consistency");
|
|
137
|
+
|
|
138
|
+
const output = flagValue(args, "--output");
|
|
139
|
+
const wait = args.includes("--wait") || Boolean(output);
|
|
140
|
+
const jsonStream = args.includes("--json-stream");
|
|
141
|
+
const idempotencyKey = flagValue(args, "--idempotency-key") ?? undefined;
|
|
142
|
+
const pollIntervalMs = parseIntegerFlag("--poll-interval", flagValue(args, "--poll-interval"), { min: 100, def: 1000 });
|
|
143
|
+
const timeoutMs = parseIntegerFlag("--timeout", flagValue(args, "--timeout"), { min: 1000, def: 600000 });
|
|
144
|
+
const emit = (event) => {
|
|
145
|
+
if (jsonStream) console.log(JSON.stringify(event));
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
const sdk = getSdk();
|
|
150
|
+
const created = await sdk.archives.create(projectId, {
|
|
151
|
+
scope,
|
|
152
|
+
auth,
|
|
153
|
+
consistency,
|
|
154
|
+
idempotencyKey,
|
|
155
|
+
});
|
|
156
|
+
emit(progressEvent("archive_export_created", "create", projectId, created));
|
|
157
|
+
|
|
158
|
+
let archive = created;
|
|
159
|
+
let outputPath = null;
|
|
160
|
+
let bytesWritten = 0;
|
|
161
|
+
if (wait) {
|
|
162
|
+
archive = created.status === "ready"
|
|
163
|
+
? created
|
|
164
|
+
: await sdk.archives.wait(projectId, created.archive_id, {
|
|
165
|
+
pollIntervalMs,
|
|
166
|
+
timeoutMs,
|
|
167
|
+
onProgress: emit,
|
|
168
|
+
});
|
|
169
|
+
if (archive.status !== "ready") {
|
|
170
|
+
const result = finalCreateResult({ projectId, created, archive, outputPath, bytesWritten });
|
|
171
|
+
printJson(result, jsonStream);
|
|
172
|
+
process.exit(1);
|
|
173
|
+
}
|
|
174
|
+
if (output) {
|
|
175
|
+
const download = await sdk.archives.download(projectId, archive.archive_id);
|
|
176
|
+
outputPath = resolve(output);
|
|
177
|
+
mkdirSync(dirname(outputPath), { recursive: true });
|
|
178
|
+
writeFileSync(outputPath, download.bytes);
|
|
179
|
+
bytesWritten = download.bytes.byteLength;
|
|
180
|
+
emit({
|
|
181
|
+
...progressEvent("archive_export_downloaded", "download", projectId, archive),
|
|
182
|
+
context: { output_path: outputPath, bytes_written: bytesWritten },
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const result = finalCreateResult({ projectId, created, archive, outputPath, bytesWritten });
|
|
188
|
+
if (jsonStream) {
|
|
189
|
+
console.log(JSON.stringify({
|
|
190
|
+
event: "archive_export_complete",
|
|
191
|
+
stage: "complete",
|
|
192
|
+
resource_type: "project_archive",
|
|
193
|
+
resource_id: archive.archive_id,
|
|
194
|
+
project_id: projectId,
|
|
195
|
+
status: archive.status === "ready" ? "complete" : archive.status,
|
|
196
|
+
completed_units: archive.status === "ready" ? 1 : 0,
|
|
197
|
+
total_units: 1,
|
|
198
|
+
code: null,
|
|
199
|
+
message: archive.status === "ready" ? "Archive export complete." : "Archive export did not complete.",
|
|
200
|
+
next_action: archive.next_action,
|
|
201
|
+
retryable: false,
|
|
202
|
+
result,
|
|
203
|
+
}));
|
|
204
|
+
} else {
|
|
205
|
+
console.log(JSON.stringify(result, null, 2));
|
|
206
|
+
}
|
|
207
|
+
if (archive.status !== "ready") process.exit(1);
|
|
208
|
+
} catch (err) {
|
|
209
|
+
reportSdkError(err);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async function download(rawArgs) {
|
|
214
|
+
const args = normalizeArgv(rawArgs);
|
|
215
|
+
assertKnownFlags(args, ["--project", "--target", "--output", "--json", "--help", "-h"], ["--project", "--target", "--output"]);
|
|
216
|
+
requireTarget(args, "download", "cloud");
|
|
217
|
+
const { projectId, rest } = resolveProjectSelector(args, { valueFlags: ["--project", "--target", "--output"] });
|
|
218
|
+
const pos = positionalArgs(rest, ["--project", "--target", "--output"]);
|
|
219
|
+
const [archiveId] = pos;
|
|
220
|
+
const output = flagValue(args, "--output");
|
|
221
|
+
if (!archiveId || pos.length > 1 || !output) {
|
|
222
|
+
fail({ code: "BAD_USAGE", message: "Usage: run402 archives download [<project_id>] <archive_id> --output <file> [--project <project_id>] [--target cloud] [--json]" });
|
|
223
|
+
}
|
|
224
|
+
try {
|
|
225
|
+
const download = await getSdk().archives.download(projectId, archiveId);
|
|
226
|
+
const outputPath = resolve(output);
|
|
227
|
+
mkdirSync(dirname(outputPath), { recursive: true });
|
|
228
|
+
writeFileSync(outputPath, download.bytes);
|
|
229
|
+
console.log(JSON.stringify({
|
|
230
|
+
ok: true,
|
|
231
|
+
project_id: projectId,
|
|
232
|
+
archive_id: archiveId,
|
|
233
|
+
output_path: outputPath,
|
|
234
|
+
bytes_written: download.bytes.byteLength,
|
|
235
|
+
sha256: download.archive.sha256,
|
|
236
|
+
verify_command: `run402 archives verify ${JSON.stringify(outputPath)} --json`,
|
|
237
|
+
import_command: `run402 archives import ${JSON.stringify(outputPath)} --target core --name imported-project --env-file ./required.env --json`,
|
|
238
|
+
archive: download.archive,
|
|
239
|
+
}, null, 2));
|
|
240
|
+
} catch (err) {
|
|
241
|
+
reportSdkError(err);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
async function status(rawArgs) {
|
|
246
|
+
const args = normalizeArgv(rawArgs);
|
|
247
|
+
assertKnownFlags(args, ["--project", "--target", "--json", "--help", "-h"], ["--project", "--target"]);
|
|
248
|
+
requireTarget(args, "status", "cloud");
|
|
249
|
+
const { projectId, rest } = resolveProjectSelector(args, { valueFlags: ["--project", "--target"] });
|
|
250
|
+
const pos = positionalArgs(rest, ["--project", "--target"]);
|
|
251
|
+
const [archiveId] = pos;
|
|
252
|
+
if (!archiveId || pos.length > 1) {
|
|
253
|
+
fail({ code: "BAD_USAGE", message: "Usage: run402 archives status [<project_id>] <archive_id> [--project <project_id>] [--target cloud] [--json]" });
|
|
254
|
+
}
|
|
255
|
+
try {
|
|
256
|
+
const archive = await getSdk().archives.get(projectId, archiveId);
|
|
257
|
+
console.log(JSON.stringify({ archive }, null, 2));
|
|
258
|
+
} catch (err) {
|
|
259
|
+
reportSdkError(err);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function finalCreateResult({ projectId, created, archive, outputPath, bytesWritten }) {
|
|
264
|
+
return {
|
|
265
|
+
ok: archive.status === "ready",
|
|
266
|
+
project_id: projectId,
|
|
267
|
+
archive_id: archive.archive_id,
|
|
268
|
+
operation_id: archive.operation_id,
|
|
269
|
+
created_archive_id: created.archive_id,
|
|
270
|
+
archive_status: archive.status,
|
|
271
|
+
output_path: outputPath,
|
|
272
|
+
bytes_written: bytesWritten,
|
|
273
|
+
sha256: archive.sha256,
|
|
274
|
+
byte_count: archive.byte_count,
|
|
275
|
+
expires_at: archive.expires_at,
|
|
276
|
+
verify_command: outputPath ? `run402 archives verify ${JSON.stringify(outputPath)} --json` : null,
|
|
277
|
+
import_command: outputPath ? `run402 archives import ${JSON.stringify(outputPath)} --target core --name imported-project --env-file ./required.env --json` : null,
|
|
278
|
+
next_action: archive.next_action,
|
|
279
|
+
portability_report: archive.portability_report,
|
|
280
|
+
export_report: archive.export_report,
|
|
281
|
+
archive,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function progressEvent(event, stage, projectId, archive) {
|
|
286
|
+
return {
|
|
287
|
+
event,
|
|
288
|
+
stage,
|
|
289
|
+
resource_type: "project_archive",
|
|
290
|
+
resource_id: archive.archive_id,
|
|
291
|
+
project_id: projectId,
|
|
292
|
+
status: archive.status,
|
|
293
|
+
completed_units: archive.status === "ready" || archive.status === "failed" || archive.status === "expired" ? 1 : 0,
|
|
294
|
+
total_units: 1,
|
|
295
|
+
code: archive.status === "failed" ? archive.error?.code ?? "ARCHIVE_EXPORT_FAILED" : null,
|
|
296
|
+
message: `Archive export status: ${archive.status}`,
|
|
297
|
+
next_action: archive.next_action,
|
|
298
|
+
retryable: archive.status === "running",
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function printJson(result, alreadyStreamed) {
|
|
303
|
+
if (!alreadyStreamed) console.log(JSON.stringify(result, null, 2));
|
|
304
|
+
}
|
|
305
|
+
|
|
30
306
|
async function inspect(rawArgs) {
|
|
31
307
|
const args = normalizeArgv(rawArgs);
|
|
32
308
|
assertKnownFlags(args, ["--json", "--help", "-h"], []);
|
|
33
309
|
const archivePath = positionalArgs(args, [])[0];
|
|
34
|
-
if (!archivePath) fail({ code: "BAD_USAGE", message: "Usage: run402 archives inspect <
|
|
310
|
+
if (!archivePath) fail({ code: "BAD_USAGE", message: "Usage: run402 archives inspect <archive_path> [--json]" });
|
|
35
311
|
try {
|
|
36
312
|
const result = await inspectArchive(archivePath);
|
|
37
313
|
console.log(JSON.stringify({ archive: result }, null, 2));
|
|
@@ -44,7 +320,7 @@ async function verify(rawArgs) {
|
|
|
44
320
|
const args = normalizeArgv(rawArgs);
|
|
45
321
|
assertKnownFlags(args, ["--json", "--help", "-h"], []);
|
|
46
322
|
const archivePath = positionalArgs(args, [])[0];
|
|
47
|
-
if (!archivePath) fail({ code: "BAD_USAGE", message: "Usage: run402 archives verify <
|
|
323
|
+
if (!archivePath) fail({ code: "BAD_USAGE", message: "Usage: run402 archives verify <archive_path> [--json]" });
|
|
48
324
|
try {
|
|
49
325
|
const result = await verifyArchive(archivePath);
|
|
50
326
|
console.log(JSON.stringify({ ok: result.ok, verified: result.ok, archive: result }, null, 2));
|
|
@@ -53,3 +329,90 @@ async function verify(rawArgs) {
|
|
|
53
329
|
reportSdkError(err);
|
|
54
330
|
}
|
|
55
331
|
}
|
|
332
|
+
|
|
333
|
+
async function importProject(rawArgs) {
|
|
334
|
+
const args = normalizeArgv(rawArgs);
|
|
335
|
+
assertKnownFlags(args, [...IMPORT_FLAGS], IMPORT_FLAG_VALUES);
|
|
336
|
+
requireTarget(args, "import", "core");
|
|
337
|
+
const archivePath = positionalArgs(args, IMPORT_FLAG_VALUES)[0];
|
|
338
|
+
if (!archivePath) {
|
|
339
|
+
fail({ code: "BAD_USAGE", message: "Usage: run402 archives import <archive_path> --name <project_name> [--target core] [options]" });
|
|
340
|
+
}
|
|
341
|
+
const jsonStream = args.includes("--json-stream");
|
|
342
|
+
const secretValues = parseSecrets(args);
|
|
343
|
+
const name = flagValue(args, "--name") ?? "imported-project";
|
|
344
|
+
const coreUrl = flagValue(args, "--core-url") ?? undefined;
|
|
345
|
+
const startedEvent = {
|
|
346
|
+
event: "core_archive_import_started",
|
|
347
|
+
stage: "verify",
|
|
348
|
+
resource_type: "project_archive",
|
|
349
|
+
resource_id: archivePath,
|
|
350
|
+
status: "running",
|
|
351
|
+
completed_units: 0,
|
|
352
|
+
total_units: 1,
|
|
353
|
+
code: null,
|
|
354
|
+
message: "Verifying archive locally before Core import.",
|
|
355
|
+
next_action: { type: "none" },
|
|
356
|
+
retryable: false,
|
|
357
|
+
context: { archive_path: archivePath, core_url: coreUrl ?? null, project_name: name },
|
|
358
|
+
};
|
|
359
|
+
if (jsonStream) console.log(JSON.stringify(startedEvent));
|
|
360
|
+
try {
|
|
361
|
+
const result = await importArchiveToCore({
|
|
362
|
+
archivePath,
|
|
363
|
+
name,
|
|
364
|
+
coreUrl,
|
|
365
|
+
envFile: flagValue(args, "--env-file") ?? undefined,
|
|
366
|
+
secretValues,
|
|
367
|
+
dryRun: args.includes("--dry-run"),
|
|
368
|
+
requireRunnable: args.includes("--require-runnable"),
|
|
369
|
+
});
|
|
370
|
+
if (jsonStream) {
|
|
371
|
+
console.log(JSON.stringify({
|
|
372
|
+
event: "core_archive_import_complete",
|
|
373
|
+
stage: "complete",
|
|
374
|
+
resource_type: "project_archive",
|
|
375
|
+
resource_id: archivePath,
|
|
376
|
+
status: result.status,
|
|
377
|
+
completed_units: result.status === "imported" || result.status === "dry_run" ? 1 : 0,
|
|
378
|
+
total_units: 1,
|
|
379
|
+
code: firstDiagnosticCode(result),
|
|
380
|
+
message: `Core archive import status: ${result.status}`,
|
|
381
|
+
next_action: result.next_action,
|
|
382
|
+
retryable: result.status === "failed" && result.diagnostics.some((d) => d.retryable),
|
|
383
|
+
result,
|
|
384
|
+
}));
|
|
385
|
+
} else {
|
|
386
|
+
console.log(JSON.stringify({ ok: result.status === "imported" || result.status === "dry_run", import: result }, null, 2));
|
|
387
|
+
}
|
|
388
|
+
if (result.status !== "imported" && result.status !== "dry_run") process.exit(1);
|
|
389
|
+
} catch (err) {
|
|
390
|
+
reportSdkError(err);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function parseSecrets(args) {
|
|
395
|
+
const out = {};
|
|
396
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
397
|
+
if (args[i] !== "--secret") continue;
|
|
398
|
+
const value = args[i + 1];
|
|
399
|
+
if (!value || value.startsWith("--")) {
|
|
400
|
+
fail({ code: "BAD_FLAG", message: "--secret requires KEY=VALUE" });
|
|
401
|
+
}
|
|
402
|
+
const eq = value.indexOf("=");
|
|
403
|
+
if (eq <= 0) {
|
|
404
|
+
fail({ code: "BAD_FLAG", message: "--secret requires KEY=VALUE", details: { value } });
|
|
405
|
+
}
|
|
406
|
+
const key = value.slice(0, eq);
|
|
407
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
|
|
408
|
+
fail({ code: "BAD_FLAG", message: `Invalid secret env var name: ${key}` });
|
|
409
|
+
}
|
|
410
|
+
out[key] = value.slice(eq + 1);
|
|
411
|
+
i += 1;
|
|
412
|
+
}
|
|
413
|
+
return out;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function firstDiagnosticCode(result) {
|
|
417
|
+
return result.diagnostics.find((d) => d.severity === "blocking")?.code ?? null;
|
|
418
|
+
}
|
package/lib/argparse.mjs
CHANGED
|
@@ -318,11 +318,11 @@ export function resolvePositionalProject(args, opts = {}) {
|
|
|
318
318
|
}
|
|
319
319
|
|
|
320
320
|
// Resolve the project id for a project-scoped command from BOTH accepted
|
|
321
|
-
// forms — the canonical `--project <
|
|
321
|
+
// forms — the canonical `--project <project_id>` flag and the legacy leading
|
|
322
322
|
// positional `prj_...` — with active-project fallback (CLI-wide convention).
|
|
323
323
|
//
|
|
324
324
|
// Precedence:
|
|
325
|
-
// 1. explicit `--project <
|
|
325
|
+
// 1. explicit `--project <project_id>` (canonical)
|
|
326
326
|
// 2. a leading positional starting with "prj_" (legacy compat, no warning)
|
|
327
327
|
// 3. the active project (`resolveProjectId(null)`)
|
|
328
328
|
//
|
|
@@ -338,7 +338,7 @@ export function resolvePositionalProject(args, opts = {}) {
|
|
|
338
338
|
// requireRestPositional: only treat a leading `prj_` positional as the
|
|
339
339
|
// project selector when at least one more bare positional follows (counted
|
|
340
340
|
// with opts.valueFlags). Needed by commands whose OWN attribute is itself a
|
|
341
|
-
// project id (e.g. `branches renew <
|
|
341
|
+
// project id (e.g. `branches renew <branch_project_id>`).
|
|
342
342
|
export function resolveProjectSelector(args, opts = {}) {
|
|
343
343
|
const list = Array.isArray(args) ? [...args] : [];
|
|
344
344
|
let flagProject = null;
|
|
@@ -361,7 +361,7 @@ export function resolveProjectSelector(args, opts = {}) {
|
|
|
361
361
|
fail({
|
|
362
362
|
code: "BAD_USAGE",
|
|
363
363
|
message: `Conflicting project ids: --project ${flagProject} vs positional ${first}`,
|
|
364
|
-
hint: "Pass the project once — prefer --project <
|
|
364
|
+
hint: "Pass the project once — prefer --project <project_id>.",
|
|
365
365
|
details: { project_flag: flagProject, positional: first },
|
|
366
366
|
});
|
|
367
367
|
}
|
|
@@ -405,7 +405,7 @@ function closestFlag(flag, candidates) {
|
|
|
405
405
|
/**
|
|
406
406
|
* Known subcommands of a command family, derived from COMMAND_MANIFEST
|
|
407
407
|
* (never hand-maintained). `family` may be multi-word for nested groups
|
|
408
|
-
* (e.g. "
|
|
408
|
+
* (e.g. "deploy releases", "email webhooks").
|
|
409
409
|
*/
|
|
410
410
|
export function knownSubcommands(family) {
|
|
411
411
|
const familyWords = String(family).split(" ").filter(Boolean);
|
package/lib/assets.mjs
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
* run402 assets — direct-to-S3 storage CLI.
|
|
3
3
|
*
|
|
4
4
|
* Usage:
|
|
5
|
-
* run402 assets put <file> [files...] [--project <
|
|
6
|
-
* run402 assets get <key> --output <file> [--project <
|
|
7
|
-
* run402 assets ls [--project <
|
|
8
|
-
* run402 assets rm <key> [--project <
|
|
9
|
-
* run402 assets sign <key> [--project <
|
|
5
|
+
* run402 assets put <file> [files...] [--project <project_id>] [--key <dest>] [--content-type <mime>] [--private] [--immutable] [--concurrency N] [--no-resume]
|
|
6
|
+
* run402 assets get <key> --output <file> [--project <project_id>]
|
|
7
|
+
* run402 assets ls [--project <project_id>] [--prefix <p>] [--limit <n>]
|
|
8
|
+
* run402 assets rm <key> [--project <project_id>]
|
|
9
|
+
* run402 assets sign <key> [--project <project_id>] [--ttl <seconds>]
|
|
10
10
|
*
|
|
11
11
|
* For any file ≤ 5 GiB a single presigned PUT is used. Larger files use S3
|
|
12
12
|
* multipart uploads with 16 MiB parts (640 parts at 10 GiB; up to 10 000
|
|
@@ -45,7 +45,7 @@ Usage:
|
|
|
45
45
|
run402 assets diagnose <url> [options]
|
|
46
46
|
|
|
47
47
|
Options:
|
|
48
|
-
--project <
|
|
48
|
+
--project <project_id> Project ID (defaults to active project from 'run402 projects use')
|
|
49
49
|
--key <dest> Destination key (put only; defaults to file basename)
|
|
50
50
|
--content-type <mime> MIME override for blob put (defaults to extension inference)
|
|
51
51
|
--private Upload as private (not served by CDN; apikey required to read)
|
|
@@ -89,7 +89,7 @@ Arguments:
|
|
|
89
89
|
<file> Path to a file (or glob); pass multiple files to batch-upload
|
|
90
90
|
|
|
91
91
|
Options:
|
|
92
|
-
--project <
|
|
92
|
+
--project <project_id> Project ID (defaults to active project from 'run402 projects use')
|
|
93
93
|
--key <dest> Destination key; defaults to file basename. Use trailing '/' as prefix.
|
|
94
94
|
--content-type <mime> MIME override; defaults to inferring from the destination key extension
|
|
95
95
|
--private Upload as private (not served by CDN; apikey required to read)
|
|
@@ -124,7 +124,7 @@ Arguments:
|
|
|
124
124
|
|
|
125
125
|
Options:
|
|
126
126
|
--output <file> Local destination path (required)
|
|
127
|
-
--project <
|
|
127
|
+
--project <project_id> Project ID (defaults to active project)
|
|
128
128
|
|
|
129
129
|
Examples:
|
|
130
130
|
run402 assets get images/logo.png --output /tmp/logo.png --project prj_abc123
|
|
@@ -135,7 +135,7 @@ Usage:
|
|
|
135
135
|
run402 assets ls [options]
|
|
136
136
|
|
|
137
137
|
Options:
|
|
138
|
-
--project <
|
|
138
|
+
--project <project_id> Project ID (defaults to active project)
|
|
139
139
|
--prefix <p> Only list keys starting with this prefix
|
|
140
140
|
--limit <n> Max results (default 100, max 1000)
|
|
141
141
|
--sort <key> v1.50: key:asc (default) | createdAt:asc | createdAt:desc.
|
|
@@ -162,7 +162,7 @@ Arguments:
|
|
|
162
162
|
<key> Blob key to delete
|
|
163
163
|
|
|
164
164
|
Options:
|
|
165
|
-
--project <
|
|
165
|
+
--project <project_id> Project ID (defaults to active project)
|
|
166
166
|
|
|
167
167
|
Examples:
|
|
168
168
|
run402 assets rm images/logo.png --project prj_abc123
|
|
@@ -176,7 +176,7 @@ Arguments:
|
|
|
176
176
|
<key> Blob key to sign
|
|
177
177
|
|
|
178
178
|
Options:
|
|
179
|
-
--project <
|
|
179
|
+
--project <project_id> Project ID (defaults to active project)
|
|
180
180
|
--ttl <seconds> Signed-URL TTL (default 3600, min 60, max 604800)
|
|
181
181
|
|
|
182
182
|
Examples:
|
|
@@ -191,7 +191,7 @@ Arguments:
|
|
|
191
191
|
<url> Full blob URL (e.g. https://app.run402.com/_blob/avatar.png)
|
|
192
192
|
|
|
193
193
|
Options:
|
|
194
|
-
--project <
|
|
194
|
+
--project <project_id> Project ID (defaults to active project)
|
|
195
195
|
|
|
196
196
|
Output:
|
|
197
197
|
- Prints the JSON envelope on stdout (parseable by agent shell loops).
|