agent-dag 1.33.55 → 1.33.56

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.
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
6
  <title>agents-deck</title>
7
7
  <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ctext y='84' font-size='84'%3E%E2%97%89%3C/text%3E%3C/svg%3E" />
8
- <script type="module" crossorigin src="/assets/index-DRBr38dw.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-CwR-2lK9.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="/assets/index-CF2yG9oG.css">
10
10
  </head>
11
11
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-dag",
3
- "version": "1.33.55",
3
+ "version": "1.33.56",
4
4
  "description": "Live deck of Claude Code and Codex agents — watch parallel subagents fork, call tools, and return on one calm canvas. Also available as npx ccdeck and npx agent-dag.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -126,8 +126,23 @@ export const tryNext = (err) =>
126
126
  Boolean(err) && (err.code === "ENOENT" || err.code === "EACCES" ||
127
127
  err.code === "EINVAL" || err.code === "UNKNOWN");
128
128
 
129
+ // The three lines cmd.exe prints when it cannot find what it was asked to run,
130
+ // anchored to a whole line each. First the two-line pair for a bare name, then
131
+ // the one it uses when a directory in an explicit path does not exist.
132
+ const CMD_UNKNOWN = /^'(.+)' is not recognized as an internal or external command,?$/i;
133
+ const CMD_UNKNOWN_TAIL = /^operable program or batch file\.?$/i;
134
+ const CMD_NO_PATH = /^the system cannot find the (?:path|file) specified\.?$/i;
135
+
136
+ // cmd.exe echoes the command token exactly as it was given, so an exact match
137
+ // is what we expect; the comparison is only case- and quote-insensitive because
138
+ // Windows paths are.
139
+ const sameCommand = (quoted, name) => {
140
+ const norm = (s) => String(s).trim().replace(/^"+|"+$/g, "").toLowerCase();
141
+ return norm(quoted) === norm(name);
142
+ };
143
+
129
144
  /**
130
- * cmd.exe's way of saying ENOENT.
145
+ * cmd.exe's way of saying ENOENT — and only cmd.exe's.
131
146
  *
132
147
  * A .cmd or .bat candidate is launched THROUGH cmd.exe, and cmd.exe exists — so
133
148
  * a missing tool is not a spawn error at all. It is a healthy shell exiting 1
@@ -140,11 +155,39 @@ export const tryNext = (err) =>
140
155
  * second line — on its own, meaningless — in front of the user. Reported from
141
156
  * Windows on 2026-08-14: the accounts panel said only "operable program or
142
157
  * batch file." when sharing an account.
158
+ *
159
+ * The fussiness is about the other direction, which is worse. "The system
160
+ * cannot find the file specified" is Windows' generic text for ENOENT, and it
161
+ * appears INSIDE the output of tools that ran perfectly well — cswap is a
162
+ * Python CLI, and a FileNotFoundError traceback ends in that exact sentence.
163
+ * Believed there, it threw the real error away, told the user the tool was not
164
+ * on PATH when PATH was fine, and — the dangerous half — sent the candidate
165
+ * loop on to re-run the entire command, which for `cswap remove 3` means asking
166
+ * to delete an account a second time.
167
+ *
168
+ * So the output has to be cmd.exe's message and nothing else: cmd.exe prints it
169
+ * INSTEAD of running anything, so any other line, or any prefix on the line,
170
+ * means something ran and this is its report. And when cmd.exe names the
171
+ * command it could not find, that name must be the candidate we asked for — a
172
+ * tool that shells out itself can forward the message about some other command.
173
+ *
174
+ * `name` is the candidate spelling that produced the output. Callers that only
175
+ * have the text (failureText) omit it and get the shape rules alone.
143
176
  */
144
- export const looksMissing = (text) =>
145
- /is not recognized as an internal or external command/i.test(String(text ?? "")) ||
146
- /operable program or batch file/i.test(String(text ?? "")) ||
147
- /The system cannot find the (?:path|file) specified/i.test(String(text ?? ""));
177
+ export function looksMissing(text, name = "") {
178
+ const lines = String(text ?? "").split(/\r?\n/).map(l => l.trim()).filter(Boolean);
179
+ if (!lines.length) return false;
180
+ for (const line of lines) {
181
+ const named = CMD_UNKNOWN.exec(line);
182
+ if (named) {
183
+ if (name && !sameCommand(named[1], name)) return false;
184
+ continue;
185
+ }
186
+ if (CMD_UNKNOWN_TAIL.test(line) || CMD_NO_PATH.test(line)) continue;
187
+ return false;
188
+ }
189
+ return true;
190
+ }
148
191
 
149
192
  /**
150
193
  * Run a command and collect its output. Never rejects, and never throws —
@@ -175,8 +218,12 @@ export function run(cmd, args, { timeout = 20_000, maxBuffer = 4 << 20 } = {}) {
175
218
  const done = (err, stdout, stderr) => {
176
219
  clearTimeout(timer);
177
220
  // cmd.exe's "is not recognized" counts as "not this spelling" too, and
178
- // it arrives as a normal non-zero exit rather than a spawn error.
179
- const missing = Boolean(err) && looksMissing(`${stderr ?? ""}\n${stdout ?? ""}`);
221
+ // it arrives as a normal non-zero exit rather than a spawn error. Only
222
+ // a batch candidate goes through a shell, so only there can the output
223
+ // be a shell's verdict rather than the tool's own words — spawned
224
+ // directly, a missing file is a plain ENOENT and anything printed came
225
+ // from a tool that ran.
226
+ const missing = Boolean(err) && tree && looksMissing(`${stderr ?? ""}\n${stdout ?? ""}`, raw);
180
227
  if (err && (tryNext(err) || missing) && i + 1 < tries.length) return attempt(i + 1);
181
228
  if (!err) resolved.set(cmd, raw);
182
229
  resolve({
@@ -317,8 +364,11 @@ export function runInteractive(cmd, args, { timeout = 300_000, maxOutput = 256 <
317
364
  proc.on("close", (code) => {
318
365
  if (stale()) return;
319
366
  // Same cmd.exe case as in `run`: exit 1 with "is not recognized" means
320
- // this spelling does not exist, not that the tool failed.
321
- if (code !== 0 && looksMissing(`${stderr}\n${stdout}`)) {
367
+ // this spelling does not exist, not that the tool failed. Restricted to a
368
+ // batch candidate, which is the only kind launched through a shell, and
369
+ // to output that is cmd.exe's message alone — everything below re-runs
370
+ // the whole command, and these commands remove accounts.
371
+ if (code !== 0 && isBatch(raw) && looksMissing(`${stderr}\n${stdout}`, raw)) {
322
372
  if (i + 1 < tries.length) {
323
373
  stdout = ""; stderr = ""; pending = "";
324
374
  child = null;