run402 3.8.0 → 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/lib/deploy-v2.mjs +11 -0
- package/lib/doctor.mjs +18 -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/dist/index.d.ts +9 -0
- package/sdk/dist/index.d.ts.map +1 -1
- package/sdk/dist/index.js +1 -0
- package/sdk/dist/index.js.map +1 -1
- package/sdk/dist/kernel.d.ts +7 -0
- package/sdk/dist/kernel.d.ts.map +1 -1
- package/sdk/dist/kernel.js +35 -0
- package/sdk/dist/kernel.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/index.d.ts +7 -0
- package/sdk/dist/node/index.d.ts.map +1 -1
- package/sdk/dist/node/index.js +24 -0
- package/sdk/dist/node/index.js.map +1 -1
package/README.md
CHANGED
|
@@ -7,13 +7,13 @@ For the full CLI reference (every flag, every subcommand) see **<https://docs.ru
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npm install -g run402
|
|
10
|
+
npm install -g run402@latest
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
Or run without installing:
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
npx run402 <command>
|
|
16
|
+
npx -y run402@latest <command>
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
## 30-second start
|
|
@@ -29,6 +29,8 @@ That's a real Postgres database + a deployed static site, paid for autonomously
|
|
|
29
29
|
|
|
30
30
|
Every command prints **JSON to stdout**, **JSON errors to stderr**, and exits **0 on success / 1 on failure**. Designed for shells, scripts, and agent loops — pipe everything to `jq`.
|
|
31
31
|
|
|
32
|
+
Stale CLI notices are advisory and never pollute success stdout. When a cached npm check says a newer `run402` exists, normal commands may emit a structured `cli.update_available` JSON object on stderr, or an NDJSON event in `--json-stream`. Run `run402 doctor --refresh` for the explicit live check; it reports whether this invocation is local, global, npx/npm exec, pnpm/yarn/bun, or custom, and includes an `upgrade_client` action with both `command` and `argv`.
|
|
33
|
+
|
|
32
34
|
## Common commands
|
|
33
35
|
|
|
34
36
|
### One-command app deploy
|
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/up.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { stdin as input, stderr as output } from "node:process";
|
|
|
3
3
|
import { getSdk } from "./sdk.mjs";
|
|
4
4
|
import { reportSdkError, fail } from "./sdk-errors.mjs";
|
|
5
5
|
import { assertKnownFlags, flagValue, normalizeArgv, positionalArgs } from "./argparse.mjs";
|
|
6
|
+
import { createUpdateCheckScheduler, emitUpdateNotice } from "./update-check.mjs";
|
|
6
7
|
|
|
7
8
|
const HELP = `run402 up — Provision/link/deploy the current app
|
|
8
9
|
|
|
@@ -44,6 +45,11 @@ Options:
|
|
|
44
45
|
--json-stream Emit NDJSON progress events on stdout and a final result event.
|
|
45
46
|
--quiet Suppress action progress events on stderr.
|
|
46
47
|
|
|
48
|
+
Update notices:
|
|
49
|
+
Stale CLI notices are advisory and never change the result payload or exit
|
|
50
|
+
code. Non-streaming notices are JSON on stderr; --json-stream emits
|
|
51
|
+
cli.update_available as an NDJSON event.
|
|
52
|
+
|
|
47
53
|
Project resolution:
|
|
48
54
|
explicit --project > .run402/project.json > manifest project_id > approved
|
|
49
55
|
project creation from --name > approved active-project fallback.
|
|
@@ -184,6 +190,10 @@ export async function run(args = []) {
|
|
|
184
190
|
});
|
|
185
191
|
}
|
|
186
192
|
const allowWarningCodes = collectRepeatedValues(parsed, "--allow-warning");
|
|
193
|
+
const updateScheduler = createUpdateCheckScheduler({
|
|
194
|
+
command: ["run402", "up", ...parsed],
|
|
195
|
+
});
|
|
196
|
+
emitUpdateNotice(updateScheduler.cachedNotice, { jsonStream, quiet });
|
|
187
197
|
|
|
188
198
|
try {
|
|
189
199
|
const sdk = getSdk();
|
|
@@ -224,11 +234,20 @@ export async function run(args = []) {
|
|
|
224
234
|
} else {
|
|
225
235
|
console.log(JSON.stringify(result, null, 2));
|
|
226
236
|
}
|
|
237
|
+
if (shouldExitNonZeroForUpResult(result)) {
|
|
238
|
+
process.exitCode = 1;
|
|
239
|
+
}
|
|
227
240
|
} catch (err) {
|
|
228
241
|
reportSdkError(err);
|
|
229
242
|
}
|
|
230
243
|
}
|
|
231
244
|
|
|
245
|
+
export function shouldExitNonZeroForUpResult(result) {
|
|
246
|
+
return result?.action === "up" &&
|
|
247
|
+
result?.mode === "apply" &&
|
|
248
|
+
result?.result?.app_result?.status === "deployed_unverified";
|
|
249
|
+
}
|
|
250
|
+
|
|
232
251
|
function parseExecutionMode(args) {
|
|
233
252
|
const modes = [];
|
|
234
253
|
if (args.includes("--check")) modes.push("--check");
|
package/lib/up.test.mjs
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { shouldExitNonZeroForUpResult } from "./up.mjs";
|
|
4
|
+
|
|
5
|
+
test("run402 up exits nonzero for deployed-but-unverified app results", () => {
|
|
6
|
+
assert.equal(shouldExitNonZeroForUpResult({
|
|
7
|
+
action: "up",
|
|
8
|
+
mode: "apply",
|
|
9
|
+
result: {
|
|
10
|
+
app_result: { status: "deployed_unverified" },
|
|
11
|
+
},
|
|
12
|
+
}), true);
|
|
13
|
+
|
|
14
|
+
assert.equal(shouldExitNonZeroForUpResult({
|
|
15
|
+
action: "up",
|
|
16
|
+
mode: "apply",
|
|
17
|
+
result: {
|
|
18
|
+
app_result: { status: "succeeded" },
|
|
19
|
+
},
|
|
20
|
+
}), false);
|
|
21
|
+
|
|
22
|
+
assert.equal(shouldExitNonZeroForUpResult({
|
|
23
|
+
action: "up",
|
|
24
|
+
mode: "check",
|
|
25
|
+
result: {
|
|
26
|
+
app_result: { status: "deployed_unverified" },
|
|
27
|
+
},
|
|
28
|
+
}), false);
|
|
29
|
+
});
|