agent-dag 1.33.80 → 1.33.82
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/bin/agent-dag.js +57 -16
- package/dist/web/assets/{index-5_r9cSQl.js → index-CK4AFqll.js} +8 -8
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
- package/src/server/index.mjs +124 -11
- package/src/server/npx.mjs +133 -0
- package/src/server/self-update.mjs +95 -3
package/bin/agent-dag.js
CHANGED
|
@@ -26,17 +26,16 @@ import { spawn } from "node:child_process";
|
|
|
26
26
|
import { connect } from "node:net";
|
|
27
27
|
import { dirname, join } from "node:path";
|
|
28
28
|
import { fileURLToPath } from "node:url";
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
29
|
+
import { npxFailureHint, npxFailureSummary, npxLaunch } from "../src/server/npx.mjs";
|
|
30
|
+
import {
|
|
31
|
+
bareSpecName, clearRestartFailure, installedVersion, npxRestartSpec, recordRestartFailure,
|
|
32
|
+
} from "../src/server/self-update.mjs";
|
|
31
33
|
import { dieOfSignal, workerExitAction } from "../src/server/supervisor.mjs";
|
|
32
34
|
|
|
33
35
|
const BIN_DIR = dirname(fileURLToPath(import.meta.url));
|
|
34
36
|
const WORKER = join(BIN_DIR, "deck.js");
|
|
35
37
|
const PKG_ROOT = dirname(BIN_DIR);
|
|
36
38
|
|
|
37
|
-
// npx is a .cmd shim on Windows, which spawn can only launch through cmd.exe —
|
|
38
|
-
// spawnSpec is how, with the arguments quoted rather than pasted together.
|
|
39
|
-
const NPX = process.platform === "win32" ? "npx.cmd" : "npx";
|
|
40
39
|
const VERSION = installedVersion(PKG_ROOT) ?? "?";
|
|
41
40
|
|
|
42
41
|
// The port the worker actually bound, which is not necessarily the one it was
|
|
@@ -147,17 +146,47 @@ function launchNpx() {
|
|
|
147
146
|
// the deck talking over itself.
|
|
148
147
|
args.push("--no-open");
|
|
149
148
|
|
|
149
|
+
// A retry answers for itself: whatever the last attempt left on disk is about
|
|
150
|
+
// to be replaced by this attempt's outcome, and leaving it there would keep
|
|
151
|
+
// the browser showing an old failure over a running fetch.
|
|
152
|
+
const pkgName = bareSpecName(spec) ?? "agents-deck";
|
|
153
|
+
clearRestartFailure(pkgName);
|
|
154
|
+
|
|
150
155
|
process.stdout.write(`\n ↻ fetching ${spec}…\n`);
|
|
151
|
-
//
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
//
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
|
|
158
|
-
|
|
156
|
+
// npxLaunch prefers npm's own npx-cli.js next to this Node binary, which
|
|
157
|
+
// needs no PATH lookup and no batch shim; the PATH shim is the fallback and
|
|
158
|
+
// still goes through cmd.exe with each argument quoted.
|
|
159
|
+
//
|
|
160
|
+
// Not `shell: true` on either path. Everything after the spec is the user's
|
|
161
|
+
// own argv, and Node would paste it into one command line unquoted:
|
|
162
|
+
// `--workspace C:\Users\John Smith\proj` would reach the new deck as two
|
|
163
|
+
// arguments, the second one silently dropped by its parser — an upgraded deck
|
|
164
|
+
// watching the wrong directory.
|
|
165
|
+
const { file, args: argv, opts } = npxLaunch(args);
|
|
166
|
+
// stderr is piped rather than inherited so a crash can be summarised instead
|
|
167
|
+
// of dumped: npm's MODULE_NOT_FOUND stack, with its `requireStack` and its
|
|
168
|
+
// caret line, is not something a user of a DAG dashboard can act on. stdout
|
|
169
|
+
// stays inherited — the new deck's banner, colours and URL line are the whole
|
|
170
|
+
// point of the supervisor staying out of the way.
|
|
171
|
+
const started = spawn(file, argv, { stdio: ["inherit", "inherit", "pipe"], ...opts });
|
|
159
172
|
child = started;
|
|
160
173
|
|
|
174
|
+
// Held, not discarded: the moment the replacement is serving, everything it
|
|
175
|
+
// wrote goes to the terminal and every later byte passes straight through.
|
|
176
|
+
// Until then it is only evidence for a failure that may not happen.
|
|
177
|
+
let tail = "";
|
|
178
|
+
let teeing = false;
|
|
179
|
+
const tee = () => {
|
|
180
|
+
if (teeing) return;
|
|
181
|
+
teeing = true;
|
|
182
|
+
if (tail) process.stderr.write(tail);
|
|
183
|
+
};
|
|
184
|
+
started.stderr?.on("data", (d) => {
|
|
185
|
+
const s = String(d);
|
|
186
|
+
if (teeing) { process.stderr.write(s); return; }
|
|
187
|
+
tail = (tail + s).slice(-8000);
|
|
188
|
+
});
|
|
189
|
+
|
|
161
190
|
// Whether the replacement ever got as far as serving. An npx that cannot
|
|
162
191
|
// resolve exits in seconds having bound nothing; a deck the user stops with
|
|
163
192
|
// Ctrl+C exits non-zero too, and only this tells the two apart.
|
|
@@ -166,7 +195,7 @@ function launchNpx() {
|
|
|
166
195
|
if (boundPort == null || served) return;
|
|
167
196
|
const sock = connect({ port: boundPort, host: "127.0.0.1" });
|
|
168
197
|
sock.setTimeout(1000);
|
|
169
|
-
sock.on("connect", () => { served = true; sock.destroy(); });
|
|
198
|
+
sock.on("connect", () => { served = true; tee(); sock.destroy(); });
|
|
170
199
|
sock.on("timeout", () => sock.destroy());
|
|
171
200
|
sock.on("error", () => { /* not up yet */ });
|
|
172
201
|
}, 1000);
|
|
@@ -176,7 +205,19 @@ function launchNpx() {
|
|
|
176
205
|
clearInterval(probe);
|
|
177
206
|
child = null;
|
|
178
207
|
if (stopping || served) return; // the user stopped it, or it ran and ended
|
|
208
|
+
const summary = npxFailureSummary(tail);
|
|
209
|
+
const hint = npxFailureHint(tail);
|
|
179
210
|
console.error(`agents-deck: ${why} — staying on v${VERSION}`);
|
|
211
|
+
if (summary) console.error(` ${summary}`);
|
|
212
|
+
if (hint) console.error(` ${hint}`);
|
|
213
|
+
// Left for the worker about to be launched: it is the only way the browser
|
|
214
|
+
// learns this happened at all. See recordRestartFailure.
|
|
215
|
+
recordRestartFailure({
|
|
216
|
+
name: pkgName,
|
|
217
|
+
command: `npx -y ${spec}`,
|
|
218
|
+
error: [summary ?? why, hint].filter(Boolean).join(" — "),
|
|
219
|
+
version: VERSION === "?" ? null : VERSION,
|
|
220
|
+
});
|
|
180
221
|
restarts++;
|
|
181
222
|
launch(true);
|
|
182
223
|
};
|
|
@@ -189,9 +230,9 @@ function launchNpx() {
|
|
|
189
230
|
else process.exit(code ?? 0);
|
|
190
231
|
return;
|
|
191
232
|
}
|
|
192
|
-
giveUp(
|
|
233
|
+
giveUp(`npx ${spec} exited ${code ?? signal}`);
|
|
193
234
|
});
|
|
194
|
-
started.on("error", (err) => giveUp(`could not run
|
|
235
|
+
started.on("error", (err) => giveUp(`could not run npx: ${err.message}`));
|
|
195
236
|
}
|
|
196
237
|
|
|
197
238
|
// Ctrl+C already reaches the child directly — it shares this process group — so
|