@indigoai-us/hq-cli 5.345.170 → 5.345.172

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 (142) hide show
  1. package/CHANGELOG.md +84 -0
  2. package/assets/scaffold/core/scripts/hq-delegate-send.sh +1 -1
  3. package/dist/command-catalog.generated.d.ts +206 -7
  4. package/dist/command-catalog.generated.js +252 -8
  5. package/dist/command-registration-plan.d.ts +7 -0
  6. package/dist/command-registration-plan.js +1 -0
  7. package/dist/commands/access.d.ts +32 -1
  8. package/dist/commands/access.js +112 -3
  9. package/dist/commands/agent-kit.d.ts +1 -1
  10. package/dist/commands/agent-kit.js +3 -1
  11. package/dist/commands/agents.js +2 -1
  12. package/dist/commands/bot.d.ts +3 -0
  13. package/dist/commands/bot.js +179 -18
  14. package/dist/commands/cloud.d.ts +58 -1
  15. package/dist/commands/cloud.js +228 -10
  16. package/dist/commands/core-company-index-scope-flag.d.ts +8 -1
  17. package/dist/commands/core-company-index-scope-flag.js +9 -2
  18. package/dist/commands/core-delegate-w1b.js +5 -2
  19. package/dist/commands/core-dm-bind-native.js +19 -4
  20. package/dist/commands/core-rebuild-index-fast.js +24 -6
  21. package/dist/commands/core.js +18 -6
  22. package/dist/commands/daemon.js +16 -1
  23. package/dist/commands/dm.d.ts +17 -3
  24. package/dist/commands/dm.js +121 -26
  25. package/dist/commands/files.d.ts +1 -0
  26. package/dist/commands/files.js +22 -4
  27. package/dist/commands/integrations-api.d.ts +4 -0
  28. package/dist/commands/integrations-api.js +18 -2
  29. package/dist/commands/integrations-connect.d.ts +10 -3
  30. package/dist/commands/integrations-connect.js +70 -6
  31. package/dist/commands/integrations-core.d.ts +23 -0
  32. package/dist/commands/integrations-core.js +100 -0
  33. package/dist/commands/integrations-manage.js +8 -1
  34. package/dist/commands/integrations.js +49 -5
  35. package/dist/commands/lanes-loop.d.ts +20 -0
  36. package/dist/commands/lanes-loop.js +69 -11
  37. package/dist/commands/lanes.js +9 -1
  38. package/dist/commands/rescue.d.ts +9 -0
  39. package/dist/commands/rescue.js +31 -0
  40. package/dist/commands/run.js +115 -40
  41. package/dist/commands/scan.d.ts +5 -1
  42. package/dist/commands/scan.js +16 -4
  43. package/dist/commands/secrets.js +50 -9
  44. package/dist/commands/sync-core.d.ts +22 -0
  45. package/dist/commands/sync-core.js +124 -0
  46. package/dist/hq-cloud-sync-runner/bin/sync-runner.js +2447 -2135
  47. package/dist/hq-cloud-sync-runner/package.json +1 -1
  48. package/dist/lib/agent-kit/inbox-state.d.ts +17 -0
  49. package/dist/lib/agent-kit/inbox-state.js +41 -20
  50. package/dist/lib/agent-kit/kit-config.d.ts +2 -0
  51. package/dist/lib/agent-kit/kit-config.js +6 -0
  52. package/dist/lib/agent-kit/mcp/tools.js +12 -5
  53. package/dist/lib/agent-kit/run/heartbeat.d.ts +6 -3
  54. package/dist/lib/agent-kit/run/heartbeat.js +6 -3
  55. package/dist/lib/agent-kit/run/inbox.d.ts +8 -3
  56. package/dist/lib/agent-kit/run/inbox.js +42 -10
  57. package/dist/lib/agent-kit/run/request-timeout.d.ts +26 -0
  58. package/dist/lib/agent-kit/run/request-timeout.js +51 -0
  59. package/dist/lib/bot/api.d.ts +1 -1
  60. package/dist/lib/bot/config.d.ts +10 -0
  61. package/dist/lib/bot/config.js +17 -1
  62. package/dist/lib/bot/fleet-local.d.ts +142 -0
  63. package/dist/lib/bot/fleet-local.js +689 -0
  64. package/dist/lib/bot/fleet-python.d.ts +47 -0
  65. package/dist/lib/bot/fleet-python.js +218 -0
  66. package/dist/lib/bot/index.d.ts +2 -0
  67. package/dist/lib/bot/index.js +2 -0
  68. package/dist/lib/bot/local-config.d.ts +3 -2
  69. package/dist/lib/bot/local-config.js +5 -2
  70. package/dist/lib/bot/promote.d.ts +10 -0
  71. package/dist/lib/bot/promote.js +3 -2
  72. package/dist/lib/bot/prompt.d.ts +6 -1
  73. package/dist/lib/bot/prompt.js +8 -3
  74. package/dist/lib/bot/runtime/index.js +8 -4
  75. package/dist/lib/bot/runtime/scratch-dir.d.ts +24 -0
  76. package/dist/lib/bot/runtime/scratch-dir.js +41 -0
  77. package/dist/lib/caller-principal.d.ts +4 -0
  78. package/dist/lib/caller-principal.js +40 -0
  79. package/dist/lib/capacity/collectors/grok.js +8 -2
  80. package/dist/lib/cli-flag-keys.d.ts +2 -0
  81. package/dist/lib/cli-flag-keys.js +2 -0
  82. package/dist/lib/daemon/catalog.d.ts +12 -4
  83. package/dist/lib/daemon/catalog.js +70 -24
  84. package/dist/lib/daemon/config.d.ts +0 -7
  85. package/dist/lib/daemon/config.js +0 -32
  86. package/dist/lib/daemon/run.d.ts +5 -1
  87. package/dist/lib/daemon/run.js +48 -11
  88. package/dist/lib/daemon/supervisor.js +3 -1
  89. package/dist/lib/doctor/checks/release-links.d.ts +62 -1
  90. package/dist/lib/doctor/checks/release-links.js +188 -2
  91. package/dist/lib/doctor/fix/apply.js +54 -0
  92. package/dist/lib/doctor/fix/remediation.d.ts +8 -1
  93. package/dist/lib/doctor/fix/remediation.js +22 -0
  94. package/dist/lib/flag-registry-worker-source.generated.d.ts +1 -1
  95. package/dist/lib/flag-registry-worker-source.generated.js +1 -1
  96. package/dist/lib/index-render/index.d.ts +9 -0
  97. package/dist/lib/index-render/index.js +65 -5
  98. package/dist/lib/index-render/session-scope.d.ts +69 -0
  99. package/dist/lib/index-render/session-scope.js +279 -0
  100. package/dist/lib/lanes/admission.js +7 -20
  101. package/dist/lib/lanes/reconcile.js +13 -15
  102. package/dist/lib/lanes/stale-lock.d.ts +50 -0
  103. package/dist/lib/lanes/stale-lock.js +105 -0
  104. package/dist/lib/lanes/store.js +11 -76
  105. package/dist/lib/notify-recipient.d.ts +10 -4
  106. package/dist/lib/notify-recipient.js +16 -4
  107. package/dist/lib/secret-input-link.d.ts +42 -0
  108. package/dist/lib/secret-input-link.js +93 -0
  109. package/dist/lib/secret-scan/range.d.ts +57 -0
  110. package/dist/lib/secret-scan/range.js +266 -0
  111. package/dist/lib/secret-scan/repo-config.d.ts +18 -3
  112. package/dist/lib/secret-scan/repo-config.js +135 -2
  113. package/dist/lib/sync-core/engine-home.d.ts +44 -0
  114. package/dist/lib/sync-core/engine-home.js +193 -0
  115. package/dist/lib/sync-core/flag.d.ts +17 -0
  116. package/dist/lib/sync-core/flag.js +47 -0
  117. package/dist/lib/sync-core/reconcile.d.ts +31 -0
  118. package/dist/lib/sync-core/reconcile.js +196 -0
  119. package/dist/lib/sync-core/release.d.ts +32 -0
  120. package/dist/lib/sync-core/release.js +140 -0
  121. package/dist/lib/sync-core/rollout-state.d.ts +24 -0
  122. package/dist/lib/sync-core/rollout-state.js +94 -0
  123. package/dist/lib/vault-sync-switches.d.ts +5 -0
  124. package/dist/lib/vault-sync-switches.js +43 -0
  125. package/dist/run/script-bound-schema.d.ts +27 -0
  126. package/dist/run/script-bound-schema.js +97 -0
  127. package/dist/secrets-env-action.d.ts +2 -0
  128. package/dist/secrets-env-action.js +7 -2
  129. package/dist/utils/cognito-session.d.ts +3 -30
  130. package/dist/utils/cognito-session.js +2 -100
  131. package/dist/utils/machine-principal-state.d.ts +38 -0
  132. package/dist/utils/machine-principal-state.js +110 -0
  133. package/dist/utils/remote-list-not-converging.d.ts +21 -0
  134. package/dist/utils/remote-list-not-converging.js +40 -0
  135. package/dist/utils/safe-link.js +9 -0
  136. package/dist/utils/script-bound-exec.d.ts +158 -0
  137. package/dist/utils/script-bound-exec.js +568 -0
  138. package/dist/utils/sync-watchdog.d.ts +92 -0
  139. package/dist/utils/sync-watchdog.js +250 -0
  140. package/package.json +8 -8
  141. package/dist/lib/daemon/sync-core-canary.d.ts +0 -9
  142. package/dist/lib/daemon/sync-core-canary.js +0 -56
@@ -0,0 +1,24 @@
1
+ import { type SyncCorePlanFigures, type SyncCoreVaultKind, type SyncCoreVaultPhase } from "./engine-home.js";
2
+ export interface RolloutVault {
3
+ uid: string;
4
+ kind: SyncCoreVaultKind;
5
+ slug?: string;
6
+ phase: SyncCoreVaultPhase;
7
+ engineHome: string;
8
+ legacyOffSince?: string;
9
+ legacyAlreadyOff?: true;
10
+ plan?: SyncCorePlanFigures;
11
+ hold?: string;
12
+ }
13
+ export interface SyncCoreRolloutState {
14
+ version: 1;
15
+ readAt: string;
16
+ release: string;
17
+ binaryPath: string;
18
+ vaults: RolloutVault[];
19
+ error?: string;
20
+ }
21
+ export declare function syncCoreRolloutStatePath(home: string): string;
22
+ export declare function readSyncCoreRolloutState(home: string): SyncCoreRolloutState | null;
23
+ export declare function writeSyncCoreRolloutState(home: string, state: SyncCoreRolloutState): void;
24
+ //# sourceMappingURL=rollout-state.d.ts.map
@@ -0,0 +1,94 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { isSyncCoreVaultUid } from "./engine-home.js";
4
+ function planFigures(value) {
5
+ if (!value || typeof value !== "object" || Array.isArray(value))
6
+ return null;
7
+ const p = value;
8
+ if (typeof p.at !== "string")
9
+ return null;
10
+ const numbers = ["uploads", "downloads", "deletes", "remoteRows", "localFiles"];
11
+ if (numbers.some((key) => typeof p[key] !== "number" || !Number.isFinite(p[key])))
12
+ return null;
13
+ return {
14
+ at: p.at,
15
+ uploads: p.uploads,
16
+ downloads: p.downloads,
17
+ deletes: p.deletes,
18
+ remoteRows: p.remoteRows,
19
+ localFiles: p.localFiles,
20
+ };
21
+ }
22
+ export function syncCoreRolloutStatePath(home) {
23
+ return path.join(home, ".hq", "sync-core", "rollout.json");
24
+ }
25
+ function vault(value) {
26
+ if (!value || typeof value !== "object" || Array.isArray(value))
27
+ return null;
28
+ const v = value;
29
+ if (!isSyncCoreVaultUid(v.uid))
30
+ return null;
31
+ if (v.kind !== "personal" && v.kind !== "company")
32
+ return null;
33
+ if (typeof v.engineHome !== "string" || !path.isAbsolute(v.engineHome))
34
+ return null;
35
+ if (v.legacyOffSince !== undefined && typeof v.legacyOffSince !== "string")
36
+ return null;
37
+ if (v.phase === undefined && typeof v.legacyOffSince === "string")
38
+ v.phase = "full";
39
+ if (v.phase !== "warming" && v.phase !== "full")
40
+ return null;
41
+ if (v.kind === "company" && (typeof v.slug !== "string" || !/^[a-z0-9][a-z0-9_-]*$/.test(v.slug)))
42
+ return null;
43
+ const plan = v.plan === undefined ? undefined : planFigures(v.plan);
44
+ if (plan === null)
45
+ return null;
46
+ if (v.hold !== undefined && typeof v.hold !== "string")
47
+ return null;
48
+ return {
49
+ uid: v.uid,
50
+ kind: v.kind,
51
+ ...(typeof v.slug === "string" ? { slug: v.slug } : {}),
52
+ phase: v.phase,
53
+ engineHome: v.engineHome,
54
+ ...(typeof v.legacyOffSince === "string" ? { legacyOffSince: v.legacyOffSince } : {}),
55
+ ...(v.legacyAlreadyOff === true ? { legacyAlreadyOff: true } : {}),
56
+ ...(plan ? { plan } : {}),
57
+ ...(typeof v.hold === "string" ? { hold: v.hold } : {}),
58
+ };
59
+ }
60
+ export function readSyncCoreRolloutState(home) {
61
+ let raw;
62
+ try {
63
+ raw = JSON.parse(fs.readFileSync(syncCoreRolloutStatePath(home), "utf8"));
64
+ }
65
+ catch {
66
+ return null;
67
+ }
68
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
69
+ return null;
70
+ const obj = raw;
71
+ if (obj.version !== 1 || typeof obj.release !== "string" || typeof obj.binaryPath !== "string")
72
+ return null;
73
+ if (typeof obj.readAt !== "string" || !Array.isArray(obj.vaults))
74
+ return null;
75
+ const vaults = obj.vaults.map(vault);
76
+ if (vaults.some((v) => v === null))
77
+ return null;
78
+ return {
79
+ version: 1,
80
+ readAt: obj.readAt,
81
+ release: obj.release,
82
+ binaryPath: obj.binaryPath,
83
+ vaults: vaults,
84
+ ...(typeof obj.error === "string" ? { error: obj.error } : {}),
85
+ };
86
+ }
87
+ export function writeSyncCoreRolloutState(home, state) {
88
+ const file = syncCoreRolloutStatePath(home);
89
+ fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
90
+ const tmp = `${file}.${process.pid}.tmp`;
91
+ fs.writeFileSync(tmp, `${JSON.stringify(state, null, 2)}\n`, { mode: 0o600 });
92
+ fs.renameSync(tmp, file);
93
+ }
94
+ //# sourceMappingURL=rollout-state.js.map
@@ -38,4 +38,9 @@ export declare function unreadableSwitchesWarning(reason: string): string;
38
38
  * (`HQ_SYNC_SKIP_COMPANIES`, `HQ_SYNC_SKIP_PERSONAL`). Empty when all are on.
39
39
  */
40
40
  export declare function vaultSkipEnv(switches: VaultSyncSwitches): Record<string, string>;
41
+ export interface VaultSyncSwitchPatch {
42
+ personal?: boolean;
43
+ companies?: Record<string, boolean>;
44
+ }
45
+ export declare function writeVaultSyncSwitches(home: string, patch: VaultSyncSwitchPatch): boolean;
41
46
  //# sourceMappingURL=vault-sync-switches.d.ts.map
@@ -83,4 +83,47 @@ export function vaultSkipEnv(switches) {
83
83
  env.HQ_SYNC_SKIP_PERSONAL = "1";
84
84
  return env;
85
85
  }
86
+ export function writeVaultSyncSwitches(home, patch) {
87
+ const file = vaultSyncSettingsPath(home);
88
+ let prefs = {};
89
+ let text = null;
90
+ try {
91
+ text = fs.readFileSync(file, "utf8");
92
+ }
93
+ catch (err) {
94
+ if (err?.code !== "ENOENT")
95
+ throw err;
96
+ }
97
+ if (text !== null) {
98
+ const raw = JSON.parse(text);
99
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw))
100
+ throw new Error(`${file} is not a JSON object`);
101
+ prefs = { ...raw };
102
+ }
103
+ let changed = false;
104
+ if (patch.personal !== undefined && prefs.personalSyncEnabled !== patch.personal) {
105
+ prefs.personalSyncEnabled = patch.personal;
106
+ changed = true;
107
+ }
108
+ if (patch.companies && Object.keys(patch.companies).length > 0) {
109
+ const existing = prefs.workspaceSyncEnabled;
110
+ const map = typeof existing === "object" && existing !== null && !Array.isArray(existing)
111
+ ? { ...existing }
112
+ : {};
113
+ for (const [slug, enabled] of Object.entries(patch.companies)) {
114
+ if (map[slug] !== enabled) {
115
+ map[slug] = enabled;
116
+ changed = true;
117
+ }
118
+ }
119
+ prefs.workspaceSyncEnabled = map;
120
+ }
121
+ if (!changed)
122
+ return false;
123
+ fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
124
+ const tmp = `${file}.${process.pid}.tmp`;
125
+ fs.writeFileSync(tmp, `${JSON.stringify(prefs, null, 2)}\n`, { mode: 0o600 });
126
+ fs.renameSync(tmp, file);
127
+ return true;
128
+ }
86
129
  //# sourceMappingURL=vault-sync-switches.js.map
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Strict env-schema reader for `hq run --script` (feedback_0e6abcb8).
3
+ *
4
+ * With --script, `hq run` must not load the caller's schema through varlock:
5
+ * a varlock graph can run `exec()` resolvers, load plugins, or import other
6
+ * files, and any of that runs inside the CLI process while the fetched
7
+ * secrets are in memory. A caller could write `LEAK=exec(...)` next to a
8
+ * script-locked `hq()` item and read the secret before the script binding is
9
+ * checked.
10
+ *
11
+ * So the --script path reads the schema and local files with this parser and
12
+ * never builds a varlock graph. Only these lines are accepted (allowlist):
13
+ * - blank lines and `#` comments. A comment may carry only the decorators
14
+ * listed in ALLOWED_DECORATORS; any other `@decorator` is refused.
15
+ * - `KEY=hq()` or `KEY=hq("NAME")` / `KEY=hq('NAME')`: an HQ secret.
16
+ * - `KEY=literal`, optionally quoted, with no `(`, `)`, `$`, `{`, `}` or
17
+ * backtick: a static value. An empty value is allowed.
18
+ * Everything else is refused with an error before any secret is fetched.
19
+ * Later files override earlier ones, as in the varlock path.
20
+ */
21
+ export interface StrictSchema {
22
+ statics: Record<string, string>;
23
+ /** Env key -> vault secret name. */
24
+ secrets: Map<string, string>;
25
+ }
26
+ export declare function parseStrictSchemaFiles(paths: string[]): StrictSchema;
27
+ //# sourceMappingURL=script-bound-schema.d.ts.map
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Strict env-schema reader for `hq run --script` (feedback_0e6abcb8).
3
+ *
4
+ * With --script, `hq run` must not load the caller's schema through varlock:
5
+ * a varlock graph can run `exec()` resolvers, load plugins, or import other
6
+ * files, and any of that runs inside the CLI process while the fetched
7
+ * secrets are in memory. A caller could write `LEAK=exec(...)` next to a
8
+ * script-locked `hq()` item and read the secret before the script binding is
9
+ * checked.
10
+ *
11
+ * So the --script path reads the schema and local files with this parser and
12
+ * never builds a varlock graph. Only these lines are accepted (allowlist):
13
+ * - blank lines and `#` comments. A comment may carry only the decorators
14
+ * listed in ALLOWED_DECORATORS; any other `@decorator` is refused.
15
+ * - `KEY=hq()` or `KEY=hq("NAME")` / `KEY=hq('NAME')`: an HQ secret.
16
+ * - `KEY=literal`, optionally quoted, with no `(`, `)`, `$`, `{`, `}` or
17
+ * backtick: a static value. An empty value is allowed.
18
+ * Everything else is refused with an error before any secret is fetched.
19
+ * Later files override earlier ones, as in the varlock path.
20
+ */
21
+ import * as fs from 'node:fs';
22
+ const KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;
23
+ const SECRET_NAME = /^[A-Z][A-Z0-9_]*(\/[A-Z][A-Z0-9_]+)*$/;
24
+ const HQ_CALL = /^hq\(\s*(?:(["'])([^"']*)\1)?\s*\)$/;
25
+ const UNSAFE_LITERAL = /[()${}`]/;
26
+ const ALLOWED_DECORATORS = new Set([
27
+ 'hqCompany',
28
+ 'required',
29
+ 'optional',
30
+ 'sensitive',
31
+ 'public',
32
+ 'defaultRequired',
33
+ 'defaultSensitive',
34
+ ]);
35
+ function refuse(file, lineNo, reason) {
36
+ throw new Error(`hq run --script reads ${file} in strict mode and refused line ${lineNo}: ${reason}. ` +
37
+ 'With --script only KEY=hq(...), KEY=literal and the decorators ' +
38
+ `${[...ALLOWED_DECORATORS].map((d) => '@' + d).join(', ')} are allowed; resolvers such as exec() and @plugin/@import are not. ` +
39
+ 'Remove them, or run without --script.');
40
+ }
41
+ function unquote(value) {
42
+ if (value.length >= 2) {
43
+ const q = value[0];
44
+ if ((q === '"' || q === "'") && value[value.length - 1] === q) {
45
+ const inner = value.slice(1, -1);
46
+ if (!inner.includes(q))
47
+ return inner;
48
+ }
49
+ }
50
+ return value;
51
+ }
52
+ export function parseStrictSchemaFiles(paths) {
53
+ const statics = {};
54
+ const secrets = new Map();
55
+ for (const file of paths) {
56
+ const lines = fs.readFileSync(file, 'utf8').split(/\r?\n/);
57
+ lines.forEach((raw, index) => {
58
+ const lineNo = index + 1;
59
+ const line = raw.trim();
60
+ if (line === '')
61
+ return;
62
+ if (line.startsWith('#')) {
63
+ for (const match of line.matchAll(/@([A-Za-z_][A-Za-z0-9_]*)/g)) {
64
+ if (!ALLOWED_DECORATORS.has(match[1]))
65
+ refuse(file, lineNo, `decorator @${match[1]} is not allowed`);
66
+ }
67
+ // An allowed decorator may take only a quoted literal argument.
68
+ const stripped = line.replace(/@hqCompany\(\s*"[^"()$`{}]*"\s*\)/g, '');
69
+ if (/[()]/.test(stripped) && /@/.test(stripped))
70
+ refuse(file, lineNo, 'decorator arguments are not allowed');
71
+ return;
72
+ }
73
+ const eq = line.indexOf('=');
74
+ if (eq <= 0)
75
+ refuse(file, lineNo, 'expected KEY=value');
76
+ const key = line.slice(0, eq).trim().replace(/^export\s+/, '');
77
+ if (!KEY.test(key))
78
+ refuse(file, lineNo, 'invalid key');
79
+ const value = line.slice(eq + 1).trim();
80
+ const hq = HQ_CALL.exec(value);
81
+ if (hq) {
82
+ const name = hq[2] || key;
83
+ if (!SECRET_NAME.test(name))
84
+ refuse(file, lineNo, `invalid secret name for ${key}`);
85
+ secrets.set(key, name);
86
+ delete statics[key];
87
+ return;
88
+ }
89
+ if (UNSAFE_LITERAL.test(value))
90
+ refuse(file, lineNo, `${key} is not a plain literal or hq() call`);
91
+ statics[key] = unquote(value);
92
+ secrets.delete(key);
93
+ });
94
+ }
95
+ return { statics, secrets };
96
+ }
97
+ //# sourceMappingURL=script-bound-schema.js.map
@@ -76,6 +76,8 @@ export declare function requestedApiKeyCompany(flagCompany: string | undefined):
76
76
  export declare function assertApiKeyCompany(token: string, requested: string, commandLabel: string): Promise<string>;
77
77
  export declare function loadRevealedSecrets(token: string, companyUid: string, keys: string[], usage?: SecretUsage, options?: {
78
78
  allowOfflineFallback?: boolean;
79
+ /** Filled with each returned row's scriptLock.mode (undefined when absent). */
80
+ lockModes?: Map<string, string | undefined>;
79
81
  }): Promise<Map<string, string>>;
80
82
  /**
81
83
  * Emit `hq secrets env` output and terminate deterministically.
@@ -129,7 +129,7 @@ export function parseSecretNameList(input) {
129
129
  // refuse a key bound to a different company. Without it the fetch route reads
130
130
  // from the key's own company, which made `--company <other>` silently return
131
131
  // the key company's same-named secret (P0 triage, 2026-10-11).
132
- async function loadRevealedSecretsViaApiKey(token, keys, companyUid) {
132
+ async function loadRevealedSecretsViaApiKey(token, keys, companyUid, lockModes) {
133
133
  const resolved = new Map();
134
134
  const requested = [...new Set(keys)];
135
135
  const cacheScope = "__api_key__";
@@ -170,6 +170,10 @@ async function loadRevealedSecretsViaApiKey(token, keys, companyUid) {
170
170
  throw new Error(`Failed to fetch secret '${name}': malformed fetch response`);
171
171
  }
172
172
  removeCacheEntry(cacheScope, name);
173
+ // The API-key fetch route returns the secret's scriptLock (the vault denies
174
+ // an enforced secret on this route, since it carries no script usage).
175
+ const mode = secret.scriptLock?.mode;
176
+ lockModes?.set(name, typeof mode === "string" ? mode : undefined);
173
177
  resolved.set(name, secret.value);
174
178
  }
175
179
  return resolved;
@@ -219,7 +223,7 @@ export async function assertApiKeyCompany(token, requested, commandLabel) {
219
223
  }
220
224
  export async function loadRevealedSecrets(token, companyUid, keys, usage, options = {}) {
221
225
  if (token.startsWith(HQ_API_KEY_PREFIX)) {
222
- return loadRevealedSecretsViaApiKey(token, keys, companyUid);
226
+ return loadRevealedSecretsViaApiKey(token, keys, companyUid, options.lockModes);
223
227
  }
224
228
  const resolved = new Map();
225
229
  const requested = [...new Set(keys)];
@@ -284,6 +288,7 @@ export async function loadRevealedSecrets(token, companyUid, keys, usage, option
284
288
  throw new Error("Invalid secret load response from vault");
285
289
  }
286
290
  seenNames.add(s.name);
291
+ options.lockModes?.set(s.name, s.scriptLock?.mode);
287
292
  if (s.revokedAt != null) {
288
293
  errorsByName.set(s.name, {
289
294
  code: "revoked",
@@ -19,11 +19,12 @@
19
19
  * HQ_VAULT_API_URL — vault-service API Gateway URL
20
20
  */
21
21
  import { isExpiring, isMachineIdentity, type CognitoAuthConfig, type CognitoTokens, type ClientInfo, type VaultServiceConfig } from "@indigoai-us/hq-cloud";
22
+ import { hasPersonCachedSession, isMachineCredsFileEnvSet, wantsMachineCognitoTokens, type CognitoTokenSource } from "./machine-principal-state.js";
22
23
  import { loadCachedTokens, personTokenCacheFile, readPersonTokenFile } from "./person-token-cache.js";
23
24
  export { loadCachedTokens, personTokenCacheFile, readPersonTokenFile };
24
25
  export { isExpiring, isMachineIdentity };
25
- /** Person browser/login cache vs machine-creds USER_PASSWORD_AUTH mint. */
26
- export type CognitoTokenSource = "person" | "machine";
26
+ export { hasPersonCachedSession, isMachineCredsFileEnvSet, wantsMachineCognitoTokens };
27
+ export type { CognitoTokenSource };
27
28
  /** Actor kind derived from the active token / machine-creds signal. */
28
29
  export type CognitoActorKind = "human" | "agent" | "unknown";
29
30
  /**
@@ -97,13 +98,6 @@ export declare const DEFAULT_HQ_ROOT: string;
97
98
  export declare function machineTokenStateDir(home?: string, env?: NodeJS.ProcessEnv): string;
98
99
  /** Absolute path of the machine-token cache file (daemon state dir). */
99
100
  export declare function machineTokenCacheFile(home?: string, env?: NodeJS.ProcessEnv): string;
100
- /**
101
- * True when `HQ_MACHINE_CREDS_FILE` is explicitly set (non-empty) in `env`.
102
- * Explicit env remains the primary opt-in for daemon units. Fresh agent boxes
103
- * without the env still mint via {@link wantsMachineCognitoTokens}'s
104
- * default-file fallback when no usable person session exists.
105
- */
106
- export declare function isMachineCredsFileEnvSet(env?: NodeJS.ProcessEnv): boolean;
107
101
  /** Default machine-creds path hq-cloud also checks when the env is unset. */
108
102
  export declare function defaultMachineCredsFile(home?: string): string;
109
103
  export type EnsureCognitoTokenOptions = {
@@ -127,33 +121,12 @@ export type EnsureCognitoTokenOptions = {
127
121
  export declare const HQ_NO_BROWSER_LOGIN_ENV = "HQ_NO_BROWSER_LOGIN";
128
122
  /** An explicit `interactive` option wins; otherwise interactive unless the env switch is "1". */
129
123
  export declare function resolveInteractiveLogin(options: Pick<EnsureCognitoTokenOptions, "interactive">, env?: NodeJS.ProcessEnv): boolean;
130
- /**
131
- * True when a usable person Cognito cache file exists under the effective
132
- * `HQ_STATE_DIR` (or `~/.hq`). Presence alone keeps human commands on the
133
- * person path even when the default machine-creds file is also on disk
134
- * (no-flip guarantee).
135
- */
136
- export declare function hasPersonCachedSession(env?: NodeJS.ProcessEnv): boolean;
137
124
  /**
138
125
  * Fresh-box / Outpost bootstrap: env unset, not forced to person, no person
139
126
  * cache, and the default machine-creds file is readable. Distinct from
140
127
  * explicit `HQ_MACHINE_CREDS_FILE` / `{ tokenSource: "machine" }` opt-in.
141
128
  */
142
129
  export declare function isDefaultMachineCredsFallback(options?: Pick<EnsureCognitoTokenOptions, "tokenSource">, env?: NodeJS.ProcessEnv): boolean;
143
- /**
144
- * Whether this call should mint/cache via machine creds (USER_PASSWORD_AUTH).
145
- *
146
- * Machine path when:
147
- * - explicit `{ tokenSource: "machine" }` and creds are readable, or
148
- * - `HQ_MACHINE_CREDS_FILE` is set and readable, or
149
- * - fresh-box fallback: env unset, no person cache, default
150
- * `~/.hq-agent/machine-creds.json` readable.
151
- *
152
- * Explicit `{ tokenSource: "person" }` never uses machine creds. A present
153
- * person cache keeps the person path even when the default creds file exists
154
- * (do not flip a human's commands onto machine tokens).
155
- */
156
- export declare function wantsMachineCognitoTokens(options?: Pick<EnsureCognitoTokenOptions, "tokenSource">, env?: NodeJS.ProcessEnv): boolean;
157
130
  /**
158
131
  * Which Cognito token source this process will use for doctor / status / whoami.
159
132
  * Matches {@link wantsMachineCognitoTokens}: env opt-in, or default-file
@@ -26,6 +26,7 @@ import * as path from "path";
26
26
  import * as yaml from "js-yaml";
27
27
  import chalk from "chalk";
28
28
  import { isExpiring, refreshTokens, saveCachedTokens, browserLogin, detectHqCoreVersion, isMachineIdentity, getValidMachineTokens, CognitoRefreshError, accessTokenFingerprint, invalidateCachedTokensByFingerprint, } from "@indigoai-us/hq-cloud";
29
+ import { hasPersonCachedSession, isMachineCredsFileEnvSet, probeMachineIdentity, wantsMachineCognitoTokens, } from "./machine-principal-state.js";
29
30
  import { CLI_NAME, CLI_VERSION } from "../cli-version.js";
30
31
  import { daemonDir } from "../lib/mesh/live/daemon/paths.js";
31
32
  import { workMeshRoot } from "../lib/mesh/live/paths.js";
@@ -36,6 +37,7 @@ export { loadCachedTokens, personTokenCacheFile, readPersonTokenFile };
36
37
  // Re-export the cached-session primitives for callers that must deliberately
37
38
  // avoid the refresh/login behavior in the higher-level helpers below.
38
39
  export { isExpiring, isMachineIdentity };
40
+ export { hasPersonCachedSession, isMachineCredsFileEnvSet, wantsMachineCognitoTokens };
39
41
  /**
40
42
  * Override for where machine-minted Cognito tokens are cached.
41
43
  * Default: the work-mesh daemon state dir (`~/.hq/work-mesh/daemon`), so a
@@ -253,16 +255,6 @@ export function machineTokenStateDir(home = os.homedir(), env = process.env) {
253
255
  export function machineTokenCacheFile(home = os.homedir(), env = process.env) {
254
256
  return path.join(machineTokenStateDir(home, env), "cognito-tokens.json");
255
257
  }
256
- /**
257
- * True when `HQ_MACHINE_CREDS_FILE` is explicitly set (non-empty) in `env`.
258
- * Explicit env remains the primary opt-in for daemon units. Fresh agent boxes
259
- * without the env still mint via {@link wantsMachineCognitoTokens}'s
260
- * default-file fallback when no usable person session exists.
261
- */
262
- export function isMachineCredsFileEnvSet(env = process.env) {
263
- const raw = env.HQ_MACHINE_CREDS_FILE;
264
- return typeof raw === "string" && raw.trim().length > 0;
265
- }
266
258
  /** Default machine-creds path hq-cloud also checks when the env is unset. */
267
259
  export function defaultMachineCredsFile(home = os.homedir()) {
268
260
  return path.join(home, ".hq-agent", "machine-creds.json");
@@ -279,69 +271,6 @@ export function resolveInteractiveLogin(options, env = process.env) {
279
271
  return options.interactive;
280
272
  return env[HQ_NO_BROWSER_LOGIN_ENV]?.trim() !== "1";
281
273
  }
282
- /**
283
- * Align `process.env.HQ_MACHINE_CREDS_FILE` with a (possibly synthetic) `env`
284
- * for the duration of `fn`, so hq-cloud's `isMachineIdentity()` sees the same
285
- * value doctor / tests intended.
286
- */
287
- function withAlignedMachineCredsEnv(env, fn) {
288
- const prevCreds = process.env.HQ_MACHINE_CREDS_FILE;
289
- const nextCreds = env.HQ_MACHINE_CREDS_FILE;
290
- if (nextCreds !== prevCreds) {
291
- if (nextCreds === undefined || String(nextCreds).trim() === "") {
292
- delete process.env.HQ_MACHINE_CREDS_FILE;
293
- }
294
- else {
295
- process.env.HQ_MACHINE_CREDS_FILE = nextCreds;
296
- }
297
- }
298
- try {
299
- return fn();
300
- }
301
- finally {
302
- if (nextCreds !== prevCreds) {
303
- if (prevCreds === undefined)
304
- delete process.env.HQ_MACHINE_CREDS_FILE;
305
- else
306
- process.env.HQ_MACHINE_CREDS_FILE = prevCreds;
307
- }
308
- }
309
- }
310
- /**
311
- * True when a usable person Cognito cache file exists under the effective
312
- * `HQ_STATE_DIR` (or `~/.hq`). Presence alone keeps human commands on the
313
- * person path even when the default machine-creds file is also on disk
314
- * (no-flip guarantee).
315
- */
316
- export function hasPersonCachedSession(env = process.env) {
317
- try {
318
- const file = personTokenCacheFile(os.homedir(), env);
319
- const raw = readPersonTokenFile(os.homedir(), env);
320
- if (!raw ||
321
- typeof raw.accessToken !== "string" ||
322
- raw.accessToken.length === 0 ||
323
- typeof raw.idToken !== "string" ||
324
- raw.idToken.length === 0) {
325
- return false;
326
- }
327
- // Definitive refresh rejection writes `${file}.invalid.<fingerprint>`;
328
- // treat that as no usable person session so status/whoami report machine.
329
- const marker = `${file}.invalid.${accessTokenFingerprint(raw.accessToken)}`;
330
- if (fs.existsSync(marker))
331
- return false;
332
- return true;
333
- }
334
- catch {
335
- return false;
336
- }
337
- }
338
- /**
339
- * Whether hq-cloud currently sees readable machine creds (env override or
340
- * default `~/.hq-agent/machine-creds.json`), honoring a synthetic `env`.
341
- */
342
- function probeMachineIdentity(env) {
343
- return withAlignedMachineCredsEnv(env, () => isMachineIdentity());
344
- }
345
274
  /**
346
275
  * Fresh-box / Outpost bootstrap: env unset, not forced to person, no person
347
276
  * cache, and the default machine-creds file is readable. Distinct from
@@ -361,33 +290,6 @@ export function isDefaultMachineCredsFallback(options = {}, env = process.env) {
361
290
  function logDefaultMachineCredsFallback() {
362
291
  console.error(chalk.dim("machine identity from default creds file"));
363
292
  }
364
- /**
365
- * Whether this call should mint/cache via machine creds (USER_PASSWORD_AUTH).
366
- *
367
- * Machine path when:
368
- * - explicit `{ tokenSource: "machine" }` and creds are readable, or
369
- * - `HQ_MACHINE_CREDS_FILE` is set and readable, or
370
- * - fresh-box fallback: env unset, no person cache, default
371
- * `~/.hq-agent/machine-creds.json` readable.
372
- *
373
- * Explicit `{ tokenSource: "person" }` never uses machine creds. A present
374
- * person cache keeps the person path even when the default creds file exists
375
- * (do not flip a human's commands onto machine tokens).
376
- */
377
- export function wantsMachineCognitoTokens(options = {}, env = process.env) {
378
- if (options.tokenSource === "person")
379
- return false;
380
- if (options.tokenSource === "machine") {
381
- return probeMachineIdentity(env);
382
- }
383
- if (isMachineCredsFileEnvSet(env)) {
384
- return probeMachineIdentity(env);
385
- }
386
- // Fresh-box fallback — default creds file only when no person session.
387
- if (hasPersonCachedSession(env))
388
- return false;
389
- return probeMachineIdentity(env);
390
- }
391
293
  /**
392
294
  * Which Cognito token source this process will use for doctor / status / whoami.
393
295
  * Matches {@link wantsMachineCognitoTokens}: env opt-in, or default-file
@@ -0,0 +1,38 @@
1
+ /** Person browser/login cache vs machine-creds USER_PASSWORD_AUTH mint. */
2
+ export type CognitoTokenSource = "person" | "machine";
3
+ /**
4
+ * True when `HQ_MACHINE_CREDS_FILE` is explicitly set (non-empty) in `env`.
5
+ * Explicit env remains the primary opt-in for daemon units. Fresh agent boxes
6
+ * without the env still mint via {@link wantsMachineCognitoTokens}'s
7
+ * default-file fallback when no usable person session exists.
8
+ */
9
+ export declare function isMachineCredsFileEnvSet(env?: NodeJS.ProcessEnv): boolean;
10
+ /**
11
+ * True when a usable person Cognito cache file exists under the effective
12
+ * `HQ_STATE_DIR` (or `~/.hq`). Presence alone keeps human commands on the
13
+ * person path even when the default machine-creds file is also on disk
14
+ * (no-flip guarantee).
15
+ */
16
+ export declare function hasPersonCachedSession(env?: NodeJS.ProcessEnv): boolean;
17
+ /**
18
+ * Whether hq-cloud currently sees readable machine creds (env override or
19
+ * default `~/.hq-agent/machine-creds.json`), honoring a synthetic `env`.
20
+ */
21
+ export declare function probeMachineIdentity(env: NodeJS.ProcessEnv): boolean;
22
+ /**
23
+ * Whether this call should mint/cache via machine creds (USER_PASSWORD_AUTH).
24
+ *
25
+ * Machine path when:
26
+ * - explicit `{ tokenSource: "machine" }` and creds are readable, or
27
+ * - `HQ_MACHINE_CREDS_FILE` is set and readable, or
28
+ * - fresh-box fallback: env unset, no person cache, default
29
+ * `~/.hq-agent/machine-creds.json` readable.
30
+ *
31
+ * Explicit `{ tokenSource: "person" }` never uses machine creds. A present
32
+ * person cache keeps the person path even when the default creds file exists
33
+ * (do not flip a human's commands onto machine tokens).
34
+ */
35
+ export declare function wantsMachineCognitoTokens(options?: {
36
+ tokenSource?: CognitoTokenSource;
37
+ }, env?: NodeJS.ProcessEnv): boolean;
38
+ //# sourceMappingURL=machine-principal-state.d.ts.map