@indigoai-us/hq-cli 5.345.114 → 5.345.116

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 (57) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/assets/scaffold/core/scripts/hq-delegate-send.sh +5 -1
  3. package/dist/commands/bot-jobs.d.ts +1 -11
  4. package/dist/commands/bot-jobs.js +13 -34
  5. package/dist/commands/core-audit-log-native.js +1 -1
  6. package/dist/commands/core-checkpoint-native.d.ts +27 -0
  7. package/dist/commands/core-checkpoint-native.js +85 -2
  8. package/dist/commands/core-dm-bind-native.js +1 -1
  9. package/dist/commands/core-git-pack-extension-check-native.js +1 -1
  10. package/dist/commands/core-job-native-utils.d.ts +2 -2
  11. package/dist/commands/core-job-native-utils.js +3 -11
  12. package/dist/commands/core-job-notify-native.js +1 -2
  13. package/dist/commands/core-job-probe-native.d.ts +2 -3
  14. package/dist/commands/core-job-probe-native.js +14 -22
  15. package/dist/commands/core-job-remediate-native.js +1 -2
  16. package/dist/commands/core-job-run-native.js +58 -54
  17. package/dist/commands/core-jobs-native.d.ts +1 -3
  18. package/dist/commands/core-jobs-native.js +16 -271
  19. package/dist/commands/core-jobs-schedule.d.ts +7 -0
  20. package/dist/commands/core-jobs-schedule.js +243 -0
  21. package/dist/commands/core-native-utilities.d.ts +2 -3
  22. package/dist/commands/core-native-utilities.js +2 -19
  23. package/dist/commands/core-pin-native.js +1 -1
  24. package/dist/commands/core-refresh-vault-access-native.js +1 -1
  25. package/dist/commands/core-resolve-company-command.js +2 -17
  26. package/dist/commands/core-work-mesh-bind-trusted-native.js +1 -1
  27. package/dist/commands/core.js +1 -17
  28. package/dist/fast-scaffold.d.ts +1 -1
  29. package/dist/fast-scaffold.js +0 -38
  30. package/dist/lib/daemon/catalog.d.ts +0 -2
  31. package/dist/lib/daemon/catalog.js +3 -6
  32. package/dist/lib/daemon/run.js +0 -7
  33. package/dist/lib/scan-packages/index.js +6 -0
  34. package/dist/lib/workers-registry/index.d.ts +16 -0
  35. package/dist/lib/workers-registry/index.js +1 -1
  36. package/dist/utils/resident-package-preload.js +2 -7
  37. package/package.json +1 -1
  38. package/assets/scaffold/core/scripts/audit-log.sh +0 -384
  39. package/assets/scaffold/core/scripts/git-pack-extension-check.sh +0 -164
  40. package/assets/scaffold/core/scripts/hq-dm-bind.sh +0 -590
  41. package/assets/scaffold/core/scripts/hq-job-notify.sh +0 -389
  42. package/assets/scaffold/core/scripts/hq-job-probe.sh +0 -865
  43. package/assets/scaffold/core/scripts/hq-job-remediate.sh +0 -359
  44. package/assets/scaffold/core/scripts/hq-job-run.sh +0 -1025
  45. package/assets/scaffold/core/scripts/hq-pin.sh +0 -118
  46. package/assets/scaffold/core/scripts/jobs-schedule-parse.sh +0 -310
  47. package/assets/scaffold/core/scripts/jobs-validate.sh +0 -482
  48. package/assets/scaffold/core/scripts/outpost-jobs-reconcile.sh +0 -836
  49. package/assets/scaffold/core/scripts/refresh-vault-access.sh +0 -188
  50. package/assets/scaffold/core/scripts/resolve-company.sh +0 -331
  51. package/assets/scaffold/core/scripts/work-mesh-live-bind-trusted.sh +0 -184
  52. package/dist/commands/core-job-post-exit-assets.d.ts +0 -8
  53. package/dist/commands/core-job-post-exit-assets.js +0 -37
  54. package/dist/commands/core-native-jobs-flag.d.ts +0 -4
  55. package/dist/commands/core-native-jobs-flag.js +0 -7
  56. package/dist/commands/core-native-utilities-flag.d.ts +0 -4
  57. package/dist/commands/core-native-utilities-flag.js +0 -7
@@ -1,188 +0,0 @@
1
- #!/usr/bin/env bash
2
- # hq-core: public
3
- # refresh-vault-access.sh — build/refresh the local vault-access manifest that
4
- # enforce-vault-write-access.sh (PreToolUse hook) reads.
5
- #
6
- # Output: <hqRoot>/.hq/vault-access.json
7
- #
8
- # {
9
- # "version": 1,
10
- # "generatedAt": "2026-01-01T00:00:00Z",
11
- # "companies": {
12
- # "<slug>": {
13
- # "role": "owner|admin|member|guest|unknown",
14
- # "grants": [ { "path": "reports/*", "permission": "read|write" } ]
15
- # }
16
- # }
17
- # }
18
- #
19
- # Sources (best-effort, all via the hq CLI):
20
- # - grants: `hq files shared-with-me` (explicit per-prefix grants)
21
- # - role: `hq members --company <slug> list` matched against the caller's
22
- # email (from `hq whoami`, override with HQ_VAULT_ACCESS_EMAIL)
23
- #
24
- # Fail-open by design: anything this script cannot determine is recorded as
25
- # role "unknown" (the hook does not enforce unknown roles), and a company it
26
- # cannot query at all is left out of the manifest entirely (the hook allows
27
- # companies absent from the manifest). The server-side STS/ACL layer remains
28
- # the authoritative security boundary — this manifest only powers the local
29
- # early-warning block for read-only shares.
30
- #
31
- # Usage:
32
- # bash core/scripts/refresh-vault-access.sh [--company <slug>]... [--root <hqRoot>]
33
- #
34
- # With no --company, refreshes every company that appears in the
35
- # `hq files shared-with-me` cross-company roll-up.
36
-
37
- set -uo pipefail
38
-
39
- log() { printf 'refresh-vault-access: %s\n' "$*" >&2; }
40
-
41
- ROOT=""
42
- SLUGS=()
43
- while [ $# -gt 0 ]; do
44
- case "$1" in
45
- --root)
46
- ROOT="${2:-}"
47
- shift 2 || { log "--root requires a value"; exit 1; }
48
- ;;
49
- --company)
50
- SLUGS[${#SLUGS[@]}]="${2:-}"
51
- shift 2 || { log "--company requires a value"; exit 1; }
52
- ;;
53
- -h|--help)
54
- sed -n '2,33p' "$0" | sed 's/^# \{0,1\}//'
55
- exit 0
56
- ;;
57
- *)
58
- log "unknown argument: $1"
59
- exit 1
60
- ;;
61
- esac
62
- done
63
-
64
- is_hq_root() { [ -n "${1:-}" ] && [ -d "${1%/}/core" ] && [ -d "${1%/}/.claude" ]; }
65
-
66
- if [ -z "$ROOT" ]; then
67
- if is_hq_root "${CLAUDE_PROJECT_DIR:-}"; then
68
- ROOT="$CLAUDE_PROJECT_DIR"
69
- else
70
- dir="$PWD"
71
- while [ -n "$dir" ] && [ "$dir" != "/" ]; do
72
- if is_hq_root "$dir"; then ROOT="$dir"; break; fi
73
- dir="$(dirname "$dir")"
74
- done
75
- fi
76
- fi
77
- if ! is_hq_root "$ROOT"; then
78
- log "could not resolve the HQ root (pass --root <hqRoot>)"
79
- exit 1
80
- fi
81
-
82
- command -v jq >/dev/null 2>&1 || { log "jq is required"; exit 1; }
83
- if ! command -v hq >/dev/null 2>&1; then
84
- log "hq CLI not found — nothing refreshed (the write-access hook stays fail-open without a manifest)"
85
- exit 0
86
- fi
87
-
88
- strip_ansi() { sed -e 's/\x1b\[[0-9;]*m//g'; }
89
-
90
- # ── caller email (for role lookup) ─────────────────────────────────────────
91
- EMAIL="${HQ_VAULT_ACCESS_EMAIL:-}"
92
- if [ -z "$EMAIL" ]; then
93
- EMAIL="$(hq whoami 2>/dev/null | strip_ansi \
94
- | grep -Eo '[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}' \
95
- | grep -v '@outposts\.' | head -1 || true)"
96
- fi
97
- [ -n "$EMAIL" ] || log "could not determine caller email — roles will be 'unknown' (hook will not enforce)"
98
-
99
- # ── explicit grants via shared-with-me ─────────────────────────────────────
100
- # Table columns: COMPANY PATH PERMISSION SOURCE (header + ─── separator).
101
- fetch_grants_tsv() {
102
- # $1 = optional slug filter; prints "slug<TAB>path<TAB>permission" rows.
103
- local slug="${1:-}" out
104
- if [ -n "$slug" ]; then
105
- out="$(hq files shared-with-me --company "$slug" 2>/dev/null | strip_ansi)" || return 1
106
- else
107
- out="$(hq files shared-with-me 2>/dev/null | strip_ansi)" || return 1
108
- fi
109
- # hq-cli >= 5.142 renders a private-folder row as "foo/ (private folder)";
110
- # drop the label so the PATH column stays the verbatim `foo/` pattern.
111
- printf '%s\n' "$out" | awk '
112
- { sub(/ \(private folder\)/, "") }
113
- /^COMPANY[[:space:]]/ { next }
114
- /^─/ { next }
115
- /^[[:space:]]*$/ { next }
116
- /^Nothing is explicitly shared/ { next }
117
- NF >= 3 {
118
- perm = ""
119
- if ($3 == "read" || $3 == "write" || $3 == "admin") perm = $3
120
- else if (NF >= 4 && ($4 == "read" || $4 == "write" || $4 == "admin")) perm = $4
121
- if (perm != "") printf "%s\t%s\t%s\n", $1, $2, perm
122
- }'
123
- }
124
-
125
- # ── role lookup ────────────────────────────────────────────────────────────
126
- fetch_role() {
127
- # $1 = slug; prints role or "unknown". Never fails.
128
- local slug="$1" out role=""
129
- if [ -n "$EMAIL" ]; then
130
- # --company is declared on the parent `members` group, so it goes first.
131
- out="$(hq members --company "$slug" list 2>/dev/null | strip_ansi)" || out=""
132
- if [ -n "$out" ]; then
133
- role="$(printf '%s\n' "$out" | awk -v email="$EMAIL" '$1 == email { print $2; exit }')"
134
- fi
135
- fi
136
- case "$role" in
137
- owner|admin|member|guest) printf '%s' "$role" ;;
138
- *) printf 'unknown' ;;
139
- esac
140
- }
141
-
142
- GRANTS_TSV=""
143
- if [ "${#SLUGS[@]}" -gt 0 ]; then
144
- for slug in "${SLUGS[@]}"; do
145
- rows="$(fetch_grants_tsv "$slug")" || { log "skipping $slug — shared-with-me query failed"; continue; }
146
- GRANTS_TSV="${GRANTS_TSV}${rows}
147
- "
148
- done
149
- else
150
- GRANTS_TSV="$(fetch_grants_tsv)" || { log "shared-with-me roll-up failed — nothing refreshed"; exit 0; }
151
- fi
152
-
153
- # Companies to include = every slug present in the grant rows (plus any
154
- # explicitly requested slugs, so a company with zero explicit grants still
155
- # gets a role entry when asked for by name).
156
- ALL_SLUGS="$(printf '%s\n' "$GRANTS_TSV" | awk -F'\t' 'NF { print $1 }' | sort -u)"
157
- if [ "${#SLUGS[@]}" -gt 0 ]; then
158
- ALL_SLUGS="$(printf '%s\n%s\n' "$ALL_SLUGS" "$(printf '%s\n' "${SLUGS[@]}")" | awk 'NF' | sort -u)"
159
- fi
160
-
161
- if [ -z "$ALL_SLUGS" ]; then
162
- log "no explicit grants found — writing an empty manifest (hook stays fail-open everywhere)"
163
- fi
164
-
165
- COMPANIES_JSON="{}"
166
- while IFS= read -r slug; do
167
- [ -n "$slug" ] || continue
168
- role="$(fetch_role "$slug")"
169
- grants_json="$(printf '%s\n' "$GRANTS_TSV" \
170
- | awk -F'\t' -v s="$slug" '$1 == s { printf "%s\t%s\n", $2, $3 }' \
171
- | jq -Rn '[inputs | split("\t") | select(length == 2) | {path: .[0], permission: .[1]}]')"
172
- COMPANIES_JSON="$(printf '%s' "$COMPANIES_JSON" \
173
- | jq --arg slug "$slug" --arg role "$role" --argjson grants "$grants_json" \
174
- '. + {($slug): {role: $role, grants: $grants}}')"
175
- done <<EOF
176
- $ALL_SLUGS
177
- EOF
178
-
179
- mkdir -p "$ROOT/.hq"
180
- TMP_OUT="$(mktemp "$ROOT/.hq/vault-access.json.XXXXXX")"
181
- jq -n --argjson companies "$COMPANIES_JSON" \
182
- --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
183
- '{version: 1, generatedAt: $ts, companies: $companies}' > "$TMP_OUT"
184
- mv "$TMP_OUT" "$ROOT/.hq/vault-access.json"
185
-
186
- N_COMPANIES="$(printf '%s\n' "$ALL_SLUGS" | awk 'NF' | wc -l | tr -d ' ')"
187
- log "wrote .hq/vault-access.json ($N_COMPANIES companies, identity: ${EMAIL:-unknown})"
188
- exit 0
@@ -1,331 +0,0 @@
1
- #!/usr/bin/env bash
2
- # hq-core: public
3
- # resolve-company.sh — resolve the active company for skills and routing.
4
- #
5
- # Resolution order (first hit wins): explicit manifest slug in the prompt,
6
- # session company_slug, enabled device default, then none. Prompt matching is
7
- # whole-token only; free text never guesses a company.
8
- #
9
- # Output: {"company":"<slug>","source":"prompt|session|device_default|none"}
10
- # Always exits 0 and never writes state.
11
- #
12
- # Folder mode (--path <dir>): ask the repo-to-company registry through
13
- # `$HQ_CLI_BIN resolve-company --path <dir> --json` (HQ_CLI_BIN defaults to
14
- # `hq`). Sources: registry (linked folder), registry_miss (not linked), or
15
- # unavailable (CLI missing, failed, timed out, or returned bad output). A
16
- # registry answer is accepted only when it names a real companies/<slug>
17
- # directory. Folder mode never falls back to prompt, session, or device default.
18
-
19
- set -uo pipefail
20
-
21
- ROOT=""
22
- PROMPT=""
23
- HAVE_PROMPT=0
24
- FOLDER=""
25
- HAVE_FOLDER=0
26
-
27
- while [ $# -gt 0 ]; do
28
- case "$1" in
29
- --root) ROOT="${2:-}"; shift 2 || shift ;;
30
- --prompt) PROMPT="${2:-}"; HAVE_PROMPT=1; shift 2 || shift ;;
31
- --path) FOLDER="${2:-}"; HAVE_FOLDER=1; shift 2 || shift ;;
32
- --help|-h)
33
- sed -n '2,21p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
34
- exit 0
35
- ;;
36
- *) shift ;;
37
- esac
38
- done
39
-
40
- if [ "$HAVE_PROMPT" -eq 0 ] && [ "$HAVE_FOLDER" -eq 0 ] && [ ! -t 0 ]; then
41
- PROMPT="$(cat 2>/dev/null || true)"
42
- fi
43
- : "${PROMPT:=}"
44
-
45
- emit() {
46
- printf '{"company":"%s","source":"%s"}\n' "$1" "$2"
47
- exit 0
48
- }
49
-
50
- if [ -z "$ROOT" ]; then
51
- ROOT="${HQ_ROOT:-${CLAUDE_PROJECT_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && cd .. && pwd)}}"
52
- fi
53
-
54
- # Runs a command with a hard deadline (HQ_RESOLVE_TIMEOUT_MS, default 2000).
55
- run_with_timeout() {
56
- command -v node >/dev/null 2>&1 || return 127
57
- node -e '
58
- const { spawn } = require("child_process");
59
- const command = process.argv[1];
60
- const args = process.argv.slice(2);
61
- const timeoutMs = Number(process.env.HQ_RESOLVE_TIMEOUT_MS) || 2000;
62
- if (!command) process.exitCode = 127;
63
- else {
64
- let child;
65
- try {
66
- child = spawn(command, args, { detached: true, stdio: ["ignore", "pipe", "ignore"] });
67
- } catch {
68
- process.exitCode = 127;
69
- }
70
- if (child) {
71
- let exited = false;
72
- let exitStatus = 1;
73
- let timedOut = false;
74
- let drainExpired = false;
75
- let pendingWrites = 0;
76
- let finished = false;
77
- let drainTimer;
78
- let timeoutKillTimer;
79
- let timeoutFinalTimer;
80
- let deadlineTimer;
81
- const signalGroup = (signal) => {
82
- if (!child.pid) return;
83
- try {
84
- if (process.platform === "win32") child.kill(signal);
85
- else process.kill(-child.pid, signal);
86
- } catch {}
87
- };
88
- const maybeFinish = () => {
89
- if (finished || !exited || pendingWrites !== 0 || (!drainExpired && !child.stdout.readableEnded)) return;
90
- finished = true;
91
- clearTimeout(deadlineTimer);
92
- clearTimeout(drainTimer);
93
- clearTimeout(timeoutKillTimer);
94
- clearTimeout(timeoutFinalTimer);
95
- signalGroup("SIGTERM");
96
- process.exitCode = timedOut ? 124 : exitStatus;
97
- process.exit();
98
- };
99
- const writeChunk = (chunk) => {
100
- pendingWrites += 1;
101
- process.stdout.write(chunk, () => {
102
- pendingWrites -= 1;
103
- maybeFinish();
104
- });
105
- };
106
- child.stdout.on("data", writeChunk);
107
- child.stdout.once("end", () => {
108
- drainExpired = true;
109
- maybeFinish();
110
- });
111
- child.once("error", () => {
112
- exited = true;
113
- exitStatus = 127;
114
- drainExpired = true;
115
- maybeFinish();
116
- });
117
- child.once("exit", (code) => {
118
- exited = true;
119
- exitStatus = typeof code === "number" ? code : 1;
120
- signalGroup("SIGTERM");
121
- if (!child.stdout.readableEnded) {
122
- drainTimer = setTimeout(() => {
123
- child.stdout.pause();
124
- let chunk;
125
- while ((chunk = child.stdout.read()) !== null) writeChunk(chunk);
126
- process.stdout.write("", () => {
127
- drainExpired = true;
128
- maybeFinish();
129
- });
130
- }, 100);
131
- } else {
132
- drainExpired = true;
133
- }
134
- maybeFinish();
135
- });
136
- deadlineTimer = setTimeout(() => {
137
- timedOut = true;
138
- signalGroup("SIGTERM");
139
- timeoutKillTimer = setTimeout(() => signalGroup("SIGKILL"), 100);
140
- timeoutFinalTimer = setTimeout(() => {
141
- if (!exited) {
142
- exited = true;
143
- exitStatus = 124;
144
- child.stdout.destroy();
145
- drainExpired = true;
146
- maybeFinish();
147
- }
148
- }, 1000);
149
- }, timeoutMs);
150
- }
151
- }
152
- ' "$@"
153
- }
154
-
155
- unavailable() {
156
- printf 'resolve-company: WARNING company registry unavailable (%s); binding personal, not guessing a company\n' "$1" >&2
157
- emit "" "unavailable"
158
- }
159
-
160
- if [ "$HAVE_FOLDER" -eq 1 ]; then
161
- [ -n "$FOLDER" ] || unavailable "empty --path"
162
- case "$FOLDER" in
163
- /*|[A-Za-z]:/*|[A-Za-z]:\\*|\\\\*) ;;
164
- *)
165
- caller_cwd="${HQ_CALLER_CWD:-$PWD}"
166
- caller_cwd="$(cd "$caller_cwd" 2>/dev/null && pwd -P)" || unavailable "caller working directory unavailable"
167
- if [ "$FOLDER" = "." ]; then FOLDER="$caller_cwd"; else FOLDER="$caller_cwd/$FOLDER"; fi
168
- ;;
169
- esac
170
- cli="${HQ_CLI_BIN:-hq}"
171
- command -v "$cli" >/dev/null 2>&1 || unavailable "'$cli' not found"
172
- command -v jq >/dev/null 2>&1 || unavailable "jq not found"
173
- status=0
174
- if command -v node >/dev/null 2>&1; then
175
- out="$(HQ_NO_UPDATE_CHECK=1 HQ_RESOLVE_TIMEOUT_MS="${HQ_RESOLVE_TIMEOUT_MS:-5000}" run_with_timeout "$cli" resolve-company --path "$FOLDER" --json)" || status=$?
176
- else
177
- out="$("$cli" resolve-company --path "$FOLDER" --json 2>/dev/null)" || status=$?
178
- fi
179
- kind="$(printf '%s' "$out" | jq -r 'if type != "object" then "bad" elif .company == null then "miss" elif (.company | type) == "string" then "hit" else "bad" end' 2>/dev/null | sed 's/\r$//' || printf 'bad')"
180
- if [ "$status" -ne 0 ] && ! { [ "$status" -eq 1 ] && [ "$kind" = "miss" ]; }; then
181
- unavailable "'$cli resolve-company' exited $status"
182
- fi
183
- case "$kind" in
184
- miss) emit "" "registry_miss" ;;
185
- hit) ;;
186
- *) unavailable "'$cli resolve-company' returned unreadable output" ;;
187
- esac
188
- slug="$(printf '%s' "$out" | jq -r '.company' | sed 's/\r$//')"
189
- case "$slug" in
190
- ''|personal|_*|*[!A-Za-z0-9_-]*) unavailable "registry returned invalid company '$slug'" ;;
191
- esac
192
- if [ ! -d "$ROOT/companies/$slug" ] || [ -L "$ROOT/companies/$slug" ]; then
193
- unavailable "registry company '$slug' has no companies/$slug directory in $ROOT"
194
- fi
195
- emit "$slug" "registry"
196
- fi
197
-
198
- MANIFEST="$ROOT/companies/manifest.yaml"
199
- [ -f "$MANIFEST" ] || emit "" "none"
200
-
201
- SLUGS="$(
202
- awk '
203
- function keep(slug) {
204
- return slug != "" && slug != "_template" && slug != "companies" && slug != "unaffiliated_repos"
205
- }
206
- /^companies:[[:space:]]*$/ { wrapped = 1; next }
207
- wrapped && /^[^[:space:]][^:]*:[[:space:]]*$/ { wrapped = 0 }
208
- wrapped && /^ [A-Za-z][A-Za-z0-9_-]*:/ {
209
- line = $0; sub(/^[[:space:]]+/, "", line); sub(/:.*/, "", line)
210
- if (keep(line)) print line
211
- next
212
- }
213
- !wrapped && /^[A-Za-z][A-Za-z0-9_-]*:/ {
214
- line = $0; sub(/:.*/, "", line)
215
- if (keep(line)) print line
216
- }
217
- ' "$MANIFEST" | sort -u
218
- )"
219
- [ -n "$SLUGS" ] || emit "" "none"
220
-
221
- is_known_slug() {
222
- printf '%s\n' "$SLUGS" | grep -Fx "$1" >/dev/null
223
- }
224
-
225
- prompt_slug() {
226
- local normalized="" match=""
227
- [ -n "$PROMPT" ] || return 0
228
- normalized="$(printf '%s' "$PROMPT" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9_-' ' ')"
229
- match="$(
230
- # Prompt normalization is case-insensitive, but company IDs are not.
231
- # Keep a folded match only when exactly one registered ID has that key.
232
- # If IDs differ only by case, do not guess which tenant the prompt means.
233
- printf '%s\n' "$SLUGS" | awk -v line="$normalized" '
234
- BEGIN {
235
- n = split(line, tok, /[ \t]+/)
236
- for (i = 1; i <= n; i++) if (tok[i] != "" && !(tok[i] in pos)) pos[tok[i]] = i
237
- bestSlug = ""; bestLen = 0; bestPos = 0
238
- }
239
- {
240
- slug = $0
241
- key = tolower(slug)
242
- if (key in pos) {
243
- matches[key]++
244
- candidate[key] = slug
245
- }
246
- }
247
- END {
248
- for (key in matches) {
249
- if (matches[key] != 1) continue
250
- slug = candidate[key]
251
- len = length(slug); p = pos[key]
252
- if (len > bestLen || (len == bestLen && p < bestPos)) {
253
- bestSlug = slug; bestLen = len; bestPos = p
254
- }
255
- }
256
- if (bestSlug != "") print bestSlug
257
- }
258
- '
259
- )"
260
- printf '%s' "$match"
261
- }
262
-
263
- MATCH="$(prompt_slug)"
264
- [ -n "$MATCH" ] && emit "$MATCH" "prompt"
265
-
266
- SESSION_CO=""
267
- if [ -x "$ROOT/core/scripts/hq-session.sh" ]; then
268
- SESSION_CO="$(bash "$ROOT/core/scripts/hq-session.sh" get company_slug 2>/dev/null || true)"
269
- SESSION_CO="$(printf '%s' "$SESSION_CO" | tr -d '[:space:]\"')"
270
- fi
271
- if [ -n "$SESSION_CO" ] && is_known_slug "$SESSION_CO"; then
272
- emit "$SESSION_CO" "session"
273
- fi
274
-
275
- is_fleet_identity() {
276
- [ -n "${HQ_AGENT_WORKDIR:-}" ] && return 0
277
- [ -n "${HQ_AGENT_COMPANY_DIR:-}" ] && return 0
278
- [ -n "${HQ_AGENT_IDENTITY_FILE:-}" ] && return 0
279
- [ -f /etc/hq-agent/identity.json ] && return 0
280
- [ -f /etc/hq-agent/machine-creds.json ] && return 0
281
- agent_root="${HQ_AGENT_ROOT_PREFIX:-}"
282
- if [ -n "$agent_root" ]; then
283
- [ -f "${agent_root%/}/var/lib/hq-agent/identity.json" ] && return 0
284
- else
285
- [ -f /var/lib/hq-agent/identity.json ] && return 0
286
- fi
287
- [ -f /var/lib/hq-agent/machine-creds.json ] && return 0
288
- return 1
289
- }
290
-
291
- if ! is_fleet_identity && command -v hq >/dev/null 2>&1 && command -v jq >/dev/null 2>&1; then
292
- DEFAULT_JSON="$(run_with_timeout hq mesh context default get --json 2>/dev/null || true)"
293
- DEFAULT_SLUG="$(printf '%s' "$DEFAULT_JSON" | jq -er '(.defaultCompany // .) as $default | if $default.enabled == true and ($default.source // "") != "disabled" and ($default.needsChoice // false) == false and ($default.slug | type == "string") then $default.slug else empty end' 2>/dev/null || true)"
294
- DEFAULT_SLUG="$(printf '%s' "$DEFAULT_SLUG" | tr -d '[:space:]\"')"
295
- if [ -n "$DEFAULT_SLUG" ] && is_known_slug "$DEFAULT_SLUG"; then
296
- # A device default is only a local preference. Reconcile performs the
297
- # token-backed active-membership check; never promote this fallback when
298
- # that preflight is unavailable, times out, or returns a noncanonical row.
299
- PREFLIGHT_SESSION_ID="resolve-default-$$-$RANDOM"
300
- PREFLIGHT_OPERATION_ID="resolve-default-op-$$-$RANDOM"
301
- PREFLIGHT_OBSERVATION="$(jq -cn \
302
- --arg session_id "$PREFLIGHT_SESSION_ID" \
303
- --arg operation_id "$PREFLIGHT_OPERATION_ID" \
304
- --arg root "$ROOT" \
305
- '{contractVersion:1,clientOperationId:$operation_id,identity:{sessionId:$session_id,harness:"hq-core",adapterVersion:"1"},cwd:$root,hqRoot:$root}')"
306
- PREFLIGHT_STATUS=0
307
- PREFLIGHT_RESULT="$(run_with_timeout hq mesh context reconcile --observation-json "$PREFLIGHT_OBSERVATION" --machine --offline 2>/dev/null)" || PREFLIGHT_STATUS=$?
308
- VALIDATED_SLUG=""
309
- if [ "$PREFLIGHT_STATUS" -eq 0 ]; then
310
- VALIDATED_SLUG="$(printf '%s' "$PREFLIGHT_RESULT" | jq -er \
311
- --arg slug "$DEFAULT_SLUG" \
312
- --arg session_id "$PREFLIGHT_SESSION_ID" \
313
- --arg operation_id "$PREFLIGHT_OPERATION_ID" '
314
- if type == "object"
315
- and .contractVersion == 1
316
- and (.sessionId | type == "string") and .sessionId == $session_id
317
- and (.clientOperationId | type == "string") and .clientOperationId == $operation_id
318
- and (.kind == "queued" or .kind == "needs_project" or .kind == "needs_task" or .kind == "bound")
319
- and (.classification == "needs_project" or .classification == "needs_task" or .classification == "bound")
320
- and (.delivery == "queued" or .delivery == "acked")
321
- and .lifecycle == "open"
322
- and .companySlug == $slug
323
- and (.companyUid | type == "string") and (.companyUid | length > 0)
324
- then .companySlug else empty end
325
- ' 2>/dev/null || true)"
326
- fi
327
- [ "$VALIDATED_SLUG" = "$DEFAULT_SLUG" ] && emit "$DEFAULT_SLUG" "device_default"
328
- fi
329
- fi
330
-
331
- emit "" "none"