@indigoai-us/hq-cli 5.335.1-beta.0 → 5.336.0
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 +5 -1
- package/assets/scaffold/core/scripts/outpost-jobs-reconcile.sh +5 -3
- package/assets/scaffold/core/scripts/policy-age-report.sh +18 -2
- package/assets/scaffold/core/scripts/session-journal.sh +38 -4
- package/dist/commands/core-policy-frontmatter.d.ts +1 -1
- package/dist/commands/core-policy-frontmatter.js +5 -2
- package/dist/commands/core-policy.d.ts +2 -2
- package/dist/commands/core-policy.js +22 -14
- package/dist/commands/core-resolve-company-native.js +1 -1
- package/dist/commands/core-session-journal-native.js +2 -2
- package/dist/lib/core-utils/awk-diagnostics.d.ts +7 -0
- package/dist/lib/core-utils/awk-diagnostics.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,8 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
-
## [5.
|
|
5
|
+
## [5.336.0] — 2026-10-03
|
|
6
6
|
- Client-health refresh failures now retain the bounded Cognito OAuth error class for accurate auth diagnostics.
|
|
7
|
+
- Fix macOS Bash 3.2 service PATH generation when nounset is enabled.
|
|
8
|
+
- Parse policy creation dates at midnight UTC and reject calendar-invalid dates consistently across the shell and native report.
|
|
9
|
+
- Classify missing hidden references by basename-scoped extension siblings, and escape C1 bytes in session journal YAML on every platform.
|
|
10
|
+
- Match BSD awk diagnostics for company resolution and policy frontmatter.
|
|
7
11
|
- CI: Run main-push Linux ARM checks on the reserved self-hosted runner pool.
|
|
8
12
|
|
|
9
13
|
## [5.335.0] — 2026-10-03
|
|
@@ -413,7 +413,7 @@ unit_service_path() { printf '%s/hq-job-%s.service' "$UNIT_DIR" "$1"; }
|
|
|
413
413
|
unit_timer_path() { printf '%s/hq-job-%s.timer' "$UNIT_DIR" "$1"; }
|
|
414
414
|
|
|
415
415
|
normalize_service_path() {
|
|
416
|
-
local remaining="$1" entry existing normalized="" has_more seen
|
|
416
|
+
local remaining="$1" entry existing normalized="" has_more seen entry_count=0 index
|
|
417
417
|
local -a entries=()
|
|
418
418
|
while :; do
|
|
419
419
|
if [[ "$remaining" == *:* ]]; then
|
|
@@ -428,7 +428,8 @@ normalize_service_path() {
|
|
|
428
428
|
case "$entry" in
|
|
429
429
|
/*)
|
|
430
430
|
seen=0
|
|
431
|
-
for
|
|
431
|
+
for ((index = 0; index < entry_count; index++)); do
|
|
432
|
+
existing="${entries[index]}"
|
|
432
433
|
if [ "$existing" = "$entry" ]; then
|
|
433
434
|
seen=1
|
|
434
435
|
break
|
|
@@ -436,12 +437,13 @@ normalize_service_path() {
|
|
|
436
437
|
done
|
|
437
438
|
if [ "$seen" -eq 0 ]; then
|
|
438
439
|
entries+=("$entry")
|
|
440
|
+
entry_count=$((entry_count + 1))
|
|
439
441
|
fi
|
|
440
442
|
;;
|
|
441
443
|
esac
|
|
442
444
|
[ "$has_more" -eq 1 ] || break
|
|
443
445
|
done
|
|
444
|
-
if [ "$
|
|
446
|
+
if [ "$entry_count" -gt 0 ]; then
|
|
445
447
|
local IFS=:
|
|
446
448
|
normalized="${entries[*]}"
|
|
447
449
|
fi
|
|
@@ -100,7 +100,12 @@ else
|
|
|
100
100
|
fi
|
|
101
101
|
|
|
102
102
|
to_epoch() { # YYYY-MM-DD -> epoch (0 on failure)
|
|
103
|
-
|
|
103
|
+
local epoch normalized
|
|
104
|
+
epoch="$(date -u -d "$1 00:00:00" +%s 2>/dev/null || date -u -j -f '%Y-%m-%d %H:%M:%S' "$1 00:00:00" +%s 2>/dev/null)" || { echo 0; return; }
|
|
105
|
+
[ -n "$epoch" ] || { echo 0; return; }
|
|
106
|
+
normalized="$(date -u -d "@$epoch" +%Y-%m-%d 2>/dev/null || date -u -r "$epoch" +%Y-%m-%d 2>/dev/null)" || { echo 0; return; }
|
|
107
|
+
[ "$normalized" = "$1" ] || { echo 0; return; }
|
|
108
|
+
echo "$epoch"
|
|
104
109
|
}
|
|
105
110
|
|
|
106
111
|
for dir in "${DIRS[@]}"; do
|
|
@@ -144,7 +149,18 @@ for dir in "${DIRS[@]}"; do
|
|
|
144
149
|
if [ ! -e "$HQ_ROOT/$ref" ]; then
|
|
145
150
|
# A sibling with the same stem (renamed extension: foo.sh -> foo.mjs) means
|
|
146
151
|
# the subject still exists and the rule needs a fix, not retirement.
|
|
147
|
-
|
|
152
|
+
ref_for_stem="${ref%/}"
|
|
153
|
+
ref_basename="${ref_for_stem##*/}"
|
|
154
|
+
case "$ref_basename" in
|
|
155
|
+
.* )
|
|
156
|
+
case "${ref_basename#.}" in
|
|
157
|
+
*.*) stem="${ref_for_stem%.*}" ;;
|
|
158
|
+
*) stem="$ref_for_stem" ;;
|
|
159
|
+
esac
|
|
160
|
+
;;
|
|
161
|
+
*.*) stem="${ref_for_stem%.*}" ;;
|
|
162
|
+
*) stem="$ref_for_stem" ;;
|
|
163
|
+
esac
|
|
148
164
|
if ls "$HQ_ROOT/$stem".* >/dev/null 2>&1; then renamed="${renamed:+$renamed,}$ref"; else stale="${stale:+$stale,}$ref"; fi
|
|
149
165
|
fi
|
|
150
166
|
done < <(awk '/^## Rule/{r=1;next} r&&/^## /{r=0} r' "$f" \
|
|
@@ -44,15 +44,49 @@ slugify() {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
yaml_double_quoted() {
|
|
47
|
-
local value="$1" output='"' character next
|
|
47
|
+
local value="$1" output='"' character next pair hex
|
|
48
48
|
local LC_ALL=C index
|
|
49
49
|
for ((index = 0; index < ${#value}; index++)); do
|
|
50
50
|
character="${value:index:1}"
|
|
51
51
|
if [[ "$character" == $'\xc2' ]] && (( index + 1 < ${#value} )); then
|
|
52
52
|
next="${value:index+1:1}"
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
pair="$character$next"
|
|
54
|
+
case "$pair" in
|
|
55
|
+
$'\xc2\x80') hex=0080 ;;
|
|
56
|
+
$'\xc2\x81') hex=0081 ;;
|
|
57
|
+
$'\xc2\x82') hex=0082 ;;
|
|
58
|
+
$'\xc2\x83') hex=0083 ;;
|
|
59
|
+
$'\xc2\x84') hex=0084 ;;
|
|
60
|
+
$'\xc2\x85') hex=0085 ;;
|
|
61
|
+
$'\xc2\x86') hex=0086 ;;
|
|
62
|
+
$'\xc2\x87') hex=0087 ;;
|
|
63
|
+
$'\xc2\x88') hex=0088 ;;
|
|
64
|
+
$'\xc2\x89') hex=0089 ;;
|
|
65
|
+
$'\xc2\x8a') hex=008a ;;
|
|
66
|
+
$'\xc2\x8b') hex=008b ;;
|
|
67
|
+
$'\xc2\x8c') hex=008c ;;
|
|
68
|
+
$'\xc2\x8d') hex=008d ;;
|
|
69
|
+
$'\xc2\x8e') hex=008e ;;
|
|
70
|
+
$'\xc2\x8f') hex=008f ;;
|
|
71
|
+
$'\xc2\x90') hex=0090 ;;
|
|
72
|
+
$'\xc2\x91') hex=0091 ;;
|
|
73
|
+
$'\xc2\x92') hex=0092 ;;
|
|
74
|
+
$'\xc2\x93') hex=0093 ;;
|
|
75
|
+
$'\xc2\x94') hex=0094 ;;
|
|
76
|
+
$'\xc2\x95') hex=0095 ;;
|
|
77
|
+
$'\xc2\x96') hex=0096 ;;
|
|
78
|
+
$'\xc2\x97') hex=0097 ;;
|
|
79
|
+
$'\xc2\x98') hex=0098 ;;
|
|
80
|
+
$'\xc2\x99') hex=0099 ;;
|
|
81
|
+
$'\xc2\x9a') hex=009a ;;
|
|
82
|
+
$'\xc2\x9b') hex=009b ;;
|
|
83
|
+
$'\xc2\x9c') hex=009c ;;
|
|
84
|
+
$'\xc2\x9d') hex=009d ;;
|
|
85
|
+
$'\xc2\x9e') hex=009e ;;
|
|
86
|
+
$'\xc2\x9f') hex=009f ;;
|
|
87
|
+
*) hex= ;;
|
|
88
|
+
esac
|
|
89
|
+
if [[ -n "$hex" ]]; then
|
|
56
90
|
output+="\\u$hex"
|
|
57
91
|
index=$((index + 1))
|
|
58
92
|
continue
|
|
@@ -4,5 +4,5 @@ export type FrontmatterResult = {
|
|
|
4
4
|
stderr: Buffer;
|
|
5
5
|
};
|
|
6
6
|
/** Byte-preserving port of core/scripts/read-policy-frontmatter.sh. */
|
|
7
|
-
export declare function runPolicyFrontmatter(args: readonly string[], cwd: string): FrontmatterResult;
|
|
7
|
+
export declare function runPolicyFrontmatter(args: readonly string[], cwd: string, pathValue?: string | undefined, platform?: NodeJS.Platform): FrontmatterResult;
|
|
8
8
|
//# sourceMappingURL=core-policy-frontmatter.d.ts.map
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as fs from 'node:fs';
|
|
2
2
|
import * as path from 'node:path';
|
|
3
|
+
import { usesGnuAwk } from '../lib/core-utils/awk-diagnostics.js';
|
|
3
4
|
const USAGE = Buffer.from('USAGE: read-policy-frontmatter.sh <policy-file>\n');
|
|
4
5
|
/** Byte-preserving port of core/scripts/read-policy-frontmatter.sh. */
|
|
5
|
-
export function runPolicyFrontmatter(args, cwd) {
|
|
6
|
+
export function runPolicyFrontmatter(args, cwd, pathValue = process.env.PATH, platform = process.platform) {
|
|
6
7
|
const first = args[0];
|
|
7
8
|
if (first === undefined)
|
|
8
9
|
return { code: 1, stdout: Buffer.alloc(0), stderr: USAGE };
|
|
@@ -18,7 +19,9 @@ export function runPolicyFrontmatter(args, cwd) {
|
|
|
18
19
|
return {
|
|
19
20
|
code: 0,
|
|
20
21
|
stdout: Buffer.alloc(0),
|
|
21
|
-
stderr: Buffer.from(
|
|
22
|
+
stderr: Buffer.from(usesGnuAwk(pathValue, platform)
|
|
23
|
+
? `awk: warning: command line argument \`${first}' is a directory: skipped\n`
|
|
24
|
+
: ''),
|
|
22
25
|
};
|
|
23
26
|
}
|
|
24
27
|
if (code === 'ENOENT' || code === 'ENOTDIR' || code === 'EACCES' || code === 'ELOOP') {
|
|
@@ -5,8 +5,8 @@ export type CorePolicyResult = {
|
|
|
5
5
|
stdout: Buffer;
|
|
6
6
|
stderr: Buffer;
|
|
7
7
|
};
|
|
8
|
-
export declare function runPolicyAgeReport(args: readonly string[], root: string, env?: NodeJS.ProcessEnv, nowEpoch?: number): CorePolicyResult;
|
|
9
|
-
export declare function runPolicyFrontmatter(args: readonly string[], cwd: string): FrontmatterResult;
|
|
8
|
+
export declare function runPolicyAgeReport(args: readonly string[], root: string, env?: NodeJS.ProcessEnv, nowEpoch?: number, platform?: NodeJS.Platform): CorePolicyResult;
|
|
9
|
+
export declare function runPolicyFrontmatter(args: readonly string[], cwd: string, env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): FrontmatterResult;
|
|
10
10
|
export declare function runPolicyRetire(args: readonly string[], root: string, cwd: string, env?: NodeJS.ProcessEnv, now?: Date): CorePolicyResult;
|
|
11
11
|
/** Fast-entry adapter; family gating and shell fallback happen before import. */
|
|
12
12
|
export declare function runPolicyFast(args: readonly string[], _stdin: string, cwd: string, hqRoot: string | undefined, commandName: string): number;
|
|
@@ -206,8 +206,11 @@ function extractRulePaths(file) {
|
|
|
206
206
|
return [...paths].sort();
|
|
207
207
|
}
|
|
208
208
|
function shellReferenceStem(reference) {
|
|
209
|
-
const
|
|
210
|
-
|
|
209
|
+
const normalized = reference.replace(/\/$/, '');
|
|
210
|
+
const slash = normalized.lastIndexOf('/');
|
|
211
|
+
const basename = normalized.slice(slash + 1);
|
|
212
|
+
const lastDot = basename.lastIndexOf('.');
|
|
213
|
+
return lastDot <= 0 ? normalized : normalized.slice(0, slash + 1 + lastDot);
|
|
211
214
|
}
|
|
212
215
|
function shellLsExtensionGlobHasMatch(root, stem) {
|
|
213
216
|
const patternPath = path.join(root, `${stem}.*`);
|
|
@@ -223,8 +226,8 @@ function shellLsExtensionGlobHasMatch(root, stem) {
|
|
|
223
226
|
return false;
|
|
224
227
|
throw error;
|
|
225
228
|
}
|
|
226
|
-
//
|
|
227
|
-
// directory
|
|
229
|
+
// The shell checks extension siblings only within the reference basename's
|
|
230
|
+
// directory, so directory dot-components never count as sibling files.
|
|
228
231
|
return entries.some((entry) => entry.startsWith(literalPrefix));
|
|
229
232
|
}
|
|
230
233
|
function refSummary(file, root) {
|
|
@@ -260,12 +263,17 @@ function refSummary(file, root) {
|
|
|
260
263
|
return { stale, renamed, judged: judged.length };
|
|
261
264
|
}
|
|
262
265
|
function epochFromDate(value) {
|
|
263
|
-
if (
|
|
266
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(value))
|
|
264
267
|
return 0;
|
|
265
|
-
const
|
|
266
|
-
|
|
268
|
+
const year = Number(value.slice(0, 4));
|
|
269
|
+
const month = Number(value.slice(5, 7));
|
|
270
|
+
const day = Number(value.slice(8, 10));
|
|
271
|
+
if (month < 1 || month > 12 || day < 1 || day > 31)
|
|
267
272
|
return 0;
|
|
268
|
-
|
|
273
|
+
const parsed = new Date(Date.UTC(year, month - 1, day));
|
|
274
|
+
if (parsed.getUTCFullYear() !== year || parsed.getUTCMonth() + 1 !== month || parsed.getUTCDate() !== day)
|
|
275
|
+
return 0;
|
|
276
|
+
return Math.floor(parsed.getTime() / 1000);
|
|
269
277
|
}
|
|
270
278
|
function countLedgerRows(directory, withMtime, recursive, awkPath) {
|
|
271
279
|
const counts = new Map();
|
|
@@ -380,7 +388,7 @@ function bashTestGreaterOrEqual(left, right, scriptPath, line) {
|
|
|
380
388
|
}
|
|
381
389
|
return { value: BigInt(left) >= parsed, stderr: '' };
|
|
382
390
|
}
|
|
383
|
-
export function runPolicyAgeReport(args, root, env = process.env, nowEpoch = Math.floor(Date.now() / 1000)) {
|
|
391
|
+
export function runPolicyAgeReport(args, root, env = process.env, nowEpoch = Math.floor(Date.now() / 1000), platform = process.platform) {
|
|
384
392
|
const parsed = parseAgeOptions(args, resolveBundledAsset(POLICY_AGE_REPORT_ASSET));
|
|
385
393
|
if (parsed.kind === 'help')
|
|
386
394
|
return policyResult(0, AGE_HELP);
|
|
@@ -440,13 +448,13 @@ export function runPolicyAgeReport(args, root, env = process.env, nowEpoch = Mat
|
|
|
440
448
|
const singleIncident = ['user-correction', 'session-learning', 'back-pressure-failure'].includes(source) && (version || '1') === '1';
|
|
441
449
|
const classes = [];
|
|
442
450
|
if (fired === 0) {
|
|
443
|
-
const comparison = bashTestGreaterOrEqual(ageDays, parsed.minAge, resolveBundledAsset(POLICY_AGE_REPORT_ASSET),
|
|
451
|
+
const comparison = bashTestGreaterOrEqual(ageDays, parsed.minAge, resolveBundledAsset(POLICY_AGE_REPORT_ASSET), 173);
|
|
444
452
|
stderr += comparison.stderr;
|
|
445
453
|
if (comparison.value && usage.anyFiles)
|
|
446
454
|
classes.push('never-fired');
|
|
447
455
|
}
|
|
448
456
|
if (fired > 0) {
|
|
449
|
-
const comparison = bashTestGreaterOrEqual(lastAge, parsed.dormantDays, resolveBundledAsset(POLICY_AGE_REPORT_ASSET),
|
|
457
|
+
const comparison = bashTestGreaterOrEqual(lastAge, parsed.dormantDays, resolveBundledAsset(POLICY_AGE_REPORT_ASSET), 174);
|
|
450
458
|
stderr += comparison.stderr;
|
|
451
459
|
if (comparison.value)
|
|
452
460
|
classes.push('dormant');
|
|
@@ -456,7 +464,7 @@ export function runPolicyAgeReport(args, root, env = process.env, nowEpoch = Mat
|
|
|
456
464
|
if (refs.renamed.length > 0)
|
|
457
465
|
classes.push('renamed-refs');
|
|
458
466
|
if (singleIncident) {
|
|
459
|
-
const comparison = bashTestGreaterOrEqual(ageDays, parsed.incidentDays, resolveBundledAsset(POLICY_AGE_REPORT_ASSET),
|
|
467
|
+
const comparison = bashTestGreaterOrEqual(ageDays, parsed.incidentDays, resolveBundledAsset(POLICY_AGE_REPORT_ASSET), 178);
|
|
460
468
|
stderr += comparison.stderr;
|
|
461
469
|
if (comparison.value && fired <= 1)
|
|
462
470
|
classes.push('single-incident-old');
|
|
@@ -496,8 +504,8 @@ export function runPolicyAgeReport(args, root, env = process.env, nowEpoch = Mat
|
|
|
496
504
|
}
|
|
497
505
|
return policyResult(0, stdout, stderr);
|
|
498
506
|
}
|
|
499
|
-
export function runPolicyFrontmatter(args, cwd) {
|
|
500
|
-
return runPolicyFrontmatterImpl(args, cwd);
|
|
507
|
+
export function runPolicyFrontmatter(args, cwd, env = process.env, platform = process.platform) {
|
|
508
|
+
return runPolicyFrontmatterImpl(args, cwd, env.PATH, platform);
|
|
501
509
|
}
|
|
502
510
|
function parseRetireOptions(args, scriptPath, env) {
|
|
503
511
|
const value = {
|
|
@@ -47,7 +47,7 @@ function readManifest(filePath, fileSystem, pathValue) {
|
|
|
47
47
|
return {
|
|
48
48
|
code: 0,
|
|
49
49
|
stdout: Buffer.from('{"company":"","source":"none"}\n'),
|
|
50
|
-
stderr: Buffer.from(awkCannotOpenFileDiagnostic(filePath, "Permission denied",
|
|
50
|
+
stderr: Buffer.from(awkCannotOpenFileDiagnostic(filePath, "Permission denied", 11, process.platform, pathValue)),
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
53
|
if (code === "ENOENT" || code === "ENOTDIR" || code === "ELOOP")
|
|
@@ -202,7 +202,7 @@ function runToolCounter(root, action, sessionArg, env, now, writers) {
|
|
|
202
202
|
fs.writeFileSync(counter, `${readCounter(counter) + 1}\n`);
|
|
203
203
|
}
|
|
204
204
|
catch (error) {
|
|
205
|
-
reportDirectoryWriteFailure(error,
|
|
205
|
+
reportDirectoryWriteFailure(error, 346);
|
|
206
206
|
}
|
|
207
207
|
});
|
|
208
208
|
}
|
|
@@ -215,7 +215,7 @@ function runToolCounter(root, action, sessionArg, env, now, writers) {
|
|
|
215
215
|
fs.writeFileSync(counter, "0\n");
|
|
216
216
|
}
|
|
217
217
|
catch (error) {
|
|
218
|
-
reportDirectoryWriteFailure(error,
|
|
218
|
+
reportDirectoryWriteFailure(error, 354);
|
|
219
219
|
}
|
|
220
220
|
});
|
|
221
221
|
}
|
|
@@ -1,2 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bundled shell companions emit awk's open-file diagnostic. GNU awk and
|
|
3
|
+
* the BSD awk shipped by macOS format that diagnostic differently. Prefer
|
|
4
|
+
* the executable selected by PATH; macOS runners can put GNU awk ahead of the
|
|
5
|
+
* system awk.
|
|
6
|
+
*/
|
|
7
|
+
export declare function usesGnuAwk(pathValue: string | undefined, platform: NodeJS.Platform): boolean;
|
|
1
8
|
export declare function awkCannotOpenFileDiagnostic(filename: string, reason: string, sourceLine?: number, platform?: NodeJS.Platform, pathValue?: string | undefined): string;
|
|
2
9
|
//# sourceMappingURL=awk-diagnostics.d.ts.map
|
|
@@ -6,7 +6,7 @@ import * as path from "node:path";
|
|
|
6
6
|
* the executable selected by PATH; macOS runners can put GNU awk ahead of the
|
|
7
7
|
* system awk.
|
|
8
8
|
*/
|
|
9
|
-
function usesGnuAwk(pathValue, platform) {
|
|
9
|
+
export function usesGnuAwk(pathValue, platform) {
|
|
10
10
|
for (const directory of (pathValue ?? "/usr/bin:/bin").split(path.delimiter)) {
|
|
11
11
|
if (!directory)
|
|
12
12
|
continue;
|