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.
Files changed (131) hide show
  1. package/README.md +20 -20
  2. package/cli.mjs +7 -19
  3. package/gitvault-surface.json +2 -2
  4. package/lib/admin.mjs +5 -5
  5. package/lib/ai.mjs +5 -5
  6. package/lib/apps.mjs +9 -9
  7. package/lib/archives.mjs +371 -8
  8. package/lib/argparse.mjs +5 -5
  9. package/lib/assets.mjs +12 -12
  10. package/lib/auth.mjs +34 -34
  11. package/lib/branches.mjs +7 -7
  12. package/lib/buzz-doctor.mjs +1 -1
  13. package/lib/buzz-notifications.mjs +4 -4
  14. package/lib/buzz.mjs +2 -2
  15. package/lib/cache.mjs +1 -1
  16. package/lib/cdn.mjs +2 -2
  17. package/lib/ci.mjs +8 -8
  18. package/lib/claims.mjs +2 -2
  19. package/lib/command-manifest.mjs +35 -36
  20. package/lib/config.mjs +4 -4
  21. package/lib/contacts.mjs +3 -3
  22. package/lib/contracts.mjs +11 -11
  23. package/lib/credentials.mjs +31 -31
  24. package/lib/delegates.mjs +13 -13
  25. package/lib/deliveries.mjs +2 -2
  26. package/lib/deploy-v2.mjs +9 -9
  27. package/lib/dev.mjs +3 -3
  28. package/lib/doctor.mjs +6 -6
  29. package/lib/domains.mjs +27 -27
  30. package/lib/email.mjs +38 -38
  31. package/lib/errors.mjs +40 -24
  32. package/lib/escalations.mjs +4 -4
  33. package/lib/events.mjs +32 -25
  34. package/lib/feedback.mjs +2 -2
  35. package/lib/functions.mjs +34 -34
  36. package/lib/gitvault-scaffold.mjs +2 -2
  37. package/lib/gitvault.mjs +2 -2
  38. package/lib/grants.mjs +7 -7
  39. package/lib/image.mjs +7 -7
  40. package/lib/init.mjs +3 -3
  41. package/lib/jobs.mjs +15 -15
  42. package/lib/live.mjs +1 -1
  43. package/lib/logs.mjs +8 -8
  44. package/lib/messages.mjs +5 -5
  45. package/lib/next-actions.mjs +1 -1
  46. package/lib/org-context.mjs +13 -13
  47. package/lib/{org.mjs → orgs.mjs} +124 -163
  48. package/lib/projects.mjs +45 -56
  49. package/lib/repos.mjs +23 -23
  50. package/lib/rooms.mjs +2 -2
  51. package/lib/sdk-errors.mjs +1 -1
  52. package/lib/secrets.mjs +13 -13
  53. package/lib/sites.mjs +14 -14
  54. package/lib/snapshots.mjs +10 -10
  55. package/lib/subdomains.mjs +12 -12
  56. package/lib/subscriptions.mjs +2 -2
  57. package/lib/transfer.mjs +1 -1
  58. package/lib/up.mjs +7 -7
  59. package/lib/wallet-context.mjs +1 -1
  60. package/lib/wallets.mjs +1 -1
  61. package/lib/webhooks.mjs +8 -8
  62. package/lib/whoami.mjs +32 -5
  63. package/package.json +1 -1
  64. package/sdk/dist/actions.d.ts +3 -3
  65. package/sdk/dist/actions.d.ts.map +1 -1
  66. package/sdk/dist/app-up.d.ts +2 -2
  67. package/sdk/dist/app-up.d.ts.map +1 -1
  68. package/sdk/dist/config.d.ts +1 -2
  69. package/sdk/dist/config.d.ts.map +1 -1
  70. package/sdk/dist/config.js.map +1 -1
  71. package/sdk/dist/credentials.d.ts +1 -1
  72. package/sdk/dist/errors.d.ts +2 -2
  73. package/sdk/dist/errors.d.ts.map +1 -1
  74. package/sdk/dist/errors.js +1 -1
  75. package/sdk/dist/errors.js.map +1 -1
  76. package/sdk/dist/index.js +1 -1
  77. package/sdk/dist/index.js.map +1 -1
  78. package/sdk/dist/namespaces/admin.d.ts +1 -1
  79. package/sdk/dist/namespaces/admin.d.ts.map +1 -1
  80. package/sdk/dist/namespaces/ai.d.ts +1 -1
  81. package/sdk/dist/namespaces/ai.js +2 -2
  82. package/sdk/dist/namespaces/ai.js.map +1 -1
  83. package/sdk/dist/namespaces/assets.js +1 -1
  84. package/sdk/dist/namespaces/assets.js.map +1 -1
  85. package/sdk/dist/namespaces/ci.js +3 -3
  86. package/sdk/dist/namespaces/ci.js.map +1 -1
  87. package/sdk/dist/namespaces/deploy.d.ts.map +1 -1
  88. package/sdk/dist/namespaces/deploy.js +26 -27
  89. package/sdk/dist/namespaces/deploy.js.map +1 -1
  90. package/sdk/dist/namespaces/deploy.types.d.ts +2 -2
  91. package/sdk/dist/namespaces/deploy.types.d.ts.map +1 -1
  92. package/sdk/dist/namespaces/domains.d.ts +10 -2
  93. package/sdk/dist/namespaces/domains.d.ts.map +1 -1
  94. package/sdk/dist/namespaces/domains.js +10 -5
  95. package/sdk/dist/namespaces/domains.js.map +1 -1
  96. package/sdk/dist/namespaces/errors.js +1 -1
  97. package/sdk/dist/namespaces/functions.js +1 -1
  98. package/sdk/dist/namespaces/functions.js.map +1 -1
  99. package/sdk/dist/namespaces/gitvault.d.ts +2 -2
  100. package/sdk/dist/namespaces/gitvault.js +4 -4
  101. package/sdk/dist/namespaces/gitvault.js.map +1 -1
  102. package/sdk/dist/namespaces/tier.d.ts +1 -1
  103. package/sdk/dist/namespaces/transfers.d.ts +46 -15
  104. package/sdk/dist/namespaces/transfers.d.ts.map +1 -1
  105. package/sdk/dist/namespaces/transfers.js.map +1 -1
  106. package/sdk/dist/node/actions-node.js +8 -8
  107. package/sdk/dist/node/actions-node.js.map +1 -1
  108. package/sdk/dist/node/assets-node.js +6 -6
  109. package/sdk/dist/node/assets-node.js.map +1 -1
  110. package/sdk/dist/node/ci.js +1 -1
  111. package/sdk/dist/node/ci.js.map +1 -1
  112. package/sdk/dist/node/credentials.d.ts +1 -1
  113. package/sdk/dist/node/credentials.js +1 -1
  114. package/sdk/dist/node/deploy-manifest.d.ts +3 -5
  115. package/sdk/dist/node/deploy-manifest.d.ts.map +1 -1
  116. package/sdk/dist/node/deploy-manifest.js +3 -12
  117. package/sdk/dist/node/deploy-manifest.js.map +1 -1
  118. package/sdk/dist/node/gitvault-apply.js +1 -1
  119. package/sdk/dist/node/gitvault-apply.js.map +1 -1
  120. package/sdk/dist/node/manifest-export.js +2 -2
  121. package/sdk/dist/node/manifest-export.js.map +1 -1
  122. package/sdk/dist/node/org-adopt.js +1 -1
  123. package/sdk/dist/node/org-adopt.js.map +1 -1
  124. package/sdk/dist/node/sites-node.js +1 -1
  125. package/sdk/dist/node/sites-node.js.map +1 -1
  126. package/sdk/dist/scoped.d.ts +8 -8
  127. package/sdk/dist/scoped.d.ts.map +1 -1
  128. package/sdk/dist/scoped.js +3 -3
  129. package/sdk/dist/scoped.js.map +1 -1
  130. package/lib/cloud.mjs +0 -256
  131. package/lib/core.mjs +0 -123
package/lib/archives.mjs CHANGED
@@ -1,18 +1,106 @@
1
- import { inspectArchive, verifyArchive } from "#sdk/node";
2
- import { reportSdkError, fail } from "./sdk-errors.mjs";
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
- const HELP = `run402 archives — Inspect and verify portable Run402 project archives
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 inspect <archive-path> [--json]
9
- run402 archives verify <archive-path> [--json]
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 <archive-path> [--json]" });
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 <archive-path> [--json]" });
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 <id>` flag and the legacy leading
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 <id>` (canonical)
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 <branch-project-id>`).
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 <id>.",
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. "cloud archives", "email webhooks").
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 <id>] [--key <dest>] [--content-type <mime>] [--private] [--immutable] [--concurrency N] [--no-resume]
6
- * run402 assets get <key> --output <file> [--project <id>]
7
- * run402 assets ls [--project <id>] [--prefix <p>] [--limit <n>]
8
- * run402 assets rm <key> [--project <id>]
9
- * run402 assets sign <key> [--project <id>] [--ttl <seconds>]
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 <id> Project ID (defaults to active project from 'run402 projects use')
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 <id> Project ID (defaults to active project from 'run402 projects use')
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 <id> Project ID (defaults to active 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 <id> Project ID (defaults to active 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 <id> Project ID (defaults to active 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 <id> Project ID (defaults to active 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 <id> Project ID (defaults to active 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).