@coworker-jp/aidr 0.1.298 → 0.1.299

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coworker-jp/aidr",
3
- "version": "0.1.298",
3
+ "version": "0.1.299",
4
4
  "description": "AIDR setup CLI - installs ai-scanner hooks for 19+ AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
package/src/merge.mjs CHANGED
@@ -150,6 +150,73 @@ export function buildCursorHooksJson(scope = "project", absHome = "") {
150
150
  }
151
151
 
152
152
  // Windsurf hooks.json — flat entries, one command per event (no matcher).
153
+ //
154
+ // NO `timeout` HERE, AND THAT IS DELIBERATE (#1896). Windsurf's hook entry
155
+ // schema has no such field: the documented entry keys are `command`,
156
+ // `powershell`, `show_output` and `working_directory`, and the page says
157
+ // nothing about any time limit at all (docs.windsurf.com/windsurf/cascade/hooks
158
+ // → docs.devin.ai/desktop/cascade/hooks, read 2026-08-25). The other four
159
+ // agents each declare one because their schemas accept it; Windsurf cannot,
160
+ // so this is a DECLARED inequality rather than an oversight — which is the
161
+ // whole reason this comment exists. Emitting an unknown key would not buy a
162
+ // cap and could be rejected by a future schema validation.
163
+ //
164
+ // What bounds a Windsurf hook instead: the in-script `run_scanner` cap
165
+ // (`AI_SCANNER_HOOK_TIMEOUT`, default 30s) plus `ensure_scanner.sh`'s
166
+ // `flock -w 120`. Those are OUR caps, so they hold on every editor — but
167
+ // there is no second, editor-side net here the way there is elsewhere.
168
+ // `tests/merge-hooks-json.test.mjs` pins both directions of that statement.
169
+ //
170
+ // NO `powershell` HERE EITHER, AND THAT IS A DECLARED INEQUALITY (#1907) —
171
+ // it is NOT a claim that Windows is covered.
172
+ //
173
+ // What the vendor actually documents (docs.windsurf.com/windsurf/cascade/hooks
174
+ // → docs.devin.ai/desktop/cascade/hooks, read 2026-08-25, quoted verbatim):
175
+ //
176
+ // command "The shell command to execute on **macOS/Linux** (run via
177
+ // `bash -c`). At least one of `command` or `powershell` must
178
+ // be specified."
179
+ // powershell "Optional. The command to execute on **Windows** (run via
180
+ // `powershell -Command`). If omitted on Windows, `command` is
181
+ // used as a fallback."
182
+ //
183
+ // and its cross-platform table, for the row we are actually in:
184
+ // | Windows | `command` ✓ | `powershell` ✗ | Falls back to `command` via `powershell -Command` |
185
+ //
186
+ // So on Windows the entry is NOT skipped. #1907 was filed on the assumption
187
+ // that it would be; the primary source says otherwise, and this comment is
188
+ // the corrected record. The real question is narrower and still open: what
189
+ // gets handed to `powershell -Command` is THE PATH OF A POSIX SHELL SCRIPT
190
+ // (`.windsurf/coworker-ai/pre_command.sh`), and every hook adapter this
191
+ // product ships — all five agents, 42 files — is a `.sh`. There is no
192
+ // Windows-native adapter anywhere in this tree to point a `powershell` key at.
193
+ //
194
+ // Why the outcome is invisible either way: the same page's exit-code table
195
+ // says `0` proceeds, `2` blocks, and "Any other | Error | Action proceeds
196
+ // normally". A fallback that fires and cannot run therefore produces the same
197
+ // user-visible result as a scan that ran and found nothing. Fail-open is
198
+ // deliberate, and it is exactly what hides this.
199
+ //
200
+ // UNMEASURED: whether that fallback reaches ai-scanner on a real Windows
201
+ // machine. This development host has no Windows machine with Windsurf on it.
202
+ //
203
+ // Two wrong moves, spelled out so nobody makes them from this comment alone:
204
+ // * emit `powershell: "bash <path>.sh"` — plausible, and unverified. If Git
205
+ // Bash is not on PATH it exits non-zero, which the table above turns into
206
+ // "proceeds normally" — the same silent gap, now with a tree that CLAIMS
207
+ // Windows is handled. A bare `bash` on Windows can also resolve to WSL's
208
+ // `C:\Windows\System32\bash.exe`, which sees a different filesystem than
209
+ // the path we wrote.
210
+ // * delete this comment because "the docs say it falls back" — the fallback
211
+ // is documented; the fallback WORKING is not.
212
+ //
213
+ // HOW THIS INEQUALITY RETIRES (so it cannot sit here forever): install
214
+ // Windsurf on a Windows machine, install these hooks, and drive a pre_* hook
215
+ // with an input the scanner MUST block. Judge on the block (exit 2), not on a
216
+ // log line or a telemetry field — fail-open makes "ran and allowed" and
217
+ // "never ran" byte-identical. Then either delete this block (it reaches the
218
+ // scanner) or add the `powershell` value the measurement showed to be right.
219
+ // `tests/merge-hooks-json.test.mjs` pins both directions until that day.
153
220
  export function buildWindsurfHooksJson(scope = "project", absHome = "") {
154
221
  const p = scope === "user" ? `${absHome}/.windsurf/coworker-ai` : ".windsurf/coworker-ai";
155
222
  return {
package/src/templates.mjs CHANGED
@@ -55,6 +55,14 @@ mkdir -p "$BIN_DIR"
55
55
 
56
56
  # macOS has no base \`timeout\` (it's in GNU coreutils). Shim it so hooks work
57
57
  # on fresh Macs without brew install coreutils.
58
+ #
59
+ # NOTE (issue #1879): the last branch — a bare macOS with neither \`timeout\` nor
60
+ # \`gtimeout\` — DROPS the duration and execs directly. On such a machine every
61
+ # \`timeout N "$BIN" ...\` below is unbounded, and the only remaining safety net
62
+ # is the editor's own hook timeout (Claude Code / Codex: 60s, written by
63
+ # merge.mjs; Cursor / Windsurf / Kiro: unverified). Installing coreutils is the
64
+ # documented remedy. The shim is deliberately kept — a hook that refused to run
65
+ # without coreutils would be worse than one that runs unbounded.
58
66
  if ! command -v timeout >/dev/null 2>&1; then
59
67
  if command -v gtimeout >/dev/null 2>&1; then
60
68
  timeout() { gtimeout "$@"; }
@@ -63,6 +71,23 @@ if ! command -v timeout >/dev/null 2>&1; then
63
71
  fi
64
72
  fi
65
73
 
74
+ # Bounds for the two ai-scanner invocations this script makes. Both sit
75
+ # STRICTLY ABOVE the binary's own internal network bound for that subcommand,
76
+ # so the wrapper only ever fires on a genuinely hung binary and never pre-empts
77
+ # a slow-but-working one. (A bound that fires on healthy-but-slow would print
78
+ # the "we did NOT check" message spuriously — a false positive, which is how
79
+ # guards stop being read.)
80
+ #
81
+ # version: load_plan -> /verify, reqwest timeout 10s
82
+ # (docker/scanner/src/main.rs). 20s leaves 10s for process start.
83
+ # update: /verify (10s) THEN the binary download (30s), sequential
84
+ # (docker/scanner/src/main.rs cmd_update) = 40s internal worst case.
85
+ # 60s leaves 20s for hashing and the rename. Killing an update
86
+ # mid-flight is safe: it writes to a temp file in the same directory
87
+ # and only renames over \$BIN once the advertised hash matches.
88
+ VERSION_TIMEOUT=20
89
+ UPDATE_TIMEOUT=60
90
+
66
91
  # Generic throttle: true (0) if stamp missing or older than PULL_INTERVAL
67
92
  should_check_update() {
68
93
  [ ! -f "$PULL_STAMP" ] && return 0
@@ -248,7 +273,7 @@ rm -f "$UNSCANNED_STAMP"
248
273
  # operator can act on; "|| true" turned it into silence.
249
274
  if should_check_update; then
250
275
  want_host=$(printf '%s' "$DOWNLOAD_BASE" | sed -E 's#^[a-z]+://([^/]+).*#\\1#')
251
- have_host=$("$BIN" version 2>/dev/null | sed -n 's#^server: [a-z]*://\\([^/]*\\).*#\\1#p' | head -1)
276
+ have_host=$(timeout "$VERSION_TIMEOUT" "$BIN" version 2>/dev/null | sed -n 's#^server: [a-z]*://\\([^/]*\\).*#\\1#p' | head -1)
252
277
  if [ -n "$have_host" ] && [ -n "$want_host" ] && [ "$have_host" != "$want_host" ]; then
253
278
  echo "ai-scanner: the installed binary talks to $have_host, but this installation provisions from $want_host, so its access key can never be accepted (detection logs are refused and discarded). Re-downloading the matching binary." >&2
254
279
  download_binary "\${DOWNLOAD_BASE}/\${PLATFORM_SLUG}" || \\
@@ -256,7 +281,7 @@ if should_check_update; then
256
281
  elif [ -z "$have_host" ]; then
257
282
  echo "ai-scanner: could not read which license server the installed binary uses, so the environment match was NOT checked. If detection logs are being refused, re-run the installer." >&2
258
283
  fi
259
- "$BIN" update 2>/dev/null || \\
284
+ timeout "$UPDATE_TIMEOUT" "$BIN" update 2>/dev/null || \\
260
285
  echo "ai-scanner: the scanner could not update itself just now. It keeps scanning with the rules it already has; if this repeats, re-run the installer or contact your IT administrator." >&2
261
286
  date +%s > "$PULL_STAMP"
262
287
  fi