@nwire/cli 0.7.1 → 0.8.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 (84) hide show
  1. package/README.md +62 -20
  2. package/dist/__tests__/bench.test.d.ts +2 -0
  3. package/dist/__tests__/bench.test.d.ts.map +1 -0
  4. package/dist/__tests__/bench.test.js +94 -0
  5. package/dist/__tests__/bench.test.js.map +1 -0
  6. package/dist/__tests__/replay.test.d.ts +2 -0
  7. package/dist/__tests__/replay.test.d.ts.map +1 -0
  8. package/dist/__tests__/replay.test.js +202 -0
  9. package/dist/__tests__/replay.test.js.map +1 -0
  10. package/dist/__tests__/trace.test.d.ts +2 -0
  11. package/dist/__tests__/trace.test.d.ts.map +1 -0
  12. package/dist/__tests__/trace.test.js +136 -0
  13. package/dist/__tests__/trace.test.js.map +1 -0
  14. package/dist/__tests__/watch.test.d.ts +2 -0
  15. package/dist/__tests__/watch.test.d.ts.map +1 -0
  16. package/dist/__tests__/watch.test.js +109 -0
  17. package/dist/__tests__/watch.test.js.map +1 -0
  18. package/dist/cache-runner.js +62 -14
  19. package/dist/cache-runner.js.map +1 -1
  20. package/dist/cli.js +8 -0
  21. package/dist/cli.js.map +1 -1
  22. package/dist/commands/bench.d.ts +103 -0
  23. package/dist/commands/bench.d.ts.map +1 -0
  24. package/dist/commands/bench.js +203 -0
  25. package/dist/commands/bench.js.map +1 -0
  26. package/dist/commands/dev.d.ts.map +1 -1
  27. package/dist/commands/dev.js +31 -20
  28. package/dist/commands/dev.js.map +1 -1
  29. package/dist/commands/doctor.d.ts.map +1 -1
  30. package/dist/commands/doctor.js +29 -5
  31. package/dist/commands/doctor.js.map +1 -1
  32. package/dist/commands/infra.d.ts.map +1 -1
  33. package/dist/commands/infra.js +7 -0
  34. package/dist/commands/infra.js.map +1 -1
  35. package/dist/commands/logs.d.ts.map +1 -1
  36. package/dist/commands/logs.js +64 -11
  37. package/dist/commands/logs.js.map +1 -1
  38. package/dist/commands/replay.d.ts +45 -0
  39. package/dist/commands/replay.d.ts.map +1 -0
  40. package/dist/commands/replay.js +159 -0
  41. package/dist/commands/replay.js.map +1 -0
  42. package/dist/commands/run.d.ts.map +1 -1
  43. package/dist/commands/run.js +14 -4
  44. package/dist/commands/run.js.map +1 -1
  45. package/dist/commands/studio.d.ts.map +1 -1
  46. package/dist/commands/studio.js +7 -1
  47. package/dist/commands/studio.js.map +1 -1
  48. package/dist/commands/test.d.ts.map +1 -1
  49. package/dist/commands/test.js +3 -0
  50. package/dist/commands/test.js.map +1 -1
  51. package/dist/commands/trace.d.ts +40 -0
  52. package/dist/commands/trace.d.ts.map +1 -0
  53. package/dist/commands/trace.js +195 -0
  54. package/dist/commands/trace.js.map +1 -0
  55. package/dist/commands/watch.d.ts +33 -0
  56. package/dist/commands/watch.d.ts.map +1 -0
  57. package/dist/commands/watch.js +152 -0
  58. package/dist/commands/watch.js.map +1 -0
  59. package/dist/lib/dev-entry.d.ts +39 -0
  60. package/dist/lib/dev-entry.d.ts.map +1 -0
  61. package/dist/lib/dev-entry.js +102 -0
  62. package/dist/lib/dev-entry.js.map +1 -0
  63. package/dist/lib/exec.d.ts +7 -0
  64. package/dist/lib/exec.d.ts.map +1 -1
  65. package/dist/lib/exec.js +19 -1
  66. package/dist/lib/exec.js.map +1 -1
  67. package/dist/lib/run-task.d.ts.map +1 -1
  68. package/dist/lib/run-task.js +6 -1
  69. package/dist/lib/run-task.js.map +1 -1
  70. package/dist/lib/sse.d.ts +24 -0
  71. package/dist/lib/sse.d.ts.map +1 -0
  72. package/dist/lib/sse.js +63 -0
  73. package/dist/lib/sse.js.map +1 -0
  74. package/dist/lib/wire-discovery.d.ts +9 -0
  75. package/dist/lib/wire-discovery.d.ts.map +1 -0
  76. package/dist/lib/wire-discovery.js +82 -0
  77. package/dist/lib/wire-discovery.js.map +1 -0
  78. package/dist/load-config.d.ts +3 -3
  79. package/dist/load-config.d.ts.map +1 -1
  80. package/dist/load-config.js +15 -3
  81. package/dist/load-config.js.map +1 -1
  82. package/dist/ls-runner.js +2 -2
  83. package/dist/ls-runner.js.map +1 -1
  84. package/package.json +5 -3
@@ -0,0 +1,152 @@
1
+ /**
2
+ * `nwire watch <hook> [--limit <N>]`
3
+ *
4
+ * Streams `/_nwire/telemetry/stream` from the running wire and filters
5
+ * for `hook.step` records whose `hookName` matches the requested glob.
6
+ * Globs use a simple `*` → `.*` translation, so:
7
+ *
8
+ * • `plugin.boot:identity` matches exactly that hook
9
+ * • `plugin.boot:*` matches every plugin.boot hook
10
+ *
11
+ * One line per step:
12
+ *
13
+ * HH:MM:SS.mmm <phase> <stepKind> <stepName> (Xms) [error: msg]
14
+ *
15
+ * Phase colour: start=dim, end=green, error=red.
16
+ */
17
+ import { defineCommand } from "citty";
18
+ import { palette } from "../lib/colors.js";
19
+ import { findWireUrl } from "../lib/wire-discovery.js";
20
+ import { SseParser } from "../lib/sse.js";
21
+ // ─── Helpers (exported for testing) ────────────────────────────────
22
+ /**
23
+ * Translate a `*`-glob into a `^…$` anchored RegExp. No other glob
24
+ * features (`?`, character classes) are supported — keeping the surface
25
+ * tight matches the rest of the framework's "one obvious way" stance.
26
+ */
27
+ export function hookGlobToRegex(glob) {
28
+ const escaped = glob.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
29
+ return new RegExp(`^${escaped.replace(/\*/g, ".*")}$`);
30
+ }
31
+ const PAD2 = (n) => n.toString().padStart(2, "0");
32
+ const PAD3 = (n) => n.toString().padStart(3, "0");
33
+ function formatTimestamp(at) {
34
+ const d = at === undefined ? new Date() : new Date(at);
35
+ if (Number.isNaN(d.getTime()))
36
+ return "??:??:??.???";
37
+ return `${PAD2(d.getHours())}:${PAD2(d.getMinutes())}:${PAD2(d.getSeconds())}.${PAD3(d.getMilliseconds())}`;
38
+ }
39
+ export function formatHookStepLine(step) {
40
+ const time = palette.dim(formatTimestamp(step.occurredAt));
41
+ const rawPhase = (step.phase ?? "").padEnd(5, " ");
42
+ const phase = step.phase === "end" ? palette.ok(rawPhase) :
43
+ step.phase === "error" ? palette.err(rawPhase) :
44
+ palette.dim(rawPhase);
45
+ const stepKind = (step.stepKind ?? "-").padEnd(10, " ");
46
+ const stepName = step.stepName ?? "-";
47
+ const dur = typeof step.durationMs === "number"
48
+ ? palette.dim(`(${step.durationMs}ms)`)
49
+ : "";
50
+ let errPart = "";
51
+ if (step.error) {
52
+ const msg = typeof step.error === "string" ? step.error : step.error.message ?? "error";
53
+ errPart = " " + palette.err(`[error: ${msg}]`);
54
+ }
55
+ return `${time} ${phase} ${stepKind} ${stepName} ${dur}${errPart}`.trimEnd();
56
+ }
57
+ async function streamOnce(opts) {
58
+ const res = await fetch(`${opts.url}/_nwire/telemetry/stream`, {
59
+ headers: { accept: "text/event-stream" },
60
+ signal: opts.signal,
61
+ });
62
+ if (!res.ok || !res.body)
63
+ throw new Error(`stream returned ${res.status}`);
64
+ const reader = res.body.getReader();
65
+ const decoder = new TextDecoder("utf-8");
66
+ const parser = new SseParser();
67
+ let printed = 0;
68
+ while (!opts.signal.aborted) {
69
+ const { value, done } = await reader.read();
70
+ if (done)
71
+ return false;
72
+ const chunk = decoder.decode(value, { stream: true });
73
+ for (const payload of parser.feed(chunk)) {
74
+ const rec = payload;
75
+ if (rec.kind !== "hook.step")
76
+ continue;
77
+ const step = rec;
78
+ if (!opts.match.test(step.hookName))
79
+ continue;
80
+ opts.onLine(formatHookStepLine(step));
81
+ printed += 1;
82
+ if (opts.limit && printed >= opts.limit)
83
+ return true;
84
+ }
85
+ }
86
+ return false;
87
+ }
88
+ export const watchCommand = defineCommand({
89
+ meta: {
90
+ name: "watch",
91
+ description: "Tail hook-step telemetry for a hook (supports * wildcards)",
92
+ },
93
+ args: {
94
+ hook: {
95
+ type: "positional",
96
+ required: true,
97
+ description: "Hook name to watch (e.g. plugin.boot:identity, or plugin.boot:*)",
98
+ },
99
+ limit: {
100
+ type: "string",
101
+ description: "Stop after N matching steps",
102
+ required: false,
103
+ },
104
+ },
105
+ async run({ args }) {
106
+ const url = await findWireUrl();
107
+ if (!url) {
108
+ console.error(palette.err("nwire watch:") +
109
+ " no running wire found. Start one with `nwire dev`, or set NWIRE_INSPECT_URL.");
110
+ process.exit(1);
111
+ }
112
+ const hookArg = String(args.hook);
113
+ const match = hookGlobToRegex(hookArg);
114
+ const limit = typeof args.limit === "string" ? Number(args.limit) : undefined;
115
+ const ctrl = new AbortController();
116
+ const stop = () => ctrl.abort();
117
+ process.on("SIGINT", stop);
118
+ process.on("SIGTERM", stop);
119
+ console.log(palette.dim(`# watching ${hookArg} on ${url}/_nwire/telemetry/stream — Ctrl+C to exit`));
120
+ while (!ctrl.signal.aborted) {
121
+ try {
122
+ const limitReached = await streamOnce({
123
+ url,
124
+ match,
125
+ limit,
126
+ signal: ctrl.signal,
127
+ onLine: (l) => console.log(l),
128
+ });
129
+ if (limitReached)
130
+ process.exit(0);
131
+ // Stream ended cleanly (EOF) — reconnect.
132
+ }
133
+ catch (err) {
134
+ if (ctrl.signal.aborted)
135
+ break;
136
+ const msg = err instanceof Error ? err.message : String(err);
137
+ console.log(palette.warn(`# disconnected (${msg}) — reconnecting in 1s…`));
138
+ }
139
+ await sleep(1_000, ctrl.signal);
140
+ }
141
+ process.exit(0);
142
+ },
143
+ });
144
+ function sleep(ms, signal) {
145
+ return new Promise((res) => {
146
+ if (signal.aborted)
147
+ return res();
148
+ const t = setTimeout(() => res(), ms);
149
+ signal.addEventListener("abort", () => { clearTimeout(t); res(); }, { once: true });
150
+ });
151
+ }
152
+ //# sourceMappingURL=watch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watch.js","sourceRoot":"","sources":["../../src/commands/watch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * `resolveDevEntry` — figure out how to boot the project's dev server.
3
+ *
4
+ * Nwire projects come in three shapes, and `nwire dev` needs to work on all
5
+ * of them out of the box (no `nwire.config.ts` required):
6
+ *
7
+ * 1. Multi-wire monorepo style — `apps/dev-all/run.ts` runs every app.
8
+ * 2. Single wire under `apps/` — `apps/<name>/{run,main}.ts`.
9
+ * 3. Single-app style — `app/{main,run,index}.ts` (the small-example shape
10
+ * used by hello-world / todo-app / moderation-queue).
11
+ * 4. Fallback — `package.json` exposes a `scripts.dev` entry; we honor it.
12
+ *
13
+ * Returns a discriminated value the caller can spawn directly. The CLI prints
14
+ * a banner before handing off, so the kind + label flow through to the user.
15
+ */
16
+ export type DevEntry = {
17
+ readonly kind: "vite-node";
18
+ /** Absolute path to the .ts entry file. */
19
+ readonly entry: string;
20
+ /** Project-relative path of `entry`, for the banner. */
21
+ readonly rel: string;
22
+ /** Subtitle shown in the banner: dev-all | <wire-name> | app. */
23
+ readonly label: string;
24
+ } | {
25
+ readonly kind: "npm-script";
26
+ /** Script name in package.json (usually "dev"). */
27
+ readonly script: string;
28
+ /** Resolved command string from package.json (for the banner). */
29
+ readonly command: string;
30
+ };
31
+ /**
32
+ * Walks the resolution order described above. Returns the first hit, or
33
+ * undefined when no recognizable entry exists. The caller should error out
34
+ * with a clear message listing what was tried.
35
+ */
36
+ export declare function resolveDevEntry(cwd: string): DevEntry | undefined;
37
+ /** Same fallback list rendered for the "no dev entry found" error message. */
38
+ export declare function devEntryCandidates(): readonly string[];
39
+ //# sourceMappingURL=dev-entry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-entry.d.ts","sourceRoot":"","sources":["../../src/lib/dev-entry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,MAAM,MAAM,QAAQ,GAChB;IACE,QAAQ,CAAC,IAAI,EAAG,WAAW,CAAC;IAC5B,2CAA2C;IAC3C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,wDAAwD;IACxD,QAAQ,CAAC,GAAG,EAAI,MAAM,CAAC;IACvB,iEAAiE;IACjE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,GACD;IACE,QAAQ,CAAC,IAAI,EAAI,YAAY,CAAC;IAC9B,mDAAmD;IACnD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,kEAAkE;IAClE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEN;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAyDjE;AAED,8EAA8E;AAC9E,wBAAgB,kBAAkB,IAAI,SAAS,MAAM,EAAE,CAUtD"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * `resolveDevEntry` — figure out how to boot the project's dev server.
3
+ *
4
+ * Nwire projects come in three shapes, and `nwire dev` needs to work on all
5
+ * of them out of the box (no `nwire.config.ts` required):
6
+ *
7
+ * 1. Multi-wire monorepo style — `apps/dev-all/run.ts` runs every app.
8
+ * 2. Single wire under `apps/` — `apps/<name>/{run,main}.ts`.
9
+ * 3. Single-app style — `app/{main,run,index}.ts` (the small-example shape
10
+ * used by hello-world / todo-app / moderation-queue).
11
+ * 4. Fallback — `package.json` exposes a `scripts.dev` entry; we honor it.
12
+ *
13
+ * Returns a discriminated value the caller can spawn directly. The CLI prints
14
+ * a banner before handing off, so the kind + label flow through to the user.
15
+ */
16
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
17
+ import { relative, resolve } from "node:path";
18
+ /**
19
+ * Walks the resolution order described above. Returns the first hit, or
20
+ * undefined when no recognizable entry exists. The caller should error out
21
+ * with a clear message listing what was tried.
22
+ */
23
+ export function resolveDevEntry(cwd) {
24
+ // 1. dev-all (multi-wire dev topology)
25
+ const devAll = resolve(cwd, "apps/dev-all/run.ts");
26
+ if (existsSync(devAll)) {
27
+ return { kind: "vite-node", entry: devAll, rel: relPath(cwd, devAll), label: "dev-all" };
28
+ }
29
+ // 2. Single wire under apps/ — accept run.ts OR main.ts.
30
+ const appsDir = resolve(cwd, "apps");
31
+ if (existsSync(appsDir)) {
32
+ // Cheap enumeration — projects rarely have more than a few entries here.
33
+ // We don't need detectProject (avoids the import cycle and keeps this
34
+ // helper focused).
35
+ try {
36
+ const wires = [];
37
+ for (const ent of readdirSync(appsDir, { withFileTypes: true })) {
38
+ if (!ent.isDirectory())
39
+ continue;
40
+ const a = resolve(appsDir, ent.name, "run.ts");
41
+ const b = resolve(appsDir, ent.name, "main.ts");
42
+ if (existsSync(a) || existsSync(b))
43
+ wires.push(ent.name);
44
+ }
45
+ if (wires.length === 1) {
46
+ const only = wires[0];
47
+ for (const f of ["run.ts", "main.ts"]) {
48
+ const p = resolve(appsDir, only, f);
49
+ if (existsSync(p)) {
50
+ return { kind: "vite-node", entry: p, rel: relPath(cwd, p), label: only };
51
+ }
52
+ }
53
+ }
54
+ }
55
+ catch {
56
+ // ignore — fall through to app/ and scripts
57
+ }
58
+ }
59
+ // 3. Single-app folder — app/main.ts, app/run.ts, app/index.ts.
60
+ for (const f of ["app/main.ts", "app/run.ts", "app/index.ts"]) {
61
+ const p = resolve(cwd, f);
62
+ if (existsSync(p)) {
63
+ return { kind: "vite-node", entry: p, rel: relPath(cwd, p), label: "app" };
64
+ }
65
+ }
66
+ // 4. package.json scripts.dev fallback.
67
+ const pj = resolve(cwd, "package.json");
68
+ if (existsSync(pj)) {
69
+ try {
70
+ const raw = readFileSync(pj, "utf8");
71
+ const parsed = JSON.parse(raw);
72
+ const command = parsed.scripts?.dev;
73
+ if (command)
74
+ return { kind: "npm-script", script: "dev", command };
75
+ }
76
+ catch {
77
+ // ignore
78
+ }
79
+ }
80
+ return undefined;
81
+ }
82
+ /** Same fallback list rendered for the "no dev entry found" error message. */
83
+ export function devEntryCandidates() {
84
+ return [
85
+ "apps/dev-all/run.ts",
86
+ "apps/<wire>/run.ts",
87
+ "apps/<wire>/main.ts",
88
+ "app/main.ts",
89
+ "app/run.ts",
90
+ "app/index.ts",
91
+ "package.json scripts.dev",
92
+ ];
93
+ }
94
+ function relPath(cwd, abs) {
95
+ // `path.relative` already handles the cross-platform `path.sep` story
96
+ // (Windows backslashes vs POSIX forward slashes) and the
97
+ // "abs is outside cwd" case (returns a `..` chain) — no manual
98
+ // `startsWith(cwd + "/")` math needed.
99
+ const rel = relative(cwd, abs);
100
+ return rel === "" ? abs : rel;
101
+ }
102
+ //# sourceMappingURL=dev-entry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-entry.js","sourceRoot":"","sources":["../../src/lib/dev-entry.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,13 @@
2
2
  * Shared process helpers. Commands invoke external tools (vite-node,
3
3
  * vite, oxlint, oxfmt, vitest) through here so signal handling, stdio,
4
4
  * and exit semantics are uniform everywhere.
5
+ *
6
+ * Windows note: `pnpm`, `vite`, `docker`, `tail`, and most other CLIs we
7
+ * shell out to ship as `.cmd` shims on Windows. `child_process.spawn`
8
+ * only resolves `.exe` extensions by default, so without `shell: true`
9
+ * every spawn call ENOENTs on Win. The `IS_WIN` constant + `shell` flag
10
+ * on every spawn site keeps POSIX behavior identical (no shell) while
11
+ * making Windows work transparently.
5
12
  */
6
13
  import { type ChildProcess } from "node:child_process";
7
14
  export interface ExecOptions {
@@ -1 +1 @@
1
- {"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../src/lib/exec.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAoB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIzE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IAC5D,uEAAuE;IACvE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,2EAA2E;AAC3E,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,GAAE,WAAgB,GAAG,MAAM,CAO7F;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAwB,SAAQ,WAAW;IAC1D,2EAA2E;IAC3E,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,4DAA4D;IAC5D,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,IAAI,GAAE,uBAA4B,GACjC;IAAE,KAAK,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;CAAE,CAyChD"}
1
+ {"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../src/lib/exec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAoB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAOzE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IAC5D,uEAAuE;IACvE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,2EAA2E;AAC3E,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,GAAE,WAAgB,GAAG,MAAM,CAQ7F;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAwB,SAAQ,WAAW;IAC1D,2EAA2E;IAC3E,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,4DAA4D;IAC5D,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,IAAI,GAAE,uBAA4B,GACjC;IAAE,KAAK,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;CAAE,CA+ChD"}
package/dist/lib/exec.js CHANGED
@@ -2,15 +2,25 @@
2
2
  * Shared process helpers. Commands invoke external tools (vite-node,
3
3
  * vite, oxlint, oxfmt, vitest) through here so signal handling, stdio,
4
4
  * and exit semantics are uniform everywhere.
5
+ *
6
+ * Windows note: `pnpm`, `vite`, `docker`, `tail`, and most other CLIs we
7
+ * shell out to ship as `.cmd` shims on Windows. `child_process.spawn`
8
+ * only resolves `.exe` extensions by default, so without `shell: true`
9
+ * every spawn call ENOENTs on Win. The `IS_WIN` constant + `shell` flag
10
+ * on every spawn site keeps POSIX behavior identical (no shell) while
11
+ * making Windows work transparently.
5
12
  */
6
13
  import { spawn, spawnSync } from "node:child_process";
7
14
  import { palette } from "./colors.js";
15
+ /** True on Windows. Drives the shell-fallback toggle on every spawn. */
16
+ const IS_WIN = process.platform === "win32";
8
17
  /** Synchronous run; returns the exit code (null → 0). For one-shot ops. */
9
18
  export function execSync(cmd, args, opts = {}) {
10
19
  const result = spawnSync(cmd, args, {
11
20
  stdio: opts.quiet ? "pipe" : "inherit",
12
21
  cwd: opts.cwd,
13
22
  env: opts.env ? { ...process.env, ...opts.env } : process.env,
23
+ shell: IS_WIN,
14
24
  });
15
25
  return result.status ?? 0;
16
26
  }
@@ -19,6 +29,7 @@ export function spawnInteractive(cmd, args, opts = {}) {
19
29
  stdio: "inherit",
20
30
  cwd: opts.cwd,
21
31
  env: opts.env ? { ...process.env, ...opts.env } : process.env,
32
+ shell: IS_WIN,
22
33
  });
23
34
  let shuttingDown = false;
24
35
  const forward = (signal) => {
@@ -32,7 +43,14 @@ export function spawnInteractive(cmd, args, opts = {}) {
32
43
  setTimeout(() => {
33
44
  if (child.exitCode === null && child.signalCode === null) {
34
45
  try {
35
- child.kill("SIGKILL");
46
+ // SIGKILL doesn't exist as a signal on Windows. Node maps it to
47
+ // TerminateProcess but emits a deprecation-style ENOSYS in some
48
+ // versions; passing no argument means "default terminate," which
49
+ // on Win is the same TerminateProcess we want.
50
+ if (IS_WIN)
51
+ child.kill();
52
+ else
53
+ child.kill("SIGKILL");
36
54
  }
37
55
  catch {
38
56
  // gone
@@ -1 +1 @@
1
- {"version":3,"file":"exec.js","sourceRoot":"","sources":["../../src/lib/exec.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,EAAE,SAAS,EAAqB,MAAM,oBAAoB,CAAC;AAEzE,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AASnC,2EAA2E;AAC3E,MAAM,UAAU,QAAQ,CAAC,GAAW,EAAE,IAAuB,EAAE,OAAoB,EAAE;IACnF,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE;QAClC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;QACtC,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG;KAC9D,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;AAC5B,CAAC;AAcD,MAAM,UAAU,gBAAgB,CAC9B,GAAW,EACX,IAAuB,EACvB,OAAgC,EAAE;IAElC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE;QAC7B,KAAK,EAAE,SAAS;QAChB,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG;KAC9D,CAAC,CAAC;IAEH,IAAI,YAAY,GAAG,KAAK,CAAC;IACzB,MAAM,OAAO,GAAG,CAAC,MAAsB,EAAE,EAAE;QACzC,YAAY,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC;YACH,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrB,CAAC;QAAC,MAAM,CAAC;YACP,eAAe;QACjB,CAAC;QACD,UAAU,CAAC,GAAG,EAAE;YACd,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;gBACzD,IAAI,CAAC;oBACH,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBACxB,CAAC;gBAAC,MAAM,CAAC;oBACP,OAAO;gBACT,CAAC;YACH,CAAC;QACH,CAAC,EAAE,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;IACzC,CAAC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC9C,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;IAEhD,MAAM,IAAI,GAAG,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE;QAC3C,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;YAChC,IAAI,YAAY,IAAI,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBAChE,sCAAsC;gBACtC,OAAO,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;gBACnE,OAAO,CAAC,CAAC,CAAC,CAAC;gBACX,OAAO;YACT,CAAC;YACD,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC;QACrB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AACzB,CAAC"}
1
+ {"version":3,"file":"exec.js","sourceRoot":"","sources":["../../src/lib/exec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,KAAK,EAAE,SAAS,EAAqB,MAAM,oBAAoB,CAAC;AAEzE,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAEnC,wEAAwE;AACxE,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC;AAS5C,2EAA2E;AAC3E,MAAM,UAAU,QAAQ,CAAC,GAAW,EAAE,IAAuB,EAAE,OAAoB,EAAE;IACnF,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE;QAClC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;QACtC,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG;QAC7D,KAAK,EAAE,MAAM;KACd,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;AAC5B,CAAC;AAcD,MAAM,UAAU,gBAAgB,CAC9B,GAAW,EACX,IAAuB,EACvB,OAAgC,EAAE;IAElC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE;QAC7B,KAAK,EAAE,SAAS;QAChB,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG;QAC7D,KAAK,EAAE,MAAM;KACd,CAAC,CAAC;IAEH,IAAI,YAAY,GAAG,KAAK,CAAC;IACzB,MAAM,OAAO,GAAG,CAAC,MAAsB,EAAE,EAAE;QACzC,YAAY,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC;YACH,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrB,CAAC;QAAC,MAAM,CAAC;YACP,eAAe;QACjB,CAAC;QACD,UAAU,CAAC,GAAG,EAAE;YACd,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;gBACzD,IAAI,CAAC;oBACH,gEAAgE;oBAChE,gEAAgE;oBAChE,iEAAiE;oBACjE,+CAA+C;oBAC/C,IAAI,MAAM;wBAAE,KAAK,CAAC,IAAI,EAAE,CAAC;;wBACpB,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBAC7B,CAAC;gBAAC,MAAM,CAAC;oBACP,OAAO;gBACT,CAAC;YACH,CAAC;QACH,CAAC,EAAE,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;IACzC,CAAC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC9C,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;IAEhD,MAAM,IAAI,GAAG,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE;QAC3C,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;YAChC,IAAI,YAAY,IAAI,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBAChE,sCAAsC;gBACtC,OAAO,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;gBACnE,OAAO,CAAC,CAAC,CAAC,CAAC;gBACX,OAAO;YACT,CAAC;YACD,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC;QACrB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AACzB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"run-task.d.ts","sourceRoot":"","sources":["../../src/lib/run-task.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,sCAAsC;IACtC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,uEAAuE;IACvE,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC;IACrD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC,CAkBD;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAkChF"}
1
+ {"version":3,"file":"run-task.d.ts","sourceRoot":"","sources":["../../src/lib/run-task.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,sCAAsC;IACtC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,uEAAuE;IACvE,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC;IACrD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC,CAmBD;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAoChF"}
@@ -6,6 +6,8 @@
6
6
  import { spawn } from "node:child_process";
7
7
  import { Listr } from "listr2";
8
8
  import { palette } from "./colors.js";
9
+ /** True on Windows — toggles `shell:true` on spawn so `.cmd` shims resolve. */
10
+ const IS_WIN = process.platform === "win32";
9
11
  /**
10
12
  * Run a single command and resolve with its exit code. The command's
11
13
  * stdout/stderr are captured and discarded — listr2 owns the visual
@@ -17,6 +19,7 @@ export function runCommand(spec) {
17
19
  const child = spawn(spec.command, spec.args, {
18
20
  cwd: spec.cwd,
19
21
  stdio: ["ignore", "pipe", "pipe"],
22
+ shell: IS_WIN,
20
23
  });
21
24
  const stdoutChunks = [];
22
25
  const stderrChunks = [];
@@ -44,7 +47,9 @@ export async function runTaskList(tasks) {
44
47
  aggregate = aggregate || result.code;
45
48
  const detail = result.stderr || result.stdout || `exit ${result.code}`;
46
49
  if (t.skipOnFail) {
47
- taskCtx.skip(detail.trim().split("\n")[0]);
50
+ // CRLF-safe split — child stdout on Windows ends with \r\n,
51
+ // which would leave a trailing \r on the printed hint.
52
+ taskCtx.skip(detail.trim().split(/\r?\n/)[0]);
48
53
  return;
49
54
  }
50
55
  throw new Error(detail.trim());
@@ -1 +1 @@
1
- {"version":3,"file":"run-task.js","sourceRoot":"","sources":["../../src/lib/run-task.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"run-task.js","sourceRoot":"","sources":["../../src/lib/run-task.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAE/B,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAEnC,+EAA+E;AAC/E,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC;AAY5C;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,IAAiB;IAK1C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAgB,EAAE;YACvD,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;YACjC,KAAK,EAAE,MAAM;SACd,CAAC,CAAC;QACH,MAAM,YAAY,GAAa,EAAE,CAAC;QAClC,MAAM,YAAY,GAAa,EAAE,CAAC;QAClC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9D,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9D,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CACxB,OAAO,CAAC;YACN,IAAI,EAAE,IAAI,IAAI,CAAC;YACf,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,QAAQ,EAAE;YAC9C,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,QAAQ,EAAE;SAC/C,CAAC,CACH,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,KAA6B;IAC7D,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,MAAM,IAAI,GAAG,IAAI,KAAK,CACpB,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAChB,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE;YACzB,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,CAAC,CAAC,CAAC;YACnC,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBACtB,SAAS,GAAG,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC;gBACrC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,IAAI,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;gBACvE,IAAI,CAAC,CAAC,UAAU,EAAE,CAAC;oBACjB,4DAA4D;oBAC5D,uDAAuD;oBACvD,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;oBAC9C,OAAO;gBACT,CAAC;gBACD,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;YACjC,CAAC;QACH,CAAC;KACF,CAAC,CAAC,EACH;QACE,UAAU,EAAE,KAAK;QACjB,WAAW,EAAE,KAAK;QAClB,eAAe,EAAE,EAAE,gBAAgB,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE;KACjE,CACF,CAAC;IACF,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;IACnB,CAAC;IAAC,MAAM,CAAC;QACP,oCAAoC;IACtC,CAAC;IACD,IAAI,SAAS,KAAK,CAAC,EAAE,CAAC;QACpB,sCAAsC;QACtC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Tiny SSE parser. The wire emits Server-Sent-Events frames of the form
3
+ *
4
+ * data: {"messageId":"…",…}\n
5
+ * \n
6
+ *
7
+ * — one JSON object per `data:` line, frames separated by a blank line.
8
+ * We also tolerate multi-line `data:` payloads (per the SSE spec joined
9
+ * with `\n`) and ignore comment frames (lines starting with `:`) so
10
+ * heartbeats don't pollute output.
11
+ *
12
+ * The parser is fed arbitrary chunks of UTF-8 text via `.feed(chunk)`
13
+ * and yields the parsed JSON payload for every complete frame. Partial
14
+ * frames are kept in an internal buffer until the trailing `\n\n` arrives.
15
+ */
16
+ export declare class SseParser {
17
+ private buffer;
18
+ /**
19
+ * Feed a new chunk of decoded text. Yields one parsed JSON payload
20
+ * per complete frame. Partial frames stay buffered for the next call.
21
+ */
22
+ feed(chunk: string): unknown[];
23
+ }
24
+ //# sourceMappingURL=sse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../src/lib/sse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAM;IAEpB;;;OAGG;IACH,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,EAAE;CAa/B"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Tiny SSE parser. The wire emits Server-Sent-Events frames of the form
3
+ *
4
+ * data: {"messageId":"…",…}\n
5
+ * \n
6
+ *
7
+ * — one JSON object per `data:` line, frames separated by a blank line.
8
+ * We also tolerate multi-line `data:` payloads (per the SSE spec joined
9
+ * with `\n`) and ignore comment frames (lines starting with `:`) so
10
+ * heartbeats don't pollute output.
11
+ *
12
+ * The parser is fed arbitrary chunks of UTF-8 text via `.feed(chunk)`
13
+ * and yields the parsed JSON payload for every complete frame. Partial
14
+ * frames are kept in an internal buffer until the trailing `\n\n` arrives.
15
+ */
16
+ export class SseParser {
17
+ buffer = "";
18
+ /**
19
+ * Feed a new chunk of decoded text. Yields one parsed JSON payload
20
+ * per complete frame. Partial frames stay buffered for the next call.
21
+ */
22
+ feed(chunk) {
23
+ this.buffer += chunk.replace(/\r\n/g, "\n");
24
+ const out = [];
25
+ let idx;
26
+ // Frames are terminated by a blank line (`\n\n`).
27
+ while ((idx = this.buffer.indexOf("\n\n")) !== -1) {
28
+ const frame = this.buffer.slice(0, idx);
29
+ this.buffer = this.buffer.slice(idx + 2);
30
+ const payload = parseFrame(frame);
31
+ if (payload !== undefined)
32
+ out.push(payload);
33
+ }
34
+ return out;
35
+ }
36
+ }
37
+ function parseFrame(frame) {
38
+ const lines = frame.split("\n");
39
+ const dataParts = [];
40
+ for (const raw of lines) {
41
+ if (raw.length === 0)
42
+ continue;
43
+ // SSE comment / heartbeat — drop silently.
44
+ if (raw.startsWith(":"))
45
+ continue;
46
+ if (raw.startsWith("data:")) {
47
+ // The spec strips one leading space after the colon if present.
48
+ const body = raw.slice(5).replace(/^ /, "");
49
+ dataParts.push(body);
50
+ }
51
+ // event:/id:/retry: lines are ignored — we only care about payloads.
52
+ }
53
+ if (dataParts.length === 0)
54
+ return undefined;
55
+ const joined = dataParts.join("\n");
56
+ try {
57
+ return JSON.parse(joined);
58
+ }
59
+ catch {
60
+ return undefined;
61
+ }
62
+ }
63
+ //# sourceMappingURL=sse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse.js","sourceRoot":"","sources":["../../src/lib/sse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,SAAS;IACZ,MAAM,GAAG,EAAE,CAAC;IAEpB;;;OAGG;IACH,IAAI,CAAC,KAAa;QAChB,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAC5C,MAAM,GAAG,GAAc,EAAE,CAAC;QAC1B,IAAI,GAAW,CAAC;QAChB,kDAAkD;QAClD,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;YAClD,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;YACxC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;YACzC,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;YAClC,IAAI,OAAO,KAAK,SAAS;gBAAE,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC/C,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;CACF;AAED,SAAS,UAAU,CAAC,KAAa;IAC/B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAChC,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAC/B,2CAA2C;QAC3C,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QAClC,IAAI,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5B,gEAAgE;YAChE,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC5C,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;QACD,qEAAqE;IACvE,CAAC;IACD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC7C,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Find the base URL of a running wire, or null if none is discoverable.
3
+ *
4
+ * Honours `NWIRE_INSPECT_URL` (trusted blindly — caller asked for it).
5
+ * Otherwise probes the canonical port list and returns the first wire
6
+ * whose `/_nwire/events/recent?limit=1` responds with a JSON array.
7
+ */
8
+ export declare function findWireUrl(): Promise<string | null>;
9
+ //# sourceMappingURL=wire-discovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wire-discovery.d.ts","sourceRoot":"","sources":["../../src/lib/wire-discovery.ts"],"names":[],"mappings":"AAsBA;;;;;;GAMG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAW1D"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Wire discovery — find a running Nwire wire on localhost.
3
+ *
4
+ * Mirrors the pattern used by Studio (`packages/nwire-studio/vite.config.ts`)
5
+ * and the MCP inspect tools (`packages/nwire-mcp/src/inspect.ts`):
6
+ *
7
+ * 1. Honour the `NWIRE_INSPECT_URL` env var if set.
8
+ * 2. Otherwise probe a small fixed list of ports for a wire that
9
+ * answers `GET /_nwire/events/recent?limit=1` with a JSON array.
10
+ *
11
+ * Both `nwire trace` and `nwire watch` consume this helper.
12
+ */
13
+ import { createConnection } from "node:net";
14
+ import { request as httpRequest } from "node:http";
15
+ const DEFAULT_PROBE_PORTS = [
16
+ 3000, 3001, 3002, 3003, 3004, 3005, 3006, 3007, 3008, 3009, 3010,
17
+ 3030, 3040, 3050, 4000, 8080,
18
+ ];
19
+ const PROBE_TIMEOUT_MS = 200;
20
+ /**
21
+ * Find the base URL of a running wire, or null if none is discoverable.
22
+ *
23
+ * Honours `NWIRE_INSPECT_URL` (trusted blindly — caller asked for it).
24
+ * Otherwise probes the canonical port list and returns the first wire
25
+ * whose `/_nwire/events/recent?limit=1` responds with a JSON array.
26
+ */
27
+ export async function findWireUrl() {
28
+ const override = process.env.NWIRE_INSPECT_URL;
29
+ if (override && override.length > 0) {
30
+ return override.replace(/\/$/, "");
31
+ }
32
+ for (const port of DEFAULT_PROBE_PORTS) {
33
+ if (await isNwireWire(port, PROBE_TIMEOUT_MS)) {
34
+ return `http://127.0.0.1:${port}`;
35
+ }
36
+ }
37
+ return null;
38
+ }
39
+ /**
40
+ * Validate that a port answers `/_nwire/events/recent?limit=1` with a
41
+ * JSON array body. TCP-only checks were too permissive — unrelated
42
+ * services on the same port would slip through.
43
+ */
44
+ function isNwireWire(port, timeoutMs) {
45
+ return new Promise((resolveProbe) => {
46
+ const sock = createConnection({ host: "127.0.0.1", port });
47
+ sock.setTimeout(timeoutMs, () => {
48
+ sock.destroy();
49
+ resolveProbe(false);
50
+ });
51
+ sock.once("error", () => resolveProbe(false));
52
+ sock.once("connect", () => {
53
+ sock.destroy();
54
+ const req = httpRequest({
55
+ host: "127.0.0.1",
56
+ port,
57
+ path: "/_nwire/events/recent?limit=1",
58
+ method: "GET",
59
+ }, (res) => {
60
+ if (res.statusCode !== 200) {
61
+ res.resume();
62
+ return resolveProbe(false);
63
+ }
64
+ let buf = "";
65
+ res.setEncoding("utf8");
66
+ res.on("data", (c) => {
67
+ buf += c;
68
+ if (buf.length > 1024)
69
+ res.destroy();
70
+ });
71
+ res.on("end", () => resolveProbe(buf.trimStart().startsWith("[")));
72
+ });
73
+ req.setTimeout(timeoutMs, () => {
74
+ req.destroy();
75
+ resolveProbe(false);
76
+ });
77
+ req.once("error", () => resolveProbe(false));
78
+ req.end();
79
+ });
80
+ });
81
+ }
82
+ //# sourceMappingURL=wire-discovery.js.map