@indigoai-us/hq-cli 5.345.135 → 5.345.137
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 +44 -0
- package/assets/scaffold/core/scripts/worktree-gc.sh +98 -8
- package/dist/command-catalog.generated.d.ts +17 -6
- package/dist/command-catalog.generated.js +23 -7
- package/dist/commands/core-checkpoint-native.d.ts +10 -0
- package/dist/commands/core-checkpoint-native.js +60 -15
- package/dist/commands/daemon.js +3 -1
- package/dist/commands/integrations-core.d.ts +22 -0
- package/dist/commands/integrations-core.js +25 -0
- package/dist/commands/integrations-manage.d.ts +24 -1
- package/dist/commands/integrations-manage.js +92 -17
- package/dist/commands/integrations.d.ts +11 -0
- package/dist/commands/integrations.js +100 -19
- package/dist/commands/lanes-loop.d.ts +9 -0
- package/dist/commands/lanes-loop.js +9 -1
- package/dist/commands/lanes.js +69 -54
- package/dist/commands/meetings.d.ts +2 -0
- package/dist/commands/meetings.js +57 -14
- package/dist/commands/mesh.d.ts +19 -1
- package/dist/commands/mesh.js +158 -70
- package/dist/commands/reindex.js +80 -5
- package/dist/commands/storage-offload.d.ts +93 -28
- package/dist/commands/storage-offload.js +290 -34
- package/dist/commands/storage.js +60 -7
- package/dist/hq-cloud-sync-runner/bin/sync-runner.js +2550 -1748
- package/dist/hq-cloud-sync-runner/package.json +1 -1
- package/dist/index.js +4 -0
- package/dist/lib/capacity/route.js +24 -0
- package/dist/lib/core-utils/session-company.d.ts +11 -0
- package/dist/lib/core-utils/session-company.js +34 -0
- package/dist/lib/core-utils/worktree-resume.js +44 -4
- package/dist/lib/core-utils/worktree-session-owner.d.ts +27 -0
- package/dist/lib/core-utils/worktree-session-owner.js +157 -0
- package/dist/lib/daemon/catalog.d.ts +2 -0
- package/dist/lib/daemon/catalog.js +3 -3
- package/dist/lib/daemon/hq-child-bootstrap-stage.d.ts +5 -0
- package/dist/lib/daemon/hq-child-bootstrap-stage.js +3 -11
- package/dist/lib/daemon/server.js +10 -1
- package/dist/lib/daemon/supervisor.js +6 -0
- package/dist/lib/daemon/sync-runner-bundle.d.ts +5 -0
- package/dist/lib/daemon/sync-runner-bundle.js +4 -12
- package/dist/lib/doctor/checks/claude-wiring.js +2 -1
- package/dist/lib/doctor/fix/apply.d.ts +20 -0
- package/dist/lib/doctor/fix/apply.js +63 -3
- package/dist/lib/doctor/fix/remediation.d.ts +1 -1
- package/dist/lib/doctor/fix/remediation.js +16 -0
- package/dist/lib/doctor/shared.d.ts +11 -1
- package/dist/lib/doctor/shared.js +16 -2
- package/dist/lib/flag-registry-worker-source.generated.d.ts +1 -1
- package/dist/lib/flag-registry-worker-source.generated.js +1 -1
- package/dist/lib/lanes/monitor.js +14 -2
- package/dist/lib/mesh/live/daemon/install.d.ts +12 -0
- package/dist/lib/mesh/live/daemon/install.js +22 -0
- package/dist/lib/mesh/live/flush.js +106 -3
- package/dist/lib/mesh/live/session-events-client.d.ts +9 -0
- package/dist/lib/mesh/live/session-events-client.js +15 -0
- package/dist/lib/work-context/state.d.ts +10 -0
- package/dist/lib/work-context/state.js +5 -0
- package/dist/utils/fsync-directory.d.ts +17 -0
- package/dist/utils/fsync-directory.js +27 -0
- package/dist/utils/windows-hide-default.d.ts +42 -0
- package/dist/utils/windows-hide-default.js +98 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,50 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.345.137] — 2026-10-15
|
|
6
|
+
- The bundled sync engine now uses hq-cloud 6.18.76. On Windows, the hq-core update and rescue lay down native links after the overlay, turn git symlink stubs from Windows clones into real links, and upload symlinks with the correct target. Sync merges `board.json` by entry id instead of replacing it, merges append-only `.jsonl` conflicts instead of parking them, keeps any `.hqcloud` placeholder on pull, and no longer syncs live-meeting runtime state or server sync manifests.
|
|
7
|
+
|
|
8
|
+
## [5.345.136] — 2026-10-15
|
|
9
|
+
- The work-mesh daemon no longer dead-letters events for sessions another actor already reports (HTTP 403 `SESSION_OWNER_MISMATCH`, for example a local bot's own turns or checkpoint helper sessions). It drops them with a receipt in `dropped-receipts.jsonl`, stops posting that session while it stays bound to the same company, and `hq doctor` no longer marks the daemon unhealthy for them.
|
|
10
|
+
|
|
11
|
+
- `hq storage offload --current [paths...]` now offloads idle working files. A file under `companies/<slug>/` is replaced with a placeholder that points at the object HQ sync already stored in that company's vault; nothing is uploaded, and you must be an active member of the company. A company file sync has not stored yet (new, edited since the last sync, or changed in the vault) is skipped, and the run lists those files with "run hq sync first". Any other file points at its personal-vault copy when sync stored this exact version, and is uploaded to your personal vault otherwise. `--idle-days 0` includes files edited today, and `--dry-run` lists each file with its size, target vault, sync state and action.
|
|
12
|
+
- Current-file offload turns on only when the installed sync engine exports `CLOUD_PLACEHOLDER_HOLDS_ANY_SIBLING`, so sync never deletes or downloads over a `.hqcloud` placeholder. With an older `@indigoai-us/hq-cloud` it reports "update HQ sync" and changes nothing.
|
|
13
|
+
- `hq storage offload` without `--current` now handles only old versions in history and no longer reports on current files.
|
|
14
|
+
- Windows: the `ai.getindigo.hq-daemon` Scheduled Task now runs the hq-cli JS entry under node instead of the npm `hq.cmd` shim, so the daemon starts at logon. Re-run `hq daemon install` to rewrite an existing task.
|
|
15
|
+
- Windows: the daemon now stages its hq child bootstrap and the packaged sync runner. Directory fsync, which Windows refuses with EPERM, is skipped on win32, so the updater, search-index, lanes-reconcile and housekeeping services no longer fall back to running `node hq.cmd` and crashing.
|
|
16
|
+
- Windows: `hq doctor` no longer reports hook scripts and the Codex and Grok adapters as not executable. Windows has no execute bit, so the check now passes any present script there.
|
|
17
|
+
- Windows: an `hq` started in the background (no terminal, as the desktop app, sync runner and hooks start it) now starts its child processes with hidden console windows, so they no longer flash on screen every minute or two.
|
|
18
|
+
- `hq core checkpoint --backend auto` finds `claude.exe` and `grok.exe` on Windows, and after it finds no allowed backend it stops re-probing for six hours, or until PATH changes, instead of spawning version probes and printing "no allowed backend" on every checkpoint.
|
|
19
|
+
- `hq mesh story` no longer stops at `Unknown story` when the story is in the project's local prd.json but not yet on the Board. It registers the project from that PRD (the same merge `hq mesh project ensure` does) and retries once. `--no-ensure` prints the ensure command instead.
|
|
20
|
+
- `hq doctor --fix` now repairs a bot startup agent that points at a missing hq binary (`bots.<name>.daemon-binary`), the same way `hq bot daemon install <name>` does, so hq-checkup stops reporting safety checks off after the fix. It only points a bot at an installed hq, never at a worktree build.
|
|
21
|
+
- hqd: when a policy check, journal append or presence call names no company and the cwd is not linked to one, hqd now uses the session's own bind in its `meta.yaml` (`company_slug`). A company bot at the HQ root can read its own company's files again, and the scope guard still limits it to that one company. The bind is ignored unless that company folder exists, and it never overrides an explicit company or a cwd link.
|
|
22
|
+
- `hq integrations call` accepts tool arguments from a file with `--args-file <path>` or from stdin with `--args -`, so JSON that contains apostrophes or parentheses no longer has to pass through shell quoting. `--args` and `--args-file` together is an error, and invalid JSON in a file names the file.
|
|
23
|
+
- `hq integrations audit` pages through the full activity feed instead of stopping at the latest 50 events, so `--limit` above 50 returns that many rows. A new `--since <ISO timestamp>` stops at older events, and `--provider` keeps paging until the limit is met. Against an older server it shows the latest 50 events and says so.
|
|
24
|
+
- `hq meetings transcript`, `notes`, `get` and `set-company` resolve every short id `hq meetings list` prints. The lookup now follows the meeting list's pages instead of reading only the first one, which the server caps at 100 meetings and can end early when it runs short of time.
|
|
25
|
+
- `hq integrations approve` no longer reports "the call ran" when the approved call failed at the provider. It prints the provider's error and exits 1, prints "the call ran" only for a resumed or completed call, and shows any other status as is. `--json` output is unchanged apart from the exit code.
|
|
26
|
+
- `hq lanes monitor-check` now counts a live `hq lanes watch` process when the CLI
|
|
27
|
+
is installed through pnpm, where the process line runs node on the hq-cli
|
|
28
|
+
package entrypoint instead of an `hq` bin path.
|
|
29
|
+
- `hq lanes admit` on a queued loop lane now starts the lane's loop controller, the
|
|
30
|
+
same way background admission does, instead of running the brief once with no
|
|
31
|
+
controller. Later phase envelopes no longer sit unprocessed.
|
|
32
|
+
- An interrupt that HQ issues itself no longer records `operator_interrupt`. A loop
|
|
33
|
+
lane interrupted to recover a dead loop controller now shows
|
|
34
|
+
`loop_controller_recovery`; a stall restart shows `loop_stall_restart` and a
|
|
35
|
+
phase deadline shows `loop_phase_deadline`. Every interrupt now adds a stop-history
|
|
36
|
+
entry; an operator interrupt without `--reason` records the calling session or lane.
|
|
37
|
+
- `hq lanes cancel <lane-id>` is now an alias of `hq lanes stop`.
|
|
38
|
+
- `hq lanes create --placement local` no longer fails with `lane_placement_disabled`
|
|
39
|
+
when Outpost load balancing is off. Local placement is the default there, so the
|
|
40
|
+
flag is accepted as a no-op. Other placements still need the gate.
|
|
41
|
+
- `--account auto` and `hq capacity route` no longer tell you to log in to an account
|
|
42
|
+
that simply has no usage reading yet. With fresh capacity data it is skipped as
|
|
43
|
+
"no usage reading for this account yet" (fix: `hq capacity doctor`); with the
|
|
44
|
+
collector off or stale data it is ranked with unknown usage.
|
|
45
|
+
- `hq reindex` no longer removes a worktree under `workspace/worktrees/<repo>/<name>` that a session is still using. It keeps a worktree when a live process has its cwd inside it, when a Claude Code or Codex transcript from the last 7 days recorded a cwd inside it, when it has uncommitted or untracked changes, or when its branch has no upstream or commits not on a remote. It prints `reindex: kept stale worktree <path> (<reason>)` for each one and no longer passes `--force` to `git worktree remove`.
|
|
46
|
+
- `hq core worktree-gc` applies the same session-ownership skips (`live-session`, `recent-session`) as `core/scripts/worktree-gc.sh`, and counts a recent commit or checkout in the worktree as activity.
|
|
47
|
+
- Keep running Claude Remote Control account pools alive when a daemon restart temporarily cannot verify their HQ Secrets. A confirmed missing secret still stops its pool.
|
|
48
|
+
|
|
5
49
|
## [5.345.135] — 2026-10-15
|
|
6
50
|
- `hq fingerprint self` prints the current session's HQ fingerprint, and `hq fingerprint <value>` resolves a fingerprint to its local session record. The commit-msg hook calls `hq fingerprint self --json --offline`, which stays offline and always exits 0.
|
|
7
51
|
- Worker lanes get their own `HQ_SESSION_ID`, `HQ_LANE_ID`, `HQ_PARENT_SESSION_ID` and `HQ_COMPANY` with every inherited session id removed; lane records carry a `fingerprint` field shown by `lanes status --json` and `lanes show`; handoff commits add an `HQ-Fingerprint` trailer when a value exists. The handoff lookup is read-only and the offline builder never loads network modules (US-005).
|
|
@@ -20,8 +20,14 @@
|
|
|
20
20
|
# is already merged into the HQ root's
|
|
21
21
|
# local `main`. Fetch is best-effort; a
|
|
22
22
|
# failed fetch makes the worktree UNSAFE.
|
|
23
|
-
# 4. Not in use — no
|
|
24
|
-
#
|
|
23
|
+
# 4. Not in use — no live process has its cwd inside it,
|
|
24
|
+
# no Claude Code / Codex session transcript
|
|
25
|
+
# written in the last 7 days (env
|
|
26
|
+
# HQ_WORKTREE_SESSION_WINDOW_HOURS) recorded
|
|
27
|
+
# a cwd inside it, no session / lock file
|
|
28
|
+
# references its path, and neither the
|
|
29
|
+
# directory nor its HEAD / HEAD reflog
|
|
30
|
+
# changed in the last 2h.
|
|
25
31
|
#
|
|
26
32
|
# Removal mechanics are the gentle git verbs only — never `rm -rf`, never
|
|
27
33
|
# `--force`, never `branch -D`:
|
|
@@ -64,7 +70,7 @@ while [[ $# -gt 0 ]]; do
|
|
|
64
70
|
--gated) GATED=1; shift ;;
|
|
65
71
|
--json) JSON=1; shift ;;
|
|
66
72
|
-h|--help)
|
|
67
|
-
sed -n '2,
|
|
73
|
+
sed -n '2,51p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
|
|
68
74
|
exit 0 ;;
|
|
69
75
|
*) echo "worktree-gc: unknown arg: $1" >&2; exit 2 ;;
|
|
70
76
|
esac
|
|
@@ -126,6 +132,76 @@ session_references() {
|
|
|
126
132
|
return 1
|
|
127
133
|
}
|
|
128
134
|
|
|
135
|
+
# ---------------------------- session liveness ------------------------------
|
|
136
|
+
# A worktree is owned by a live session when a running process has its cwd
|
|
137
|
+
# inside it (lsof on macOS, /proc on Linux), or when a Claude Code / Codex
|
|
138
|
+
# session transcript written within the session window recorded a cwd inside
|
|
139
|
+
# it. Agent shells do not keep a process parked in the worktree between tool
|
|
140
|
+
# calls, so the transcript cwd is what covers an idle but open session. Both
|
|
141
|
+
# lists are gathered once per run. With no lsof and no /proc the process probe
|
|
142
|
+
# yields nothing; the transcript, reference, and activity guards still apply.
|
|
143
|
+
SESSION_WINDOW_HOURS="${HQ_WORKTREE_SESSION_WINDOW_HOURS:-168}"
|
|
144
|
+
case "$SESSION_WINDOW_HOURS" in ''|*[!0-9]*) SESSION_WINDOW_HOURS=168 ;; esac
|
|
145
|
+
LIVE_PROC_CWDS=""
|
|
146
|
+
SESSION_CWDS=""
|
|
147
|
+
collect_session_cwds() {
|
|
148
|
+
local _p d dirs
|
|
149
|
+
LIVE_PROC_CWDS="$(mktemp "${TMPDIR:-/tmp}/hq-wt-proc.XXXXXX" 2>/dev/null)" || LIVE_PROC_CWDS=""
|
|
150
|
+
SESSION_CWDS="$(mktemp "${TMPDIR:-/tmp}/hq-wt-sess.XXXXXX" 2>/dev/null)" || SESSION_CWDS=""
|
|
151
|
+
if [ -n "$LIVE_PROC_CWDS" ]; then
|
|
152
|
+
if [ -e /proc/self/cwd ]; then
|
|
153
|
+
for _p in /proc/[0-9]*; do readlink "$_p/cwd" 2>/dev/null || true; done > "$LIVE_PROC_CWDS"
|
|
154
|
+
elif command -v lsof >/dev/null 2>&1; then
|
|
155
|
+
{ lsof -w -n -P -d cwd -Fn 2>/dev/null || true; } | sed -n 's/^n//p' > "$LIVE_PROC_CWDS"
|
|
156
|
+
fi
|
|
157
|
+
fi
|
|
158
|
+
if [ -n "$SESSION_CWDS" ]; then
|
|
159
|
+
dirs="${HQ_SESSION_TRANSCRIPT_DIRS:-$HOME/.claude/projects:$HOME/.codex/sessions}"
|
|
160
|
+
printf '%s\n' "$dirs" | tr ':' '\n' | while IFS= read -r d; do
|
|
161
|
+
[ -n "$d" ] && [ -d "$d" ] || continue
|
|
162
|
+
{ find "$d" -type f -name '*.jsonl' -mmin "-$((SESSION_WINDOW_HOURS * 60))" -print0 2>/dev/null \
|
|
163
|
+
| xargs -0 grep -h -o '"cwd": *"[^"]*"' /dev/null 2>/dev/null || true; } \
|
|
164
|
+
| sed 's/^"cwd": *"//; s/"$//'
|
|
165
|
+
done | sort -u > "$SESSION_CWDS"
|
|
166
|
+
fi
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
# path_in_list <list-file> <path> [<alt-path>] — true when a listed cwd is the
|
|
170
|
+
# path or lies inside it.
|
|
171
|
+
path_in_list() {
|
|
172
|
+
local f="$1" a="${2:-}" b="${3:-}"
|
|
173
|
+
[ -n "$f" ] && [ -s "$f" ] || return 1
|
|
174
|
+
awk -v a="$a" -v b="$b" '
|
|
175
|
+
function hit(p, w) { return w != "" && (p == w || index(p, w "/") == 1) }
|
|
176
|
+
hit($0, a) || hit($0, b) { found = 1; exit }
|
|
177
|
+
END { exit found ? 0 : 1 }' "$f"
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
# session_owner_reason <wt> [<alt>] — prints live-session or recent-session and
|
|
181
|
+
# succeeds when a session owns the worktree.
|
|
182
|
+
session_owner_reason() {
|
|
183
|
+
if path_in_list "$LIVE_PROC_CWDS" "$1" "${2:-}"; then echo "live-session"; return 0; fi
|
|
184
|
+
if path_in_list "$SESSION_CWDS" "$1" "${2:-}"; then echo "recent-session"; return 0; fi
|
|
185
|
+
return 1
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
# worktree_activity_epoch <wt> — newest mtime of the worktree directory and its
|
|
189
|
+
# per-worktree HEAD and HEAD reflog (commits, checkouts, resets). The index is
|
|
190
|
+
# left out because `git status` may rewrite it on its own.
|
|
191
|
+
worktree_activity_epoch() {
|
|
192
|
+
local wt="$1" gd newest f m
|
|
193
|
+
newest=$(path_mtime "$wt")
|
|
194
|
+
gd=$(git -C "$wt" rev-parse --path-format=absolute --git-dir 2>/dev/null || echo "")
|
|
195
|
+
if [[ -n "$gd" ]]; then
|
|
196
|
+
for f in "$gd/HEAD" "$gd/logs/HEAD"; do
|
|
197
|
+
[[ -e "$f" ]] || continue
|
|
198
|
+
m=$(path_mtime "$f")
|
|
199
|
+
if (( m > newest )); then newest=$m; fi
|
|
200
|
+
done
|
|
201
|
+
fi
|
|
202
|
+
echo "$newest"
|
|
203
|
+
}
|
|
204
|
+
|
|
129
205
|
# Optional timeout wrapper for network fetches (macOS has no `timeout`).
|
|
130
206
|
_TIMEOUT_BIN=""
|
|
131
207
|
if command -v timeout >/dev/null 2>&1; then _TIMEOUT_BIN="timeout"
|
|
@@ -160,7 +236,7 @@ SELF_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
|
|
|
160
236
|
# --------------------------- results / counters -----------------------------
|
|
161
237
|
REC_NDJSON="$(mktemp 2>/dev/null || echo "/tmp/worktree-gc-rec.$$")"
|
|
162
238
|
: > "$REC_NDJSON"
|
|
163
|
-
cleanup_rec() { rm -f "$REC_NDJSON"; }
|
|
239
|
+
cleanup_rec() { rm -f "$REC_NDJSON" "${LIVE_PROC_CWDS:-}" "${SESSION_CWDS:-}"; }
|
|
164
240
|
trap cleanup_rec EXIT
|
|
165
241
|
|
|
166
242
|
examined=0
|
|
@@ -169,6 +245,9 @@ would=0
|
|
|
169
245
|
reclaimed_kb=0
|
|
170
246
|
declare -i skip_total=0
|
|
171
247
|
|
|
248
|
+
if [[ "${#CANDIDATES[@]}" -gt 0 ]]; then collect_session_cwds; fi
|
|
249
|
+
HQ_ROOT_PHYS="$(cd "$HQ_ROOT" && pwd -P)"
|
|
250
|
+
|
|
172
251
|
# wt_git <git-args...> — run git against the current worktree's main repo, adding
|
|
173
252
|
# the HQ_ALLOW_HQ_ROOT_GIT escape hatch only for HQ-root-class worktrees (whose
|
|
174
253
|
# mutations touch the HQ root object store and are otherwise hook-blocked).
|
|
@@ -225,8 +304,19 @@ for wt in "${CANDIDATES[@]:-}"; do
|
|
|
225
304
|
branch=$(git -C "$wt" symbolic-ref --short HEAD 2>/dev/null || echo "")
|
|
226
305
|
head_sha=$(git -C "$wt" rev-parse HEAD 2>/dev/null || echo "")
|
|
227
306
|
|
|
307
|
+
# ---- guard 4a: owned by a live session ----
|
|
308
|
+
# Checked before anything else inspects the tree. wt is the physical path;
|
|
309
|
+
# the spelling under the given HQ_ROOT is matched too.
|
|
310
|
+
wt_alt="$wt"
|
|
311
|
+
[[ "$wt" == "$HQ_ROOT_PHYS"/* ]] && wt_alt="$HQ_ROOT/${wt#"$HQ_ROOT_PHYS"/}"
|
|
312
|
+
if owner=$(session_owner_reason "$wt" "$wt_alt"); then
|
|
313
|
+
skip "$wt" "$main_repo" "$branch" "$is_hq_root" "" "" "$owner"; continue
|
|
314
|
+
fi
|
|
315
|
+
|
|
228
316
|
# ---- guard 1: clean working tree ----
|
|
229
|
-
|
|
317
|
+
# --no-optional-locks keeps the scan from rewriting the index. Untracked files
|
|
318
|
+
# count as dirty even when status.showUntrackedFiles=no.
|
|
319
|
+
porcelain=$(git --no-optional-locks -C "$wt" status --porcelain --untracked-files=all 2>/dev/null || echo "__ERR__")
|
|
230
320
|
if [[ "$porcelain" == "__ERR__" ]]; then
|
|
231
321
|
skip "$wt" "$main_repo" "$branch" "$is_hq_root" "" "" "git-error"; continue
|
|
232
322
|
fi
|
|
@@ -234,13 +324,13 @@ for wt in "${CANDIDATES[@]:-}"; do
|
|
|
234
324
|
skip "$wt" "$main_repo" "$branch" "$is_hq_root" "" "" "dirty"; continue
|
|
235
325
|
fi
|
|
236
326
|
|
|
237
|
-
# ---- guard
|
|
327
|
+
# ---- guard 4b: recent activity (in-use heuristic) ----
|
|
238
328
|
wt_mtime=$(path_mtime "$wt")
|
|
239
|
-
if ((
|
|
329
|
+
if (( $(worktree_activity_epoch "$wt") > RECENT_EPOCH )); then
|
|
240
330
|
skip "$wt" "$main_repo" "$branch" "$is_hq_root" "" "" "recent-activity"; continue
|
|
241
331
|
fi
|
|
242
332
|
|
|
243
|
-
# ---- guard
|
|
333
|
+
# ---- guard 4c: active-session reference ----
|
|
244
334
|
if session_references "$wt"; then
|
|
245
335
|
skip "$wt" "$main_repo" "$branch" "$is_hq_root" "" "" "active-session"; continue
|
|
246
336
|
fi
|
|
@@ -3245,7 +3245,7 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
3245
3245
|
readonly subcommands: readonly [];
|
|
3246
3246
|
}, {
|
|
3247
3247
|
readonly name: "offload";
|
|
3248
|
-
readonly description: "Move big files to
|
|
3248
|
+
readonly description: "Move big files to HQ cloud storage and leave links behind (old versions in history; with --current, idle working files to the vault that owns them)";
|
|
3249
3249
|
readonly aliases: readonly [];
|
|
3250
3250
|
readonly hidden: false;
|
|
3251
3251
|
readonly helpOption: true;
|
|
@@ -3267,8 +3267,8 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
3267
3267
|
readonly description: "Only old versions in local autosave history";
|
|
3268
3268
|
readonly hidden: false;
|
|
3269
3269
|
}, {
|
|
3270
|
-
readonly flags: "--current";
|
|
3271
|
-
readonly description: "
|
|
3270
|
+
readonly flags: "--current [paths...]";
|
|
3271
|
+
readonly description: "Idle working files instead of history; optional HQ paths limit it to those files or folders";
|
|
3272
3272
|
readonly hidden: false;
|
|
3273
3273
|
}, {
|
|
3274
3274
|
readonly flags: "--dry-run";
|
|
@@ -4813,9 +4813,12 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
4813
4813
|
readonly hidden: false;
|
|
4814
4814
|
}, {
|
|
4815
4815
|
readonly flags: "--args <json>";
|
|
4816
|
-
readonly description: "Tool arguments as JSON";
|
|
4816
|
+
readonly description: "Tool arguments as a JSON object (default {}); use - to read the JSON from stdin";
|
|
4817
|
+
readonly hidden: false;
|
|
4818
|
+
}, {
|
|
4819
|
+
readonly flags: "--args-file <path>";
|
|
4820
|
+
readonly description: "Read tool arguments from a JSON file (use - for stdin); avoids shell quoting of apostrophes and parentheses";
|
|
4817
4821
|
readonly hidden: false;
|
|
4818
|
-
readonly defaultValue: "{}";
|
|
4819
4822
|
}, {
|
|
4820
4823
|
readonly flags: "--idempotency-key <key>";
|
|
4821
4824
|
readonly description: "Stable key so retries execute at most once (default: generated)";
|
|
@@ -5492,6 +5495,10 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
5492
5495
|
readonly description: "How many events to show";
|
|
5493
5496
|
readonly hidden: false;
|
|
5494
5497
|
readonly defaultValue: "20";
|
|
5498
|
+
}, {
|
|
5499
|
+
readonly flags: "--since <iso>";
|
|
5500
|
+
readonly description: "Only events at or after this ISO timestamp, e.g. 2026-10-01T00:00:00Z";
|
|
5501
|
+
readonly hidden: false;
|
|
5495
5502
|
}, {
|
|
5496
5503
|
readonly flags: "--json";
|
|
5497
5504
|
readonly description: "Machine-readable output";
|
|
@@ -10581,6 +10588,10 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
10581
10588
|
readonly flags: "--force";
|
|
10582
10589
|
readonly description: "Override another holder's assignment";
|
|
10583
10590
|
readonly hidden: false;
|
|
10591
|
+
}, {
|
|
10592
|
+
readonly flags: "--no-ensure";
|
|
10593
|
+
readonly description: "Do not register the project from its local prd.json when the story is not on the Board";
|
|
10594
|
+
readonly hidden: false;
|
|
10584
10595
|
}, {
|
|
10585
10596
|
readonly flags: "--company <slug|uid>";
|
|
10586
10597
|
readonly description: "Company slug or cloud uid";
|
|
@@ -14985,7 +14996,7 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
14985
14996
|
}, {
|
|
14986
14997
|
readonly name: "stop";
|
|
14987
14998
|
readonly description: "Cancel a queued lane or kill a running one (terminal; use interrupt to keep it resumable)";
|
|
14988
|
-
readonly aliases: readonly [];
|
|
14999
|
+
readonly aliases: readonly ["cancel"];
|
|
14989
15000
|
readonly hidden: false;
|
|
14990
15001
|
readonly helpOption: true;
|
|
14991
15002
|
readonly helpCommand: false;
|
|
@@ -3912,7 +3912,7 @@ export const COMMAND_CATALOG = [
|
|
|
3912
3912
|
},
|
|
3913
3913
|
{
|
|
3914
3914
|
"name": "offload",
|
|
3915
|
-
"description": "Move big files to
|
|
3915
|
+
"description": "Move big files to HQ cloud storage and leave links behind (old versions in history; with --current, idle working files to the vault that owns them)",
|
|
3916
3916
|
"aliases": [],
|
|
3917
3917
|
"hidden": false,
|
|
3918
3918
|
"helpOption": true,
|
|
@@ -3938,8 +3938,8 @@ export const COMMAND_CATALOG = [
|
|
|
3938
3938
|
"hidden": false
|
|
3939
3939
|
},
|
|
3940
3940
|
{
|
|
3941
|
-
"flags": "--current",
|
|
3942
|
-
"description": "
|
|
3941
|
+
"flags": "--current [paths...]",
|
|
3942
|
+
"description": "Idle working files instead of history; optional HQ paths limit it to those files or folders",
|
|
3943
3943
|
"hidden": false
|
|
3944
3944
|
},
|
|
3945
3945
|
{
|
|
@@ -5820,9 +5820,13 @@ export const COMMAND_CATALOG = [
|
|
|
5820
5820
|
},
|
|
5821
5821
|
{
|
|
5822
5822
|
"flags": "--args <json>",
|
|
5823
|
-
"description": "Tool arguments as JSON",
|
|
5824
|
-
"hidden": false
|
|
5825
|
-
|
|
5823
|
+
"description": "Tool arguments as a JSON object (default {}); use - to read the JSON from stdin",
|
|
5824
|
+
"hidden": false
|
|
5825
|
+
},
|
|
5826
|
+
{
|
|
5827
|
+
"flags": "--args-file <path>",
|
|
5828
|
+
"description": "Read tool arguments from a JSON file (use - for stdin); avoids shell quoting of apostrophes and parentheses",
|
|
5829
|
+
"hidden": false
|
|
5826
5830
|
},
|
|
5827
5831
|
{
|
|
5828
5832
|
"flags": "--idempotency-key <key>",
|
|
@@ -6667,6 +6671,11 @@ export const COMMAND_CATALOG = [
|
|
|
6667
6671
|
"hidden": false,
|
|
6668
6672
|
"defaultValue": "20"
|
|
6669
6673
|
},
|
|
6674
|
+
{
|
|
6675
|
+
"flags": "--since <iso>",
|
|
6676
|
+
"description": "Only events at or after this ISO timestamp, e.g. 2026-10-01T00:00:00Z",
|
|
6677
|
+
"hidden": false
|
|
6678
|
+
},
|
|
6670
6679
|
{
|
|
6671
6680
|
"flags": "--json",
|
|
6672
6681
|
"description": "Machine-readable output",
|
|
@@ -12821,6 +12830,11 @@ export const COMMAND_CATALOG = [
|
|
|
12821
12830
|
"description": "Override another holder's assignment",
|
|
12822
12831
|
"hidden": false
|
|
12823
12832
|
},
|
|
12833
|
+
{
|
|
12834
|
+
"flags": "--no-ensure",
|
|
12835
|
+
"description": "Do not register the project from its local prd.json when the story is not on the Board",
|
|
12836
|
+
"hidden": false
|
|
12837
|
+
},
|
|
12824
12838
|
{
|
|
12825
12839
|
"flags": "--company <slug|uid>",
|
|
12826
12840
|
"description": "Company slug or cloud uid",
|
|
@@ -18231,7 +18245,9 @@ export const COMMAND_CATALOG = [
|
|
|
18231
18245
|
{
|
|
18232
18246
|
"name": "stop",
|
|
18233
18247
|
"description": "Cancel a queued lane or kill a running one (terminal; use interrupt to keep it resumable)",
|
|
18234
|
-
"aliases": [
|
|
18248
|
+
"aliases": [
|
|
18249
|
+
"cancel"
|
|
18250
|
+
],
|
|
18235
18251
|
"hidden": false,
|
|
18236
18252
|
"helpOption": true,
|
|
18237
18253
|
"helpCommand": false,
|
|
@@ -13,6 +13,14 @@ import { Command } from "commander";
|
|
|
13
13
|
* transient failure (a DNS blip, a brief outage) must not pin the choice.
|
|
14
14
|
*/
|
|
15
15
|
export declare const CRASHED_RUN_MAX_AGE_MS: number;
|
|
16
|
+
/**
|
|
17
|
+
* After `--backend auto` finds no allowed backend, later checkpoints skip the
|
|
18
|
+
* PATH scan and `--version` probes for this long, unless PATH changes. Each
|
|
19
|
+
* probe is a child process, and on Windows each one could open a console
|
|
20
|
+
* window; a Stop hook that checkpoints every turn re-ran them every minute or
|
|
21
|
+
* two, all day, to reach the same answer (feedback_351a40d5).
|
|
22
|
+
*/
|
|
23
|
+
export declare const NO_BACKEND_RETRY_MS: number;
|
|
16
24
|
/**
|
|
17
25
|
* Wall-clock cap on one sibling run. The supervisor kills the sibling and its
|
|
18
26
|
* descendants at this point. It stays under the lock TTL so a capped run
|
|
@@ -89,6 +97,8 @@ export declare function autoBackendPreference(preferGrok?: boolean): SpawnableBa
|
|
|
89
97
|
* registry; a flag change takes effect from the checkpoint after the next plan.
|
|
90
98
|
*/
|
|
91
99
|
export declare function companyPrefersGrok(liveRoot: string, companySlug: string | null | undefined): Promise<boolean>;
|
|
100
|
+
/** True while a recent "no allowed backend" result still holds for this PATH. */
|
|
101
|
+
export declare function noBackendRecentlyFound(liveRoot: string, now?: number): boolean;
|
|
92
102
|
export declare function siblingArgs(backend: SpawnableBackend, prompt: string): string[];
|
|
93
103
|
/**
|
|
94
104
|
* Supervisor for a detached sibling on POSIX. It forks the backend, records
|
|
@@ -8,11 +8,12 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import * as fs from "node:fs";
|
|
10
10
|
import * as path from "node:path";
|
|
11
|
-
import { randomUUID } from "node:crypto";
|
|
11
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
12
12
|
import { execFileSync, spawn } from "node:child_process";
|
|
13
13
|
import { homedir } from "node:os";
|
|
14
14
|
import { toHqStateWriteError } from "../utils/hq-state-write-error.js";
|
|
15
15
|
import { checkpointStampWriteErrorFor, isCheckpointStampWriteError, } from "../utils/checkpoint-stamp-write-error.js";
|
|
16
|
+
import { isExecutableOnPath } from "../utils/resolve-executable.js";
|
|
16
17
|
import { describeCoreCommand } from "./core-command-catalog.js";
|
|
17
18
|
const DEFAULT_TRIGGER = "stop-gate";
|
|
18
19
|
const BACKENDS = new Set(["auto", "claude", "codex", "grok", "none"]);
|
|
@@ -73,6 +74,14 @@ const BACKEND_SETTING = "HQ_CHECKPOINT_BACKEND";
|
|
|
73
74
|
export const CRASHED_RUN_MAX_AGE_MS = 6 * 60 * 60 * 1000;
|
|
74
75
|
/** A backend that cannot answer `--version` this fast is treated as broken. */
|
|
75
76
|
const BACKEND_PROBE_TIMEOUT_MS = 10_000;
|
|
77
|
+
/**
|
|
78
|
+
* After `--backend auto` finds no allowed backend, later checkpoints skip the
|
|
79
|
+
* PATH scan and `--version` probes for this long, unless PATH changes. Each
|
|
80
|
+
* probe is a child process, and on Windows each one could open a console
|
|
81
|
+
* window; a Stop hook that checkpoints every turn re-ran them every minute or
|
|
82
|
+
* two, all day, to reach the same answer (feedback_351a40d5).
|
|
83
|
+
*/
|
|
84
|
+
export const NO_BACKEND_RETRY_MS = 6 * 60 * 60 * 1000;
|
|
76
85
|
/** A sibling still holding the lock after this long is treated as abandoned. */
|
|
77
86
|
const SIBLING_LOCK_TTL_MS = 60 * 60 * 1000;
|
|
78
87
|
/**
|
|
@@ -392,6 +401,7 @@ function readGitState(repoDir) {
|
|
|
392
401
|
const read = (args) => execFileSync("git", ["-C", repoDir, ...args], {
|
|
393
402
|
encoding: "utf8",
|
|
394
403
|
stdio: ["ignore", "pipe", "ignore"],
|
|
404
|
+
windowsHide: true,
|
|
395
405
|
}).trim();
|
|
396
406
|
if (read(["rev-parse", "--is-inside-work-tree"]) !== "true")
|
|
397
407
|
return null;
|
|
@@ -415,7 +425,7 @@ function readGitState(repoDir) {
|
|
|
415
425
|
}
|
|
416
426
|
function commitsSince(repoDir, initialCommit) {
|
|
417
427
|
try {
|
|
418
|
-
const output = execFileSync("git", ["-C", repoDir, "log", "--no-decorate", "--pretty=format:%h: %s", `${initialCommit}..HEAD`], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
428
|
+
const output = execFileSync("git", ["-C", repoDir, "log", "--no-decorate", "--pretty=format:%h: %s", `${initialCommit}..HEAD`], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], windowsHide: true }).trim();
|
|
419
429
|
return output ? output.split("\n") : [];
|
|
420
430
|
}
|
|
421
431
|
catch {
|
|
@@ -590,17 +600,8 @@ function backendOnPath(name) {
|
|
|
590
600
|
const pathValue = process.env.PATH;
|
|
591
601
|
if (!pathValue)
|
|
592
602
|
return false;
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
return false;
|
|
596
|
-
try {
|
|
597
|
-
fs.accessSync(path.join(directory, name), fs.constants.X_OK);
|
|
598
|
-
return true;
|
|
599
|
-
}
|
|
600
|
-
catch {
|
|
601
|
-
return false;
|
|
602
|
-
}
|
|
603
|
-
});
|
|
603
|
+
// PATHEXT-aware on Windows, where the binary is claude.exe, not claude.
|
|
604
|
+
return isExecutableOnPath(name, pathValue);
|
|
604
605
|
}
|
|
605
606
|
/**
|
|
606
607
|
* An installed binary is not necessarily a working one: a partial install, a
|
|
@@ -613,6 +614,7 @@ function backendResponds(name) {
|
|
|
613
614
|
execFileSync(name, ["--version"], {
|
|
614
615
|
timeout: BACKEND_PROBE_TIMEOUT_MS,
|
|
615
616
|
stdio: "ignore",
|
|
617
|
+
windowsHide: true,
|
|
616
618
|
});
|
|
617
619
|
return true;
|
|
618
620
|
}
|
|
@@ -835,7 +837,43 @@ function backendSetting(liveRoot) {
|
|
|
835
837
|
const lookup = settingsEnvValue(liveRoot, BACKEND_SETTING);
|
|
836
838
|
return lookup.kind === "value" ? lookup.value.trim().toLowerCase() || undefined : undefined;
|
|
837
839
|
}
|
|
838
|
-
|
|
840
|
+
function noBackendMarkerPath(liveRoot) {
|
|
841
|
+
return path.join(liveRoot, "workspace", "orchestrator", "hook-state", "checkpoint-no-backend.json");
|
|
842
|
+
}
|
|
843
|
+
function pathFingerprint() {
|
|
844
|
+
return createHash("sha256").update(process.env.PATH ?? "").digest("hex").slice(0, 16);
|
|
845
|
+
}
|
|
846
|
+
/** True while a recent "no allowed backend" result still holds for this PATH. */
|
|
847
|
+
export function noBackendRecentlyFound(liveRoot, now = Date.now()) {
|
|
848
|
+
try {
|
|
849
|
+
const marker = JSON.parse(fs.readFileSync(noBackendMarkerPath(liveRoot), "utf8"));
|
|
850
|
+
if (typeof marker.at !== "number" || marker.path !== pathFingerprint())
|
|
851
|
+
return false;
|
|
852
|
+
return now - marker.at >= 0 && now - marker.at < NO_BACKEND_RETRY_MS;
|
|
853
|
+
}
|
|
854
|
+
catch {
|
|
855
|
+
return false;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
function recordNoBackend(liveRoot, now = Date.now()) {
|
|
859
|
+
try {
|
|
860
|
+
const marker = noBackendMarkerPath(liveRoot);
|
|
861
|
+
fs.mkdirSync(path.dirname(marker), { recursive: true });
|
|
862
|
+
fs.writeFileSync(marker, `${JSON.stringify({ at: now, path: pathFingerprint() })}\n`);
|
|
863
|
+
}
|
|
864
|
+
catch {
|
|
865
|
+
// Best effort: without the marker the next checkpoint probes again.
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
function clearNoBackend(liveRoot) {
|
|
869
|
+
try {
|
|
870
|
+
fs.rmSync(noBackendMarkerPath(liveRoot), { force: true });
|
|
871
|
+
}
|
|
872
|
+
catch {
|
|
873
|
+
// Best effort.
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
async function resolveBackend(requested, liveRoot, company, options = {}) {
|
|
839
877
|
let value = assertKnownBackend(requested);
|
|
840
878
|
if (value === "auto") {
|
|
841
879
|
const setting = backendSetting(liveRoot);
|
|
@@ -849,12 +887,19 @@ async function resolveBackend(requested, liveRoot, company) {
|
|
|
849
887
|
// An explicitly named backend is honoured as given; only `auto` shops around.
|
|
850
888
|
if (value !== "auto")
|
|
851
889
|
return value;
|
|
890
|
+
// A recent identical miss: skip the probes rather than spawn them again.
|
|
891
|
+
if (noBackendRecentlyFound(liveRoot))
|
|
892
|
+
return "none";
|
|
852
893
|
const available = autoBackendPreference(await companyPrefersGrok(liveRoot, company)).filter((name) => backendOnPath(name) && backendResponds(name));
|
|
853
894
|
if (available.length === 0) {
|
|
854
895
|
printError("checkpoint: sibling skipped: no allowed backend available (claude, grok); " +
|
|
855
896
|
`codex runs only with --backend codex or ${BACKEND_SETTING}=codex`);
|
|
897
|
+
if (!options.dryRun)
|
|
898
|
+
recordNoBackend(liveRoot);
|
|
856
899
|
return "none";
|
|
857
900
|
}
|
|
901
|
+
if (!options.dryRun)
|
|
902
|
+
clearNoBackend(liveRoot);
|
|
858
903
|
const crashed = crashedBackends(path.join(liveRoot, "workspace", "checkpoints", "sibling"));
|
|
859
904
|
// If every candidate failed last time, still try the preferred one: a stale
|
|
860
905
|
// failure is a weaker signal than running no maintenance sibling at all.
|
|
@@ -1255,7 +1300,7 @@ async function runCheckpoint(options, command, group, preResolvedHqRoot) {
|
|
|
1255
1300
|
const siblingOptOut = options.agent === false || !siblingEnabledBySetting(liveRoot);
|
|
1256
1301
|
// Validated even when nothing will spawn, so `--backend bogus` is still an error.
|
|
1257
1302
|
const requestedBackend = assertKnownBackend(options.backend);
|
|
1258
|
-
const backend = siblingOptOut ? "none" : await resolveBackend(requestedBackend, liveRoot, input.company);
|
|
1303
|
+
const backend = siblingOptOut ? "none" : await resolveBackend(requestedBackend, liveRoot, input.company, { dryRun: options.dryRun });
|
|
1259
1304
|
const now = new Date();
|
|
1260
1305
|
const threadId = `T-${formatTimestamp(now)}-auto-${summarySlug(input.summary)}`;
|
|
1261
1306
|
const threadPath = path.join(liveRoot, "workspace", "threads", `${threadId}.json`);
|
package/dist/commands/daemon.js
CHANGED
|
@@ -112,7 +112,9 @@ async function loadDefaults(over) {
|
|
|
112
112
|
err: over.err ?? ((l) => console.error(l)),
|
|
113
113
|
run,
|
|
114
114
|
nodeBinary: over.nodeBinary ?? mesh.resolveAbsoluteNodeBinary(),
|
|
115
|
-
|
|
115
|
+
// `node <hqBinary>` runs the task and every child service, so on Windows
|
|
116
|
+
// this must be the package's JS entry rather than the npm hq.cmd shim.
|
|
117
|
+
hqBinary: over.hqBinary ?? mesh.nodeRunnableHqEntry(mesh.resolveHqBinary(), platform),
|
|
116
118
|
uid: over.uid ?? (typeof process.getuid === "function" ? process.getuid() : 0),
|
|
117
119
|
isRoot: over.isRoot ?? (typeof process.getuid === "function" && process.getuid() === 0),
|
|
118
120
|
fileExists: over.fileExists ?? fs.existsSync,
|
|
@@ -224,6 +224,28 @@ export declare function toolPrefixForProvider(provider: string): string;
|
|
|
224
224
|
* projection over it.
|
|
225
225
|
*/
|
|
226
226
|
export declare function fetchAdminSurface(token: string, companyUid: string): Promise<AdminSurface>;
|
|
227
|
+
/** One page of the company's integration audit feed. */
|
|
228
|
+
export interface AuditPage {
|
|
229
|
+
/**
|
|
230
|
+
* True when the server answered the paged audit view. False means an older
|
|
231
|
+
* server ignored `view=audit` and returned the full admin surface, whose
|
|
232
|
+
* audit feed is capped at the latest 50 events and has no cursor.
|
|
233
|
+
*/
|
|
234
|
+
paged: boolean;
|
|
235
|
+
audit: AdminAuditEvent[];
|
|
236
|
+
nextCursor?: string;
|
|
237
|
+
}
|
|
238
|
+
/** Most rows the paged audit view returns per request. */
|
|
239
|
+
export declare const AUDIT_PAGE_MAX = 100;
|
|
240
|
+
/**
|
|
241
|
+
* Read one page of the audit feed via the admin route's paged view
|
|
242
|
+
* (`view=audit&limit=&cursor=`), newest first. Falls back to the surface's
|
|
243
|
+
* capped feed when the server does not know the view.
|
|
244
|
+
*/
|
|
245
|
+
export declare function fetchAuditPage(token: string, companyUid: string, opts: {
|
|
246
|
+
limit: number;
|
|
247
|
+
cursor?: string;
|
|
248
|
+
}): Promise<AuditPage>;
|
|
227
249
|
export declare function fetchConnections(token: string, companyUid: string): Promise<AdminConnection[]>;
|
|
228
250
|
/**
|
|
229
251
|
* One app the caller can use right now through the HQ gateway. Field names
|
|
@@ -352,6 +352,31 @@ export async function fetchAdminSurface(token, companyUid) {
|
|
|
352
352
|
audit: data.audit ?? [],
|
|
353
353
|
};
|
|
354
354
|
}
|
|
355
|
+
/** Most rows the paged audit view returns per request. */
|
|
356
|
+
export const AUDIT_PAGE_MAX = 100;
|
|
357
|
+
/**
|
|
358
|
+
* Read one page of the audit feed via the admin route's paged view
|
|
359
|
+
* (`view=audit&limit=&cursor=`), newest first. Falls back to the surface's
|
|
360
|
+
* capped feed when the server does not know the view.
|
|
361
|
+
*/
|
|
362
|
+
export async function fetchAuditPage(token, companyUid, opts) {
|
|
363
|
+
const limit = Math.max(1, Math.min(AUDIT_PAGE_MAX, Math.floor(opts.limit)));
|
|
364
|
+
const query = { companyUid, view: "audit", limit: String(limit) };
|
|
365
|
+
if (opts.cursor)
|
|
366
|
+
query.cursor = opts.cursor;
|
|
367
|
+
const res = await vaultApiFetch({ token, path: "/v1/integrations/admin", query });
|
|
368
|
+
if (!res.ok)
|
|
369
|
+
await raiseForResponse(res, "Failed to read integration activity");
|
|
370
|
+
const data = (await res.json());
|
|
371
|
+
const audit = Array.isArray(data.audit) ? data.audit : [];
|
|
372
|
+
if (data.view !== "audit")
|
|
373
|
+
return { paged: false, audit };
|
|
374
|
+
return {
|
|
375
|
+
paged: true,
|
|
376
|
+
audit,
|
|
377
|
+
...(typeof data.nextCursor === "string" && data.nextCursor ? { nextCursor: data.nextCursor } : {}),
|
|
378
|
+
};
|
|
379
|
+
}
|
|
355
380
|
export async function fetchConnections(token, companyUid) {
|
|
356
381
|
return (await fetchAdminSurface(token, companyUid)).connections;
|
|
357
382
|
}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* and layered under the connection's write policy.
|
|
14
14
|
*/
|
|
15
15
|
import { Command } from "commander";
|
|
16
|
-
import { type GranteeType, type Permission, type WriteAllowlistGrant } from "./integrations-core.js";
|
|
16
|
+
import { type AdminAuditEvent, type GranteeType, type Permission, type WriteAllowlistGrant } from "./integrations-core.js";
|
|
17
17
|
interface Principal {
|
|
18
18
|
granteeType: GranteeType;
|
|
19
19
|
granteeId?: string;
|
|
@@ -46,5 +46,28 @@ export declare function mergeGrant(current: WriteAllowlistGrant[], toolName: str
|
|
|
46
46
|
* dropped entirely rather than persisted as an empty rule.
|
|
47
47
|
*/
|
|
48
48
|
export declare function removeGrant(current: WriteAllowlistGrant[], toolName: string, principal: Principal): WriteAllowlistGrant[] | null;
|
|
49
|
+
/** Upper bound on audit pages one `hq integrations audit` run reads. */
|
|
50
|
+
export declare const AUDIT_MAX_PAGES = 200;
|
|
51
|
+
export interface CollectedAudit {
|
|
52
|
+
rows: AdminAuditEvent[];
|
|
53
|
+
/** False when the server only returned its capped surface feed. */
|
|
54
|
+
paged: boolean;
|
|
55
|
+
/** Rows in the surface feed (fallback path only), before filtering. */
|
|
56
|
+
feedSize: number;
|
|
57
|
+
/** More matching rows may exist past what was collected. */
|
|
58
|
+
more: boolean;
|
|
59
|
+
pageCapHit: boolean;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Page the audit feed (newest first) until `limit` rows match, the cursor runs
|
|
63
|
+
* out, a row is older than `sinceMs`, or AUDIT_MAX_PAGES pages were read.
|
|
64
|
+
* `provider` is matched client-side on the bare slug, so the qualified
|
|
65
|
+
* `factory:linear` form copied from JSON output matches bare stored rows.
|
|
66
|
+
*/
|
|
67
|
+
export declare function collectAuditRows(token: string, companyUid: string, opts: {
|
|
68
|
+
limit: number;
|
|
69
|
+
provider?: string;
|
|
70
|
+
sinceMs?: number;
|
|
71
|
+
}): Promise<CollectedAudit>;
|
|
49
72
|
export {};
|
|
50
73
|
//# sourceMappingURL=integrations-manage.d.ts.map
|