@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,110 @@
1
+ /**
2
+ * Local-state checks that decide machine vs person Cognito tokens. Reads env
3
+ * and files only; no network code, so fast native entries can import it
4
+ * (e.g. via src/lib/caller-principal.ts) without pulling cognito-session.ts.
5
+ */
6
+ import * as os from "node:os";
7
+ import * as fs from "node:fs";
8
+ import { accessTokenFingerprint, isMachineIdentity } from "@indigoai-us/hq-cloud";
9
+ import { personTokenCacheFile, readPersonTokenFile } from "./person-token-cache.js";
10
+ /**
11
+ * True when `HQ_MACHINE_CREDS_FILE` is explicitly set (non-empty) in `env`.
12
+ * Explicit env remains the primary opt-in for daemon units. Fresh agent boxes
13
+ * without the env still mint via {@link wantsMachineCognitoTokens}'s
14
+ * default-file fallback when no usable person session exists.
15
+ */
16
+ export function isMachineCredsFileEnvSet(env = process.env) {
17
+ const raw = env.HQ_MACHINE_CREDS_FILE;
18
+ return typeof raw === "string" && raw.trim().length > 0;
19
+ }
20
+ /**
21
+ * Align `process.env.HQ_MACHINE_CREDS_FILE` with a (possibly synthetic) `env`
22
+ * for the duration of `fn`, so hq-cloud's `isMachineIdentity()` sees the same
23
+ * value doctor / tests intended.
24
+ */
25
+ function withAlignedMachineCredsEnv(env, fn) {
26
+ const prevCreds = process.env.HQ_MACHINE_CREDS_FILE;
27
+ const nextCreds = env.HQ_MACHINE_CREDS_FILE;
28
+ if (nextCreds !== prevCreds) {
29
+ if (nextCreds === undefined || String(nextCreds).trim() === "") {
30
+ delete process.env.HQ_MACHINE_CREDS_FILE;
31
+ }
32
+ else {
33
+ process.env.HQ_MACHINE_CREDS_FILE = nextCreds;
34
+ }
35
+ }
36
+ try {
37
+ return fn();
38
+ }
39
+ finally {
40
+ if (nextCreds !== prevCreds) {
41
+ if (prevCreds === undefined)
42
+ delete process.env.HQ_MACHINE_CREDS_FILE;
43
+ else
44
+ process.env.HQ_MACHINE_CREDS_FILE = prevCreds;
45
+ }
46
+ }
47
+ }
48
+ /**
49
+ * True when a usable person Cognito cache file exists under the effective
50
+ * `HQ_STATE_DIR` (or `~/.hq`). Presence alone keeps human commands on the
51
+ * person path even when the default machine-creds file is also on disk
52
+ * (no-flip guarantee).
53
+ */
54
+ export function hasPersonCachedSession(env = process.env) {
55
+ try {
56
+ const file = personTokenCacheFile(os.homedir(), env);
57
+ const raw = readPersonTokenFile(os.homedir(), env);
58
+ if (!raw ||
59
+ typeof raw.accessToken !== "string" ||
60
+ raw.accessToken.length === 0 ||
61
+ typeof raw.idToken !== "string" ||
62
+ raw.idToken.length === 0) {
63
+ return false;
64
+ }
65
+ // Definitive refresh rejection writes `${file}.invalid.<fingerprint>`;
66
+ // treat that as no usable person session so status/whoami report machine.
67
+ const marker = `${file}.invalid.${accessTokenFingerprint(raw.accessToken)}`;
68
+ if (fs.existsSync(marker))
69
+ return false;
70
+ return true;
71
+ }
72
+ catch {
73
+ return false;
74
+ }
75
+ }
76
+ /**
77
+ * Whether hq-cloud currently sees readable machine creds (env override or
78
+ * default `~/.hq-agent/machine-creds.json`), honoring a synthetic `env`.
79
+ */
80
+ export function probeMachineIdentity(env) {
81
+ return withAlignedMachineCredsEnv(env, () => isMachineIdentity());
82
+ }
83
+ /**
84
+ * Whether this call should mint/cache via machine creds (USER_PASSWORD_AUTH).
85
+ *
86
+ * Machine path when:
87
+ * - explicit `{ tokenSource: "machine" }` and creds are readable, or
88
+ * - `HQ_MACHINE_CREDS_FILE` is set and readable, or
89
+ * - fresh-box fallback: env unset, no person cache, default
90
+ * `~/.hq-agent/machine-creds.json` readable.
91
+ *
92
+ * Explicit `{ tokenSource: "person" }` never uses machine creds. A present
93
+ * person cache keeps the person path even when the default creds file exists
94
+ * (do not flip a human's commands onto machine tokens).
95
+ */
96
+ export function wantsMachineCognitoTokens(options = {}, env = process.env) {
97
+ if (options.tokenSource === "person")
98
+ return false;
99
+ if (options.tokenSource === "machine") {
100
+ return probeMachineIdentity(env);
101
+ }
102
+ if (isMachineCredsFileEnvSet(env)) {
103
+ return probeMachineIdentity(env);
104
+ }
105
+ // Fresh-box fallback — default creds file only when no person session.
106
+ if (hasPersonCachedSession(env))
107
+ return false;
108
+ return probeMachineIdentity(env);
109
+ }
110
+ //# sourceMappingURL=machine-principal-state.js.map
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Plain-language rendering for hq-cloud's `RemoteListNotConvergingError`
3
+ * (code `REMOTE_LIST_NOT_CONVERGING`, hq-cloud >= 6.18.102). hq-cloud throws
4
+ * it when a company's vault file listing keeps stopping early (a repeated
5
+ * cursor, or a truncated page with no cursor) after its retries run out.
6
+ *
7
+ * Duck-typed on `code` so hq-cli stays compatible while hq-cloud and the CLI
8
+ * are released independently.
9
+ */
10
+ export declare const REMOTE_LIST_NOT_CONVERGING_CODE = "REMOTE_LIST_NOT_CONVERGING";
11
+ export declare const REMOTE_LIST_NOT_CONVERGING_HINT: string;
12
+ export declare function isRemoteListNotConvergingError(err: unknown): boolean;
13
+ /**
14
+ * Returns the user-facing message for a non-converging remote listing, with
15
+ * the original engine detail on a second line for bug reports, or null when
16
+ * `err` is some other failure.
17
+ */
18
+ export declare function formatRemoteListNotConverging(err: unknown): string | null;
19
+ /** Remote-list hint when it applies, else the raw error text. */
20
+ export declare function describeSyncError(err: unknown): string;
21
+ //# sourceMappingURL=remote-list-not-converging.d.ts.map
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Plain-language rendering for hq-cloud's `RemoteListNotConvergingError`
3
+ * (code `REMOTE_LIST_NOT_CONVERGING`, hq-cloud >= 6.18.102). hq-cloud throws
4
+ * it when a company's vault file listing keeps stopping early (a repeated
5
+ * cursor, or a truncated page with no cursor) after its retries run out.
6
+ *
7
+ * Duck-typed on `code` so hq-cli stays compatible while hq-cloud and the CLI
8
+ * are released independently.
9
+ */
10
+ export const REMOTE_LIST_NOT_CONVERGING_CODE = "REMOTE_LIST_NOT_CONVERGING";
11
+ export const REMOTE_LIST_NOT_CONVERGING_HINT = "HQ could not finish listing this company's files. Try hq sync now again in a minute; " +
12
+ "if it keeps happening, run hq feedback bug.";
13
+ export function isRemoteListNotConvergingError(err) {
14
+ return (typeof err === "object" &&
15
+ err !== null &&
16
+ err.code === REMOTE_LIST_NOT_CONVERGING_CODE);
17
+ }
18
+ /**
19
+ * Returns the user-facing message for a non-converging remote listing, with
20
+ * the original engine detail on a second line for bug reports, or null when
21
+ * `err` is some other failure.
22
+ */
23
+ export function formatRemoteListNotConverging(err) {
24
+ if (!isRemoteListNotConvergingError(err))
25
+ return null;
26
+ const detail = err instanceof Error
27
+ ? err.message
28
+ : typeof err.message === "string"
29
+ ? err.message
30
+ : "";
31
+ return detail.length > 0
32
+ ? `${REMOTE_LIST_NOT_CONVERGING_HINT}\nDetail: ${detail}`
33
+ : REMOTE_LIST_NOT_CONVERGING_HINT;
34
+ }
35
+ /** Remote-list hint when it applies, else the raw error text. */
36
+ export function describeSyncError(err) {
37
+ return (formatRemoteListNotConverging(err) ??
38
+ (err instanceof Error ? err.message : String(err)));
39
+ }
40
+ //# sourceMappingURL=remote-list-not-converging.js.map
@@ -284,6 +284,9 @@ export function removeLinkOnly(linkPath) {
284
284
  if (!st.isSymbolicLink()) {
285
285
  throw new Error(`Refusing to remove ${linkPath}: it is not a link.`);
286
286
  }
287
+ // Re-check immediately before each removal call, so an entry swapped for a
288
+ // real file or folder after the first check is refused, not removed.
289
+ assertStillLink(linkPath);
287
290
  try {
288
291
  fs.unlinkSync(linkPath);
289
292
  }
@@ -293,8 +296,14 @@ export function removeLinkOnly(linkPath) {
293
296
  // unlink(). A non-recursive rmdir removes only the junction entry.
294
297
  if (code !== 'EPERM' && code !== 'EISDIR')
295
298
  throw err;
299
+ assertStillLink(linkPath);
296
300
  fs.rmdirSync(linkPath);
297
301
  }
298
302
  return true;
299
303
  }
304
+ function assertStillLink(linkPath) {
305
+ if (!fs.lstatSync(linkPath).isSymbolicLink()) {
306
+ throw new Error(`Refusing to remove ${linkPath}: it is no longer a link.`);
307
+ }
308
+ }
300
309
  //# sourceMappingURL=safe-link.js.map
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Bind a script-locked secret release to the process that is actually run
3
+ * (feedback_0e6abcb8).
4
+ *
5
+ * `--script <path>` sends the script's path and hash to the vault, which
6
+ * releases a script-locked secret when that pair is approved. Before this
7
+ * module the command after `--` was never compared with that script, so any
8
+ * command that named an approved script received the secret.
9
+ *
10
+ * When any requested secret is script-locked (or its lock state is unknown),
11
+ * the command after `--` must be one of:
12
+ * 1. the approved script itself: `-- ./approved.sh [args...]` (realpath
13
+ * match). Its `#!` line is parsed and the interpreter it names goes
14
+ * through the same allowlist; `#!/usr/bin/env X` resolves X against a
15
+ * fixed system PATH, never the caller's PATH;
16
+ * 2. an allowlisted interpreter followed directly by the script:
17
+ * `-- bash approved.sh [args...]`, also resolved against the fixed PATH.
18
+ * Anything else (wrappers such as env, xargs, sudo, a shell `-c` string, an
19
+ * interpreter option before the script) is refused and nothing is injected.
20
+ *
21
+ * Allowlisted interpreters: sh, dash, ksh, bash (run with --noprofile
22
+ * --norc), zsh (run with -f), python3/python (run with -I -S), node. Ruby and
23
+ * perl are not allowlisted. The interpreter's realpath and every parent
24
+ * directory must be owned by root and not group- or world-writable, so
25
+ * Homebrew or other user-writable installs are refused.
26
+ *
27
+ * At spawn time the script bytes are read once, hashed, compared with the hash
28
+ * sent to the vault, written to a fresh 0700 private directory, re-hashed, and
29
+ * that copy is passed to the interpreter. The original path is exposed to the
30
+ * script as HQ_APPROVED_SCRIPT and its directory as HQ_APPROVED_SCRIPT_DIR.
31
+ * `$0` / `__file__` / `__dirname` name the copy, so a script that loads
32
+ * neighbour files relative to itself must use HQ_APPROVED_SCRIPT_DIR. The
33
+ * working directory is left as the caller's.
34
+ *
35
+ * The child environment gets a fixed system PATH and loses the variables that
36
+ * make an interpreter, the loader or the macOS xcrun shims run other code.
37
+ * The same names are refused when they arrive in the injected secrets or in
38
+ * an env-schema / env-local value, because those are merged last.
39
+ *
40
+ * The child environment is built from an allowlist (see scriptBoundChildEnv),
41
+ * not inherited from the caller.
42
+ *
43
+ * What the binding does and does not protect: it controls which entrypoint
44
+ * receives the secret. It does not isolate what that entrypoint runs. A
45
+ * same-user caller can still influence tools the script calls through files
46
+ * in HOME (for example ~/.gitconfig and fsmonitor hooks, ~/.curlrc,
47
+ * ~/.ssh/config, ~/.netrc) or through anything else the user can write. Real
48
+ * isolation needs the vault-side sandbox release (`hq secrets sandbox`).
49
+ *
50
+ * Remaining limits, documented on purpose:
51
+ * - Only the script file is hashed. Files it sources, requires or imports
52
+ * (neighbour files, modules) are not covered by the approval.
53
+ * - A process running as the same user can still race the private copy
54
+ * between its re-hash and the interpreter opening it. The window is short
55
+ * and the path is random, but same-user code is not a boundary this
56
+ * client-side check can enforce.
57
+ * - The check runs in the CLI. A modified CLI, or a direct call to the vault
58
+ * API with a self-asserted hash, is outside what it can stop.
59
+ */
60
+ export declare class ScriptBindingError extends Error {
61
+ constructor(message: string);
62
+ }
63
+ /** Root-owned search path used for interpreters and for the child. */
64
+ export declare const FIXED_SYSTEM_PATH = "/usr/bin:/bin:/usr/sbin:/sbin";
65
+ export interface ScriptBoundCommand {
66
+ scriptRealPath: string;
67
+ interpreterPath: string;
68
+ interpreterArgs: string[];
69
+ scriptArgs: string[];
70
+ /** sha256 of the script when the command was resolved. */
71
+ sha256: string;
72
+ }
73
+ export interface PreparedBoundSpawn {
74
+ file: string;
75
+ args: string[];
76
+ copyPath: string;
77
+ cleanup: () => void;
78
+ }
79
+ /** True for a variable a bound script's environment must not take from the caller. */
80
+ export declare function isDeniedEnvName(name: string): boolean;
81
+ /**
82
+ * Schema / local env literal names a bound `hq run --script` launch accepts
83
+ * (fifth review). Literals are caller-written, so instead of denying
84
+ * tool-config names one by one, only these app-level switches are allowed.
85
+ * Everything else must come from the vault through hq().
86
+ */
87
+ export declare const SAFE_LITERAL_NAMES: Set<string>;
88
+ /** Refuse schema literals that are not on SAFE_LITERAL_NAMES (or are denied). */
89
+ export declare function assertLiteralNamesAllowed(names: Iterable<string>): void;
90
+ /**
91
+ * Design (feedback_0e6abcb8, fourth review): an injected name is accepted in a
92
+ * bound launch only when it is not on the loader/interpreter denylist, not
93
+ * one of the inherited allowlist names (HOME, USER, LANG, LC_*, TZ, TMPDIR,
94
+ * ...), and not on the explicit dangerous list above. Ordinary application
95
+ * names (FAKE_API_KEY, DATABASE_URL, NODE_ENV) pass.
96
+ */
97
+ export declare function isDeniedInjectedName(name: string): boolean;
98
+ /** Refuse a bound launch whose injected values use a denied name. */
99
+ export declare function assertInjectedNamesAllowed(names: Iterable<string>): void;
100
+ export declare function privateCopyRoot(): string;
101
+ /**
102
+ * True when realPath and every parent directory are owned by root and not
103
+ * group- or world-writable.
104
+ */
105
+ export declare function isRootOwnedChain(realPath: string): boolean;
106
+ /**
107
+ * Resolve the command after `--` against the approved script. Throws
108
+ * ScriptBindingError when the command is not exactly the script invocation.
109
+ */
110
+ export declare function resolveScriptBoundCommand(scriptPath: string, childArgs: string[]): Promise<ScriptBoundCommand>;
111
+ /** Compare the script on disk with the hash sent to the vault. */
112
+ export declare function assertScriptUnchanged(bound: ScriptBoundCommand, attestedSha256: string | undefined): Promise<void>;
113
+ /**
114
+ * Read the script once, check its hash, copy those bytes into a fresh 0700
115
+ * directory, re-hash the copy, and return the spawn target that runs the copy.
116
+ */
117
+ export declare function prepareBoundSpawn(bound: ScriptBoundCommand, attestedSha256: string | undefined): PreparedBoundSpawn;
118
+ /**
119
+ * Remove the private copy when the parent gets SIGINT, SIGTERM or SIGHUP, or
120
+ * exits, then re-raise the signal so the default termination still happens.
121
+ * SIGKILL cannot be handled; a copy left by it stays in the 0700 temp
122
+ * directory until the OS cleans the temp dir.
123
+ */
124
+ export declare function cleanupOnParentSignals(cleanup: () => void, proc?: Pick<NodeJS.Process, "once" | "removeListener" | "kill" | "pid">): () => void;
125
+ /**
126
+ * Child environment for a bound script, built from an allowlist: HOME, USER,
127
+ * LOGNAME, LANG, LC_*, TERM, TZ and TMPDIR from the caller, a fixed PATH, and
128
+ * the HQ_APPROVED_* variables. Injected secrets and schema values are added by
129
+ * the caller after a denylist check.
130
+ */
131
+ export declare function scriptBoundChildEnv(env: NodeJS.ProcessEnv, scriptRealPath?: string): NodeJS.ProcessEnv;
132
+ /**
133
+ * Whether the release must be bound to the script. Only rows the vault marked
134
+ * `scriptLock.mode: "off"` are exempt; enforced or unknown fails closed.
135
+ */
136
+ export declare function requiresScriptBinding(keys: string[], lockModes: Map<string, string | undefined>): boolean;
137
+ export interface ScriptBindingPlan {
138
+ bound?: ScriptBoundCommand;
139
+ error?: ScriptBindingError;
140
+ }
141
+ /**
142
+ * Resolve the binding before any fetch. A refusal is held, not thrown, so a
143
+ * caller whose secrets turn out not to be script-locked keeps working.
144
+ */
145
+ export declare function planScriptBinding(scriptPath: string | undefined, childArgs: string[]): Promise<ScriptBindingPlan | undefined>;
146
+ export interface ChildLaunch {
147
+ file: string;
148
+ args: string[];
149
+ env: NodeJS.ProcessEnv;
150
+ cleanup: () => void;
151
+ }
152
+ /**
153
+ * Pick what to spawn once the vault has answered. Unlocked secrets keep the
154
+ * caller's command unchanged; enforced or unknown lock state requires the
155
+ * bound script and throws ScriptBindingError otherwise.
156
+ */
157
+ export declare function selectChildLaunch(plan: ScriptBindingPlan | undefined, keys: string[], lockModes: Map<string, string | undefined>, attestedSha256: string | undefined, childArgs: string[], env: NodeJS.ProcessEnv, injected: Record<string, string>): ChildLaunch;
158
+ //# sourceMappingURL=script-bound-exec.d.ts.map