@chllming/wave-orchestration 0.6.2 → 0.6.3
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/CHANGELOG.md +7 -0
- package/README.md +11 -7
- package/docs/plans/current-state.md +2 -1
- package/docs/plans/wave-orchestrator.md +11 -0
- package/package.json +1 -1
- package/releases/manifest.json +15 -0
- package/scripts/wave-autonomous.mjs +2 -4
- package/scripts/wave-orchestrator/adhoc.mjs +32 -11
- package/scripts/wave-orchestrator/autonomous.mjs +20 -6
- package/scripts/wave-orchestrator/install.mjs +198 -25
- package/scripts/wave-orchestrator/launcher.mjs +4 -0
- package/scripts/wave-orchestrator/package-update-notice.mjs +230 -0
- package/scripts/wave-orchestrator/package-version.mjs +32 -0
- package/scripts/wave.mjs +12 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.6.3 - 2026-03-22
|
|
6
|
+
|
|
7
|
+
- Added a best-effort npmjs update notice on `wave launch`, `wave autonomous`, and `wave adhoc run`, with cached lookup state under `.wave/package-update-check.json` and opt-out via `WAVE_SKIP_UPDATE_CHECK=1`.
|
|
8
|
+
- Added `wave self-update`, which detects the workspace package manager, updates `@chllming/wave-orchestration`, prints the changelog delta since the recorded install, and then runs `wave upgrade`.
|
|
9
|
+
- Suppressed duplicate notices for nested launcher calls so autonomous and ad-hoc runs announce at most once, while keeping JSON-oriented stdout surfaces clean by emitting notices on stderr.
|
|
10
|
+
- Documented the new update flow and added regression coverage for notice caching, package-manager-aware self-update, and nested-launch suppression.
|
|
11
|
+
|
|
5
12
|
## 0.6.2 - 2026-03-22
|
|
6
13
|
|
|
7
14
|
- Added first-class `claude.effort` support across config profiles, lane overrides, and per-agent `### Executor` blocks, and now emit `--effort` in Claude launch previews and live runs.
|
package/README.md
CHANGED
|
@@ -75,17 +75,16 @@ Wave is built to mitigate those failures with canonical shared state, generated
|
|
|
75
75
|
|
|
76
76
|
Current release:
|
|
77
77
|
|
|
78
|
-
- `@chllming/wave-orchestration@0.6.
|
|
79
|
-
- Release tag: [`v0.6.
|
|
78
|
+
- `@chllming/wave-orchestration@0.6.3`
|
|
79
|
+
- Release tag: [`v0.6.3`](https://github.com/chllming/wave-orchestration/releases/tag/v0.6.3)
|
|
80
80
|
- Public install path: npmjs
|
|
81
81
|
- Authenticated fallback: GitHub Packages
|
|
82
82
|
|
|
83
|
-
Highlights in `0.6.
|
|
83
|
+
Highlights in `0.6.3`:
|
|
84
84
|
|
|
85
|
-
- Runtime
|
|
86
|
-
-
|
|
87
|
-
-
|
|
88
|
-
- Shared promoted-component retries now preserve already-landed owner slices and relaunch only the sibling owners still needed for closure.
|
|
85
|
+
- Runtime launch entrypoints now check npmjs for a newer published package in the background, cache the result under `.wave/package-update-check.json`, and warn on stderr when the workspace is behind.
|
|
86
|
+
- `wave self-update` now gives downstream repos a one-command update path that detects the workspace package manager, updates the dependency, shows the changelog delta, and records the workspace upgrade report.
|
|
87
|
+
- Autonomous and ad-hoc flows suppress nested notices so operators see at most one update banner per top-level run, and structured stdout remains clean for JSON consumers.
|
|
89
88
|
|
|
90
89
|
Requirements:
|
|
91
90
|
|
|
@@ -113,6 +112,8 @@ pnpm exec wave init --adopt-existing
|
|
|
113
112
|
|
|
114
113
|
Fresh init also seeds a starter `skills/` library plus `docs/evals/benchmark-catalog.json`. The launcher projects those skill bundles into Codex, Claude, OpenCode, and local executor overlays after the final runtime for each agent is resolved, and waves that include `cont-EVAL` can declare `## Eval targets` against that catalog.
|
|
115
114
|
|
|
115
|
+
When runtime launch commands detect a newer npmjs release, Wave prints a non-blocking update notice on stderr. The fast path is `pnpm exec wave self-update`, which updates the dependency, prints the changelog delta, and then records the workspace upgrade report.
|
|
116
|
+
|
|
116
117
|
## Common Commands
|
|
117
118
|
|
|
118
119
|
```bash
|
|
@@ -129,6 +130,9 @@ pnpm exec wave dep show --lane main --wave 0 --json
|
|
|
129
130
|
|
|
130
131
|
# Run autonomous mode after the wave set is stable
|
|
131
132
|
pnpm exec wave autonomous --lane main --executor codex --codex-sandbox danger-full-access
|
|
133
|
+
|
|
134
|
+
# Pull the latest published package and record the workspace upgrade
|
|
135
|
+
pnpm exec wave self-update
|
|
132
136
|
```
|
|
133
137
|
|
|
134
138
|
## Develop This Package
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# Current State
|
|
2
2
|
|
|
3
|
-
- The starter workspace in this source repo reflects the `0.6.
|
|
3
|
+
- The starter workspace in this source repo reflects the `0.6.3` package release surface.
|
|
4
4
|
- The repository contains the published `@chllming/wave-orchestration` package plus the starter scaffold used by `wave init`.
|
|
5
5
|
- The runtime is package-first and non-destructive for adopting repos: `wave init --adopt-existing` records existing repo-owned plans, waves, prompts, and config without overwriting them, and `wave upgrade` writes only `.wave/install-state.json` plus `.wave/upgrade-history/`.
|
|
6
|
+
- Runtime launch entrypoints now perform a best-effort npmjs version check, cache the result under `.wave/package-update-check.json`, and point operators at `pnpm exec wave self-update` when a newer published package exists.
|
|
6
7
|
- This source repo is itself kept as an adopted Wave workspace, so `node scripts/wave.mjs doctor --json` should pass from the repo root.
|
|
7
8
|
- The default lane is `main`.
|
|
8
9
|
- Planner foundation is now shipped:
|
|
@@ -54,6 +54,7 @@ This runbook is the operational view of the architecture:
|
|
|
54
54
|
- `pnpm exec wave dep show --lane main --wave 0 --json`
|
|
55
55
|
- `pnpm exec wave dep post --owner-lane main --requester-lane release --owner-wave 0 --requester-wave 2 --agent launcher --summary "Need shared-plan reconciliation" --target capability:docs-shared-plan --required`
|
|
56
56
|
- `pnpm exec wave upgrade`
|
|
57
|
+
- `pnpm exec wave self-update`
|
|
57
58
|
|
|
58
59
|
## Configuration
|
|
59
60
|
|
|
@@ -151,6 +152,16 @@ Required inbound dependencies block autonomous next-wave start and lane finaliza
|
|
|
151
152
|
|
|
152
153
|
## Upgrade Flow
|
|
153
154
|
|
|
155
|
+
Fast path:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
pnpm exec wave self-update
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
That command updates the dependency through the workspace package manager, prints the changelog delta since the recorded install, and then runs `wave upgrade` to record the new install-state and upgrade report.
|
|
162
|
+
|
|
163
|
+
Manual path:
|
|
164
|
+
|
|
154
165
|
1. Upgrade the package version:
|
|
155
166
|
|
|
156
167
|
```bash
|
package/package.json
CHANGED
package/releases/manifest.json
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"packageName": "@chllming/wave-orchestration",
|
|
4
4
|
"releases": [
|
|
5
|
+
{
|
|
6
|
+
"version": "0.6.3",
|
|
7
|
+
"date": "2026-03-22",
|
|
8
|
+
"summary": "Runtime npmjs update notices plus a one-command self-update flow for downstream repos.",
|
|
9
|
+
"features": [
|
|
10
|
+
"Top-level runtime entrypoints now perform a best-effort npmjs version check, cache the result under `.wave/package-update-check.json`, and emit a non-blocking stderr notice when a newer `@chllming/wave-orchestration` release is available.",
|
|
11
|
+
"New `wave self-update` detects the workspace package manager, updates the package dependency to the latest published release, prints the changelog delta since the recorded install, and then runs `wave upgrade`.",
|
|
12
|
+
"Autonomous and ad-hoc flows now suppress nested update notices so operators see at most one banner per top-level run, while structured stdout such as `wave adhoc run --json` remains parseable."
|
|
13
|
+
],
|
|
14
|
+
"manualSteps": [
|
|
15
|
+
"No migration is required. If you prefer not to check npmjs at runtime on a workstation, set `WAVE_SKIP_UPDATE_CHECK=1` in that shell environment.",
|
|
16
|
+
"After upgrading, try `pnpm exec wave self-update` once in an adopted repo to confirm the workspace package manager and install-state workflow behave the way you expect."
|
|
17
|
+
],
|
|
18
|
+
"breaking": false
|
|
19
|
+
},
|
|
5
20
|
{
|
|
6
21
|
"version": "0.6.2",
|
|
7
22
|
"date": "2026-03-22",
|
|
@@ -5,9 +5,7 @@ import { bootstrapWaveArgs } from "./wave-cli-bootstrap.mjs";
|
|
|
5
5
|
const argv = bootstrapWaveArgs(process.argv.slice(2));
|
|
6
6
|
const { runAutonomousCli } = await import("./wave-orchestrator/autonomous.mjs");
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
runAutonomousCli(argv);
|
|
10
|
-
} catch (error) {
|
|
8
|
+
runAutonomousCli(argv).catch((error) => {
|
|
11
9
|
console.error(`[wave-autonomous] ${error instanceof Error ? error.message : String(error)}`);
|
|
12
10
|
process.exit(1);
|
|
13
|
-
}
|
|
11
|
+
});
|
|
@@ -8,6 +8,10 @@ import {
|
|
|
8
8
|
buildDefaultProjectProfile,
|
|
9
9
|
readProjectProfile,
|
|
10
10
|
} from "./project-profile.mjs";
|
|
11
|
+
import {
|
|
12
|
+
maybeAnnouncePackageUpdate,
|
|
13
|
+
WAVE_SUPPRESS_UPDATE_NOTICE_ENV,
|
|
14
|
+
} from "./package-update-notice.mjs";
|
|
11
15
|
import { runLauncherCli } from "./launcher.mjs";
|
|
12
16
|
import { renderWaveMarkdown } from "./planner.mjs";
|
|
13
17
|
import {
|
|
@@ -187,6 +191,20 @@ function buildAdhocRunId() {
|
|
|
187
191
|
return sanitizeAdhocRunId(`adhoc-${stamp}-${random}`);
|
|
188
192
|
}
|
|
189
193
|
|
|
194
|
+
async function withSuppressedNestedUpdateNotice(fn) {
|
|
195
|
+
const previousValue = process.env[WAVE_SUPPRESS_UPDATE_NOTICE_ENV];
|
|
196
|
+
process.env[WAVE_SUPPRESS_UPDATE_NOTICE_ENV] = "1";
|
|
197
|
+
try {
|
|
198
|
+
return await fn();
|
|
199
|
+
} finally {
|
|
200
|
+
if (previousValue === undefined) {
|
|
201
|
+
delete process.env[WAVE_SUPPRESS_UPDATE_NOTICE_ENV];
|
|
202
|
+
} else {
|
|
203
|
+
process.env[WAVE_SUPPRESS_UPDATE_NOTICE_ENV] = previousValue;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
190
208
|
function readEffectiveProjectProfile(config) {
|
|
191
209
|
return readProjectProfile({ config }) || buildDefaultProjectProfile(config);
|
|
192
210
|
}
|
|
@@ -1135,6 +1153,7 @@ export async function runAdhocCli(argv) {
|
|
|
1135
1153
|
if (options.tasks.length === 0) {
|
|
1136
1154
|
throw new Error("At least one --task is required for `wave adhoc run`.");
|
|
1137
1155
|
}
|
|
1156
|
+
await maybeAnnouncePackageUpdate();
|
|
1138
1157
|
const stored = createStoredRun({ config, options });
|
|
1139
1158
|
const summary = summarizePlan(stored.spec, stored.lanePaths);
|
|
1140
1159
|
if (options.json) {
|
|
@@ -1157,17 +1176,19 @@ export async function runAdhocCli(argv) {
|
|
|
1157
1176
|
writeJsonAtomic(stored.lanePaths.adhocResultPath, runningResult);
|
|
1158
1177
|
upsertAdhocIndexEntry(stored.lanePaths.adhocIndexPath, runningResult);
|
|
1159
1178
|
try {
|
|
1160
|
-
await
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1179
|
+
await withSuppressedNestedUpdateNotice(() =>
|
|
1180
|
+
runLauncherCli([
|
|
1181
|
+
"--lane",
|
|
1182
|
+
stored.lanePaths.lane,
|
|
1183
|
+
"--adhoc-run",
|
|
1184
|
+
stored.lanePaths.runId,
|
|
1185
|
+
"--start-wave",
|
|
1186
|
+
String(ADHOC_WAVE_NUMBER),
|
|
1187
|
+
"--end-wave",
|
|
1188
|
+
String(ADHOC_WAVE_NUMBER),
|
|
1189
|
+
...options.launcherArgs,
|
|
1190
|
+
]),
|
|
1191
|
+
);
|
|
1171
1192
|
const completedResult = buildResultRecord(
|
|
1172
1193
|
stored.lanePaths,
|
|
1173
1194
|
stored.request,
|
|
@@ -21,6 +21,10 @@ import {
|
|
|
21
21
|
DEFAULT_CODEX_SANDBOX_MODE,
|
|
22
22
|
normalizeCodexSandboxMode,
|
|
23
23
|
} from "./launcher.mjs";
|
|
24
|
+
import {
|
|
25
|
+
maybeAnnouncePackageUpdate,
|
|
26
|
+
WAVE_SUPPRESS_UPDATE_NOTICE_ENV,
|
|
27
|
+
} from "./package-update-notice.mjs";
|
|
24
28
|
import { readRunState } from "./wave-files.mjs";
|
|
25
29
|
import { readDependencyTickets } from "./coordination-store.mjs";
|
|
26
30
|
import { readWaveLedger } from "./ledger.mjs";
|
|
@@ -155,21 +159,30 @@ export function nextIncompleteWave(allWaves, completed) {
|
|
|
155
159
|
return null;
|
|
156
160
|
}
|
|
157
161
|
|
|
158
|
-
function runCommand(args) {
|
|
162
|
+
function runCommand(args, envOverrides = {}) {
|
|
159
163
|
const result = spawnSync("node", args, {
|
|
160
164
|
cwd: REPO_ROOT,
|
|
161
165
|
stdio: "inherit",
|
|
162
|
-
env:
|
|
166
|
+
env: {
|
|
167
|
+
...process.env,
|
|
168
|
+
...envOverrides,
|
|
169
|
+
},
|
|
163
170
|
});
|
|
164
171
|
return Number.isInteger(result.status) ? result.status : 1;
|
|
165
172
|
}
|
|
166
173
|
|
|
167
174
|
function reconcile(lane) {
|
|
168
|
-
return runCommand(
|
|
175
|
+
return runCommand(
|
|
176
|
+
[path.join(PACKAGE_ROOT, "scripts", "wave-launcher.mjs"), "--lane", lane, "--reconcile-status"],
|
|
177
|
+
{ [WAVE_SUPPRESS_UPDATE_NOTICE_ENV]: "1" },
|
|
178
|
+
);
|
|
169
179
|
}
|
|
170
180
|
|
|
171
181
|
function dryRun(lane) {
|
|
172
|
-
return runCommand(
|
|
182
|
+
return runCommand(
|
|
183
|
+
[path.join(PACKAGE_ROOT, "scripts", "wave-launcher.mjs"), "--lane", lane, "--dry-run", "--no-dashboard"],
|
|
184
|
+
{ [WAVE_SUPPRESS_UPDATE_NOTICE_ENV]: "1" },
|
|
185
|
+
);
|
|
173
186
|
}
|
|
174
187
|
|
|
175
188
|
function listPendingFeedback(lane) {
|
|
@@ -215,7 +228,7 @@ function launchSingleWave(params) {
|
|
|
215
228
|
if (params.keepTerminals) {
|
|
216
229
|
args.push("--keep-terminals");
|
|
217
230
|
}
|
|
218
|
-
return runCommand(args);
|
|
231
|
+
return runCommand(args, { [WAVE_SUPPRESS_UPDATE_NOTICE_ENV]: "1" });
|
|
219
232
|
}
|
|
220
233
|
|
|
221
234
|
function requiredInboundDependenciesOpen(lanePaths, lane) {
|
|
@@ -291,12 +304,13 @@ export function readAutonomousBarrier(lanePaths, lane, wave = null) {
|
|
|
291
304
|
return null;
|
|
292
305
|
}
|
|
293
306
|
|
|
294
|
-
export function runAutonomousCli(argv) {
|
|
307
|
+
export async function runAutonomousCli(argv) {
|
|
295
308
|
const parsed = parseArgs(argv);
|
|
296
309
|
if (parsed.help) {
|
|
297
310
|
printUsage();
|
|
298
311
|
return;
|
|
299
312
|
}
|
|
313
|
+
await maybeAnnouncePackageUpdate();
|
|
300
314
|
const options = parsed.options;
|
|
301
315
|
const allWaves = getWaveNumbers(options.lane);
|
|
302
316
|
console.log(`[autonomous] lane=${options.lane} orchestrator=${options.orchestratorId}`);
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
3
4
|
import {
|
|
4
5
|
applyContext7SelectionsToWave,
|
|
5
6
|
loadContext7BundleIndex,
|
|
6
7
|
} from "./context7.mjs";
|
|
7
8
|
import { buildLanePaths, ensureDirectory, PACKAGE_ROOT, readJsonOrNull, REPO_ROOT, writeJsonAtomic } from "./shared.mjs";
|
|
9
|
+
import { fetchLatestPackageVersion } from "./package-update-notice.mjs";
|
|
10
|
+
import {
|
|
11
|
+
compareVersions,
|
|
12
|
+
readInstalledPackageMetadata,
|
|
13
|
+
WAVE_PACKAGE_NAME,
|
|
14
|
+
} from "./package-version.mjs";
|
|
8
15
|
import { loadWaveConfig } from "./config.mjs";
|
|
9
16
|
import { applyExecutorSelectionsToWave, parseWaveFiles, validateWaveDefinition } from "./wave-files.mjs";
|
|
10
17
|
import { validateLaneSkillConfiguration } from "./skills.mjs";
|
|
@@ -14,7 +21,7 @@ export const INSTALL_STATE_DIR = ".wave";
|
|
|
14
21
|
export const INSTALL_STATE_PATH = path.join(REPO_ROOT, INSTALL_STATE_DIR, "install-state.json");
|
|
15
22
|
export const UPGRADE_HISTORY_DIR = path.join(REPO_ROOT, INSTALL_STATE_DIR, "upgrade-history");
|
|
16
23
|
export const CHANGELOG_MANIFEST_PATH = path.join(PACKAGE_ROOT, "releases", "manifest.json");
|
|
17
|
-
export const
|
|
24
|
+
export const WORKSPACE_PACKAGE_JSON_PATH = path.join(REPO_ROOT, "package.json");
|
|
18
25
|
export const STARTER_TEMPLATE_PATHS = [
|
|
19
26
|
"wave.config.json",
|
|
20
27
|
"docs/README.md",
|
|
@@ -69,11 +76,7 @@ function collectDeclaredDeployKinds(waves = []) {
|
|
|
69
76
|
}
|
|
70
77
|
|
|
71
78
|
function packageMetadata() {
|
|
72
|
-
|
|
73
|
-
if (!payload?.name || !payload?.version) {
|
|
74
|
-
throw new Error(`Invalid package metadata: ${PACKAGE_METADATA_PATH}`);
|
|
75
|
-
}
|
|
76
|
-
return payload;
|
|
79
|
+
return readInstalledPackageMetadata();
|
|
77
80
|
}
|
|
78
81
|
|
|
79
82
|
function readInstallState() {
|
|
@@ -149,25 +152,6 @@ function nextHistoryRecord(existingState, entry) {
|
|
|
149
152
|
return history;
|
|
150
153
|
}
|
|
151
154
|
|
|
152
|
-
function normalizeVersionParts(version) {
|
|
153
|
-
return String(version || "")
|
|
154
|
-
.split(".")
|
|
155
|
-
.map((part) => Number.parseInt(part.replace(/[^0-9].*$/, ""), 10) || 0);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function compareVersions(a, b) {
|
|
159
|
-
const left = normalizeVersionParts(a);
|
|
160
|
-
const right = normalizeVersionParts(b);
|
|
161
|
-
const length = Math.max(left.length, right.length);
|
|
162
|
-
for (let index = 0; index < length; index += 1) {
|
|
163
|
-
const diff = (left[index] || 0) - (right[index] || 0);
|
|
164
|
-
if (diff !== 0) {
|
|
165
|
-
return diff;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
return 0;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
155
|
function readChangelogManifest() {
|
|
172
156
|
const payload = readJsonOrNull(CHANGELOG_MANIFEST_PATH);
|
|
173
157
|
if (!payload?.releases || !Array.isArray(payload.releases)) {
|
|
@@ -478,6 +462,186 @@ export function upgradeWorkspace() {
|
|
|
478
462
|
};
|
|
479
463
|
}
|
|
480
464
|
|
|
465
|
+
function readWorkspacePackageManifest(workspaceRoot = REPO_ROOT) {
|
|
466
|
+
const payload = readJsonOrNull(path.join(workspaceRoot, "package.json"));
|
|
467
|
+
if (!payload || typeof payload !== "object") {
|
|
468
|
+
throw new Error(`Missing package.json at ${path.join(workspaceRoot, "package.json")}`);
|
|
469
|
+
}
|
|
470
|
+
return payload;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function readInstallStateForWorkspace(workspaceRoot = REPO_ROOT) {
|
|
474
|
+
const payload = readJsonOrNull(path.join(workspaceRoot, INSTALL_STATE_DIR, "install-state.json"));
|
|
475
|
+
return payload && typeof payload === "object" ? payload : null;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function parsePackageManagerId(value) {
|
|
479
|
+
const normalized = String(value || "")
|
|
480
|
+
.trim()
|
|
481
|
+
.toLowerCase();
|
|
482
|
+
if (!normalized) {
|
|
483
|
+
return null;
|
|
484
|
+
}
|
|
485
|
+
if (normalized.startsWith("pnpm@")) {
|
|
486
|
+
return "pnpm";
|
|
487
|
+
}
|
|
488
|
+
if (normalized.startsWith("npm@")) {
|
|
489
|
+
return "npm";
|
|
490
|
+
}
|
|
491
|
+
if (normalized.startsWith("yarn@")) {
|
|
492
|
+
return "yarn";
|
|
493
|
+
}
|
|
494
|
+
if (normalized.startsWith("bun@")) {
|
|
495
|
+
return "bun";
|
|
496
|
+
}
|
|
497
|
+
return null;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
export function detectWorkspacePackageManager(workspaceRoot = REPO_ROOT) {
|
|
501
|
+
const manifest = readWorkspacePackageManifest(workspaceRoot);
|
|
502
|
+
const packageManagerFromManifest = parsePackageManagerId(manifest.packageManager);
|
|
503
|
+
if (packageManagerFromManifest) {
|
|
504
|
+
return {
|
|
505
|
+
id: packageManagerFromManifest,
|
|
506
|
+
source: "packageManager",
|
|
507
|
+
raw: manifest.packageManager,
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
for (const [fileName, id] of [
|
|
511
|
+
["pnpm-lock.yaml", "pnpm"],
|
|
512
|
+
["package-lock.json", "npm"],
|
|
513
|
+
["npm-shrinkwrap.json", "npm"],
|
|
514
|
+
["yarn.lock", "yarn"],
|
|
515
|
+
["bun.lockb", "bun"],
|
|
516
|
+
["bun.lock", "bun"],
|
|
517
|
+
]) {
|
|
518
|
+
if (fs.existsSync(path.join(workspaceRoot, fileName))) {
|
|
519
|
+
return {
|
|
520
|
+
id,
|
|
521
|
+
source: "lockfile",
|
|
522
|
+
raw: fileName,
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
return {
|
|
527
|
+
id: "npm",
|
|
528
|
+
source: "default",
|
|
529
|
+
raw: null,
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
function packageManagerCommands(managerId, packageName = WAVE_PACKAGE_NAME) {
|
|
534
|
+
if (managerId === "pnpm") {
|
|
535
|
+
return {
|
|
536
|
+
install: ["pnpm", ["add", "-D", `${packageName}@latest`]],
|
|
537
|
+
execWave: (args) => ["pnpm", ["exec", "wave", ...args]],
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
if (managerId === "npm") {
|
|
541
|
+
return {
|
|
542
|
+
install: ["npm", ["install", "--save-dev", `${packageName}@latest`]],
|
|
543
|
+
execWave: (args) => ["npm", ["exec", "--", "wave", ...args]],
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
if (managerId === "yarn") {
|
|
547
|
+
return {
|
|
548
|
+
install: ["yarn", ["add", "-D", `${packageName}@latest`]],
|
|
549
|
+
execWave: (args) => ["yarn", ["exec", "wave", ...args]],
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
if (managerId === "bun") {
|
|
553
|
+
return {
|
|
554
|
+
install: ["bun", ["add", "-d", `${packageName}@latest`]],
|
|
555
|
+
execWave: (args) => ["bun", ["x", "wave", ...args]],
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
throw new Error(`Unsupported package manager: ${managerId}`);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
function runCommandOrThrow(command, args, options = {}) {
|
|
562
|
+
const spawnImpl = options.spawnImpl || spawnSync;
|
|
563
|
+
const result = spawnImpl(command, args, {
|
|
564
|
+
cwd: options.workspaceRoot || REPO_ROOT,
|
|
565
|
+
stdio: options.stdio || "inherit",
|
|
566
|
+
env: options.env || process.env,
|
|
567
|
+
encoding: "utf8",
|
|
568
|
+
});
|
|
569
|
+
const status = Number.isInteger(result?.status) ? result.status : 1;
|
|
570
|
+
if (status !== 0) {
|
|
571
|
+
throw new Error(`${command} ${args.join(" ")} failed with status ${status}`);
|
|
572
|
+
}
|
|
573
|
+
return result;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
export async function selfUpdateWorkspace(options = {}) {
|
|
577
|
+
const workspaceRoot = options.workspaceRoot || REPO_ROOT;
|
|
578
|
+
const metadata = options.packageMetadata || packageMetadata();
|
|
579
|
+
const installState = readInstallStateForWorkspace(workspaceRoot);
|
|
580
|
+
const packageManager = detectWorkspacePackageManager(workspaceRoot);
|
|
581
|
+
const commands = packageManagerCommands(packageManager.id, metadata.name || WAVE_PACKAGE_NAME);
|
|
582
|
+
const emit = options.emit || console.log;
|
|
583
|
+
let latestVersion = null;
|
|
584
|
+
|
|
585
|
+
try {
|
|
586
|
+
latestVersion = await fetchLatestPackageVersion(metadata.name || WAVE_PACKAGE_NAME, {
|
|
587
|
+
fetchImpl: options.fetchImpl,
|
|
588
|
+
timeoutMs: options.timeoutMs,
|
|
589
|
+
});
|
|
590
|
+
} catch {
|
|
591
|
+
latestVersion = null;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
const currentVersion = String(metadata.version || "").trim();
|
|
595
|
+
const recordedVersion = String(installState?.installedVersion || "").trim() || null;
|
|
596
|
+
const needsUpgradeOnly = recordedVersion && compareVersions(currentVersion, recordedVersion) !== 0;
|
|
597
|
+
|
|
598
|
+
emit(`[wave:self-update] package_manager=${packageManager.id}`);
|
|
599
|
+
|
|
600
|
+
if (latestVersion && compareVersions(latestVersion, currentVersion) <= 0) {
|
|
601
|
+
if (!needsUpgradeOnly) {
|
|
602
|
+
emit(`[wave:self-update] ${metadata.name} is already current at ${currentVersion}.`);
|
|
603
|
+
return {
|
|
604
|
+
mode: "already-current",
|
|
605
|
+
packageManager: packageManager.id,
|
|
606
|
+
currentVersion,
|
|
607
|
+
latestVersion,
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
emit(
|
|
611
|
+
`[wave:self-update] dependency is already at ${currentVersion}; recording workspace upgrade state.`,
|
|
612
|
+
);
|
|
613
|
+
const [upgradeCommand, upgradeArgs] = commands.execWave(["upgrade"]);
|
|
614
|
+
runCommandOrThrow(upgradeCommand, upgradeArgs, options);
|
|
615
|
+
return {
|
|
616
|
+
mode: "upgrade-only",
|
|
617
|
+
packageManager: packageManager.id,
|
|
618
|
+
currentVersion,
|
|
619
|
+
latestVersion,
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
emit(
|
|
624
|
+
`[wave:self-update] updating ${metadata.name} from ${currentVersion}${latestVersion ? ` to ${latestVersion}` : " to the latest published version"}.`,
|
|
625
|
+
);
|
|
626
|
+
const [installCommand, installArgs] = commands.install;
|
|
627
|
+
runCommandOrThrow(installCommand, installArgs, options);
|
|
628
|
+
|
|
629
|
+
emit("[wave:self-update] release notes since the recorded install:");
|
|
630
|
+
const [changelogCommand, changelogArgs] = commands.execWave(["changelog", "--since-installed"]);
|
|
631
|
+
runCommandOrThrow(changelogCommand, changelogArgs, options);
|
|
632
|
+
|
|
633
|
+
emit("[wave:self-update] recording install-state and upgrade report:");
|
|
634
|
+
const [upgradeCommand, upgradeArgs] = commands.execWave(["upgrade"]);
|
|
635
|
+
runCommandOrThrow(upgradeCommand, upgradeArgs, options);
|
|
636
|
+
|
|
637
|
+
return {
|
|
638
|
+
mode: "updated",
|
|
639
|
+
packageManager: packageManager.id,
|
|
640
|
+
currentVersion,
|
|
641
|
+
latestVersion,
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
|
|
481
645
|
function printJson(payload) {
|
|
482
646
|
console.log(JSON.stringify(payload, null, 2));
|
|
483
647
|
}
|
|
@@ -486,6 +650,7 @@ function printHelp() {
|
|
|
486
650
|
console.log(`Usage:
|
|
487
651
|
wave init [--adopt-existing] [--json]
|
|
488
652
|
wave upgrade [--json]
|
|
653
|
+
wave self-update
|
|
489
654
|
wave changelog [--since-installed] [--json]
|
|
490
655
|
wave doctor [--json]
|
|
491
656
|
`);
|
|
@@ -562,6 +727,14 @@ export async function runInstallCli(argv) {
|
|
|
562
727
|
return;
|
|
563
728
|
}
|
|
564
729
|
|
|
730
|
+
if (subcommand === "self-update") {
|
|
731
|
+
if (options.json) {
|
|
732
|
+
throw new Error("`wave self-update` does not support --json.");
|
|
733
|
+
}
|
|
734
|
+
await selfUpdateWorkspace();
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
|
|
565
738
|
if (subcommand === "changelog") {
|
|
566
739
|
const result = readChangelog({ sinceInstalled: options.sinceInstalled });
|
|
567
740
|
if (options.json) {
|
|
@@ -98,6 +98,7 @@ import {
|
|
|
98
98
|
commandForExecutor,
|
|
99
99
|
isExecutorCommandAvailable,
|
|
100
100
|
} from "./executors.mjs";
|
|
101
|
+
import { maybeAnnouncePackageUpdate } from "./package-update-notice.mjs";
|
|
101
102
|
import {
|
|
102
103
|
agentRequiresProofCentricValidation,
|
|
103
104
|
buildRunStateEvidence,
|
|
@@ -2971,6 +2972,9 @@ export async function runLauncherCli(argv) {
|
|
|
2971
2972
|
return;
|
|
2972
2973
|
}
|
|
2973
2974
|
const { lanePaths, options } = parsed;
|
|
2975
|
+
if (!options.reconcileStatus) {
|
|
2976
|
+
await maybeAnnouncePackageUpdate();
|
|
2977
|
+
}
|
|
2974
2978
|
let lockHeld = false;
|
|
2975
2979
|
let globalDashboard = null;
|
|
2976
2980
|
let globalDashboardTerminalEntry = null;
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { ensureDirectory, readJsonOrNull, REPO_ROOT, writeJsonAtomic } from "./shared.mjs";
|
|
4
|
+
import {
|
|
5
|
+
compareVersions,
|
|
6
|
+
readInstalledPackageMetadata,
|
|
7
|
+
WAVE_PACKAGE_NAME,
|
|
8
|
+
} from "./package-version.mjs";
|
|
9
|
+
|
|
10
|
+
export const PACKAGE_UPDATE_CHECK_SCHEMA_VERSION = 1;
|
|
11
|
+
export const PACKAGE_UPDATE_CHECK_PATH = path.join(REPO_ROOT, ".wave", "package-update-check.json");
|
|
12
|
+
export const PACKAGE_UPDATE_CHECK_TTL_MS = 6 * 60 * 60 * 1000;
|
|
13
|
+
export const PACKAGE_UPDATE_CHECK_TIMEOUT_MS = 2000;
|
|
14
|
+
export const WAVE_SKIP_UPDATE_CHECK_ENV = "WAVE_SKIP_UPDATE_CHECK";
|
|
15
|
+
export const WAVE_SUPPRESS_UPDATE_NOTICE_ENV = "WAVE_SUPPRESS_UPDATE_NOTICE";
|
|
16
|
+
export const NPM_REGISTRY_LATEST_URL = "https://registry.npmjs.org";
|
|
17
|
+
|
|
18
|
+
function isTruthyEnvValue(value) {
|
|
19
|
+
const normalized = String(value ?? "")
|
|
20
|
+
.trim()
|
|
21
|
+
.toLowerCase();
|
|
22
|
+
return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function parsePackageManagerId(value) {
|
|
26
|
+
const normalized = String(value || "")
|
|
27
|
+
.trim()
|
|
28
|
+
.toLowerCase();
|
|
29
|
+
if (normalized.startsWith("pnpm@")) {
|
|
30
|
+
return "pnpm";
|
|
31
|
+
}
|
|
32
|
+
if (normalized.startsWith("npm@")) {
|
|
33
|
+
return "npm";
|
|
34
|
+
}
|
|
35
|
+
if (normalized.startsWith("yarn@")) {
|
|
36
|
+
return "yarn";
|
|
37
|
+
}
|
|
38
|
+
if (normalized.startsWith("bun@")) {
|
|
39
|
+
return "bun";
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function runtimeSelfUpdateCommand(workspaceRoot = REPO_ROOT) {
|
|
45
|
+
const workspacePackage = readJsonOrNull(path.join(workspaceRoot, "package.json"));
|
|
46
|
+
const packageManagerId = parsePackageManagerId(workspacePackage?.packageManager);
|
|
47
|
+
if (packageManagerId === "pnpm") {
|
|
48
|
+
return "pnpm exec wave self-update";
|
|
49
|
+
}
|
|
50
|
+
if (packageManagerId === "npm") {
|
|
51
|
+
return "npm exec -- wave self-update";
|
|
52
|
+
}
|
|
53
|
+
if (packageManagerId === "yarn") {
|
|
54
|
+
return "yarn exec wave self-update";
|
|
55
|
+
}
|
|
56
|
+
if (packageManagerId === "bun") {
|
|
57
|
+
return "bun x wave self-update";
|
|
58
|
+
}
|
|
59
|
+
if (fs.existsSync(path.join(workspaceRoot, "pnpm-lock.yaml"))) {
|
|
60
|
+
return "pnpm exec wave self-update";
|
|
61
|
+
}
|
|
62
|
+
if (fs.existsSync(path.join(workspaceRoot, "yarn.lock"))) {
|
|
63
|
+
return "yarn exec wave self-update";
|
|
64
|
+
}
|
|
65
|
+
if (fs.existsSync(path.join(workspaceRoot, "bun.lock")) || fs.existsSync(path.join(workspaceRoot, "bun.lockb"))) {
|
|
66
|
+
return "bun x wave self-update";
|
|
67
|
+
}
|
|
68
|
+
return "npm exec -- wave self-update";
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function buildPackageLatestUrl(packageName) {
|
|
72
|
+
return `${NPM_REGISTRY_LATEST_URL}/${encodeURIComponent(String(packageName || WAVE_PACKAGE_NAME)).replace("%40", "@")}/latest`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function readUpdateCheckCache(cachePath = PACKAGE_UPDATE_CHECK_PATH) {
|
|
76
|
+
const payload = readJsonOrNull(cachePath);
|
|
77
|
+
return payload && typeof payload === "object" ? payload : null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function writeUpdateCheckCache(cachePath, payload) {
|
|
81
|
+
ensureDirectory(path.dirname(cachePath));
|
|
82
|
+
writeJsonAtomic(cachePath, payload);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function buildNoticeLines(packageName, currentVersion, latestVersion, workspaceRoot = REPO_ROOT) {
|
|
86
|
+
return [
|
|
87
|
+
`[wave:update] newer ${packageName} available: installed ${currentVersion}, latest ${latestVersion}`,
|
|
88
|
+
`[wave:update] update now with: ${runtimeSelfUpdateCommand(workspaceRoot)}`,
|
|
89
|
+
];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function emitNotice(packageName, currentVersion, latestVersion, emit = console.error, workspaceRoot = REPO_ROOT) {
|
|
93
|
+
for (const line of buildNoticeLines(packageName, currentVersion, latestVersion, workspaceRoot)) {
|
|
94
|
+
emit(line);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function fetchLatestPackageVersion(
|
|
99
|
+
packageName = WAVE_PACKAGE_NAME,
|
|
100
|
+
{
|
|
101
|
+
fetchImpl = globalThis.fetch,
|
|
102
|
+
timeoutMs = PACKAGE_UPDATE_CHECK_TIMEOUT_MS,
|
|
103
|
+
} = {},
|
|
104
|
+
) {
|
|
105
|
+
if (typeof fetchImpl !== "function") {
|
|
106
|
+
throw new Error("Package update check is unavailable in this Node runtime.");
|
|
107
|
+
}
|
|
108
|
+
const abortController = new AbortController();
|
|
109
|
+
const timer = setTimeout(() => abortController.abort(), timeoutMs);
|
|
110
|
+
try {
|
|
111
|
+
const response = await fetchImpl(buildPackageLatestUrl(packageName), {
|
|
112
|
+
signal: abortController.signal,
|
|
113
|
+
headers: {
|
|
114
|
+
Accept: "application/json",
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
if (!response?.ok) {
|
|
118
|
+
throw new Error(`Upstream package check failed with status ${response?.status || "unknown"}.`);
|
|
119
|
+
}
|
|
120
|
+
const payload = await response.json();
|
|
121
|
+
const latestVersion = String(payload?.version || "").trim();
|
|
122
|
+
if (!latestVersion) {
|
|
123
|
+
throw new Error("Upstream package check returned no version.");
|
|
124
|
+
}
|
|
125
|
+
return latestVersion;
|
|
126
|
+
} finally {
|
|
127
|
+
clearTimeout(timer);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export async function maybeAnnouncePackageUpdate(options = {}) {
|
|
132
|
+
const env = options.env || process.env;
|
|
133
|
+
if (
|
|
134
|
+
isTruthyEnvValue(env[WAVE_SKIP_UPDATE_CHECK_ENV]) ||
|
|
135
|
+
isTruthyEnvValue(env[WAVE_SUPPRESS_UPDATE_NOTICE_ENV])
|
|
136
|
+
) {
|
|
137
|
+
return {
|
|
138
|
+
skipped: true,
|
|
139
|
+
reason: "disabled",
|
|
140
|
+
updateAvailable: false,
|
|
141
|
+
latestVersion: null,
|
|
142
|
+
currentVersion: null,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const metadata = options.packageMetadata || readInstalledPackageMetadata();
|
|
147
|
+
const packageName = String(metadata.name || WAVE_PACKAGE_NAME);
|
|
148
|
+
const currentVersion = String(metadata.version || "").trim();
|
|
149
|
+
const cachePath = options.cachePath || PACKAGE_UPDATE_CHECK_PATH;
|
|
150
|
+
const workspaceRoot = options.workspaceRoot || REPO_ROOT;
|
|
151
|
+
const cacheTtlMs = options.cacheTtlMs ?? PACKAGE_UPDATE_CHECK_TTL_MS;
|
|
152
|
+
const nowMs = options.nowMs ?? Date.now();
|
|
153
|
+
const emit = options.emit || console.error;
|
|
154
|
+
const cache = readUpdateCheckCache(cachePath);
|
|
155
|
+
const cachedCheckedAtMs = Date.parse(String(cache?.checkedAt || ""));
|
|
156
|
+
const cacheMatchesCurrentVersion = cache?.currentVersion === currentVersion;
|
|
157
|
+
const cachedUpdateAvailable =
|
|
158
|
+
cacheMatchesCurrentVersion &&
|
|
159
|
+
typeof cache?.latestVersion === "string" &&
|
|
160
|
+
compareVersions(cache.latestVersion, currentVersion) > 0;
|
|
161
|
+
const cacheFresh =
|
|
162
|
+
cacheMatchesCurrentVersion &&
|
|
163
|
+
Number.isFinite(cachedCheckedAtMs) &&
|
|
164
|
+
nowMs - cachedCheckedAtMs <= cacheTtlMs;
|
|
165
|
+
let emitted = false;
|
|
166
|
+
|
|
167
|
+
if (cachedUpdateAvailable) {
|
|
168
|
+
emitNotice(packageName, currentVersion, cache.latestVersion, emit, workspaceRoot);
|
|
169
|
+
emitted = true;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (cacheFresh) {
|
|
173
|
+
return {
|
|
174
|
+
skipped: false,
|
|
175
|
+
source: "cache",
|
|
176
|
+
updateAvailable: cachedUpdateAvailable,
|
|
177
|
+
latestVersion: cache?.latestVersion || currentVersion,
|
|
178
|
+
currentVersion,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
const latestVersion = await fetchLatestPackageVersion(packageName, options);
|
|
184
|
+
const updateAvailable = compareVersions(latestVersion, currentVersion) > 0;
|
|
185
|
+
writeUpdateCheckCache(cachePath, {
|
|
186
|
+
schemaVersion: PACKAGE_UPDATE_CHECK_SCHEMA_VERSION,
|
|
187
|
+
packageName,
|
|
188
|
+
checkedAt: new Date(nowMs).toISOString(),
|
|
189
|
+
currentVersion,
|
|
190
|
+
latestVersion,
|
|
191
|
+
updateAvailable,
|
|
192
|
+
lastErrorAt: null,
|
|
193
|
+
lastErrorMessage: null,
|
|
194
|
+
});
|
|
195
|
+
if (updateAvailable && !emitted) {
|
|
196
|
+
emitNotice(packageName, currentVersion, latestVersion, emit, workspaceRoot);
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
skipped: false,
|
|
200
|
+
source: "network",
|
|
201
|
+
updateAvailable,
|
|
202
|
+
latestVersion,
|
|
203
|
+
currentVersion,
|
|
204
|
+
};
|
|
205
|
+
} catch (error) {
|
|
206
|
+
writeUpdateCheckCache(cachePath, {
|
|
207
|
+
schemaVersion: PACKAGE_UPDATE_CHECK_SCHEMA_VERSION,
|
|
208
|
+
packageName,
|
|
209
|
+
checkedAt: new Date(nowMs).toISOString(),
|
|
210
|
+
currentVersion,
|
|
211
|
+
latestVersion:
|
|
212
|
+
cacheMatchesCurrentVersion && typeof cache?.latestVersion === "string"
|
|
213
|
+
? cache.latestVersion
|
|
214
|
+
: currentVersion,
|
|
215
|
+
updateAvailable: cachedUpdateAvailable,
|
|
216
|
+
lastErrorAt: new Date(nowMs).toISOString(),
|
|
217
|
+
lastErrorMessage: error instanceof Error ? error.message : String(error),
|
|
218
|
+
});
|
|
219
|
+
return {
|
|
220
|
+
skipped: false,
|
|
221
|
+
source: "error",
|
|
222
|
+
updateAvailable: cachedUpdateAvailable,
|
|
223
|
+
latestVersion:
|
|
224
|
+
cacheMatchesCurrentVersion && typeof cache?.latestVersion === "string"
|
|
225
|
+
? cache.latestVersion
|
|
226
|
+
: currentVersion,
|
|
227
|
+
currentVersion,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { PACKAGE_ROOT, readJsonOrNull } from "./shared.mjs";
|
|
3
|
+
|
|
4
|
+
export const WAVE_PACKAGE_NAME = "@chllming/wave-orchestration";
|
|
5
|
+
export const PACKAGE_METADATA_PATH = path.join(PACKAGE_ROOT, "package.json");
|
|
6
|
+
|
|
7
|
+
export function readInstalledPackageMetadata(metadataPath = PACKAGE_METADATA_PATH) {
|
|
8
|
+
const payload = readJsonOrNull(metadataPath);
|
|
9
|
+
if (!payload?.name || !payload?.version) {
|
|
10
|
+
throw new Error(`Invalid package metadata: ${metadataPath}`);
|
|
11
|
+
}
|
|
12
|
+
return payload;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function normalizeVersionParts(version) {
|
|
16
|
+
return String(version || "")
|
|
17
|
+
.split(".")
|
|
18
|
+
.map((part) => Number.parseInt(part.replace(/[^0-9].*$/, ""), 10) || 0);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function compareVersions(a, b) {
|
|
22
|
+
const left = normalizeVersionParts(a);
|
|
23
|
+
const right = normalizeVersionParts(b);
|
|
24
|
+
const length = Math.max(left.length, right.length);
|
|
25
|
+
for (let index = 0; index < length; index += 1) {
|
|
26
|
+
const diff = (left[index] || 0) - (right[index] || 0);
|
|
27
|
+
if (diff !== 0) {
|
|
28
|
+
return diff;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return 0;
|
|
32
|
+
}
|
package/scripts/wave.mjs
CHANGED
|
@@ -12,6 +12,7 @@ function printHelp() {
|
|
|
12
12
|
console.log(`Usage:
|
|
13
13
|
wave init [options]
|
|
14
14
|
wave upgrade [options]
|
|
15
|
+
wave self-update
|
|
15
16
|
wave changelog [options]
|
|
16
17
|
wave doctor [options]
|
|
17
18
|
wave project setup [options]
|
|
@@ -25,6 +26,7 @@ function printHelp() {
|
|
|
25
26
|
wave local [local executor options]
|
|
26
27
|
wave coord [coordination options]
|
|
27
28
|
wave dep [dependency options]
|
|
29
|
+
wave benchmark [benchmark options]
|
|
28
30
|
|
|
29
31
|
Global options:
|
|
30
32
|
--repo-root <path> Run the command against a target workspace root
|
|
@@ -36,7 +38,7 @@ if (!subcommand || subcommand === "--help" || subcommand === "-h" || subcommand
|
|
|
36
38
|
process.exit(0);
|
|
37
39
|
}
|
|
38
40
|
|
|
39
|
-
if (["init", "upgrade", "changelog", "doctor"].includes(subcommand)) {
|
|
41
|
+
if (["init", "upgrade", "self-update", "changelog", "doctor"].includes(subcommand)) {
|
|
40
42
|
try {
|
|
41
43
|
const { runInstallCli } = await import("./wave-orchestrator/install.mjs");
|
|
42
44
|
await runInstallCli([subcommand, ...rest]);
|
|
@@ -71,7 +73,7 @@ if (["init", "upgrade", "changelog", "doctor"].includes(subcommand)) {
|
|
|
71
73
|
} else if (subcommand === "autonomous") {
|
|
72
74
|
const { runAutonomousCli } = await import("./wave-orchestrator/autonomous.mjs");
|
|
73
75
|
try {
|
|
74
|
-
runAutonomousCli(rest);
|
|
76
|
+
await runAutonomousCli(rest);
|
|
75
77
|
} catch (error) {
|
|
76
78
|
console.error(`[wave] ${error instanceof Error ? error.message : String(error)}`);
|
|
77
79
|
process.exit(1);
|
|
@@ -116,6 +118,14 @@ if (["init", "upgrade", "changelog", "doctor"].includes(subcommand)) {
|
|
|
116
118
|
console.error(`[wave] ${error instanceof Error ? error.message : String(error)}`);
|
|
117
119
|
process.exit(1);
|
|
118
120
|
}
|
|
121
|
+
} else if (subcommand === "benchmark") {
|
|
122
|
+
try {
|
|
123
|
+
const { runBenchmarkCli } = await import("./wave-orchestrator/benchmark.mjs");
|
|
124
|
+
await runBenchmarkCli(rest);
|
|
125
|
+
} catch (error) {
|
|
126
|
+
console.error(`[wave] ${error instanceof Error ? error.message : String(error)}`);
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
119
129
|
} else {
|
|
120
130
|
console.error(`[wave] Unknown subcommand: ${subcommand}`);
|
|
121
131
|
printHelp();
|