@starci/hfs 4.8.0 → 4.9.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 +6 -0
- package/emit/operation-schemas.mjs +3 -0
- package/lint/bound-sys.cjs +2 -2
- package/package.json +1 -1
- package/runtime/engine/admission.mjs +1 -1
- package/runtime/engine/config.mjs +2 -2
- package/runtime/engine/db/machine.mjs +2 -3
- package/runtime/engine/invalid-config.mjs +1 -1
- package/runtime/knowledge/hfs/canon-pins.yaml +11 -10
- package/runtime/knowledge/hfs/rules.yaml +23 -0
- package/runtime/modules/kernel/failure-codes.yaml +10 -0
- package/runtime/scripts/guards/git-policy.mjs +2 -3
- package/runtime/scripts/hfs/rules/pipeline.mjs +5 -5
- package/runtime/scripts/lib/package-at.mjs +5 -6
- package/runtime/scripts/lib/roots.mjs +2 -2
- package/runtime/scripts/work/draw/draw-dna-findings.mjs +23 -12
- package/runtime/scripts/work/draw/draw-dna.mjs +14 -8
- package/scaffold/app.mjs +4 -4
- package/src/main.mjs +3 -3
- package/sync/index.mjs +14 -1
- package/sync/managed.mjs +3 -2
- package/templates/app/codegen-graphql.mjs.partial +4 -4
- package/templates/app/quality-config/sonar-project.properties +4 -2
- package/runtime/engine/canonical-json.mjs +0 -14
- package/runtime/engine/db/ledger-open.mjs +0 -38
- package/runtime/engine/db/ledger-paths.mjs +0 -87
- package/runtime/engine/db/ledger.mjs +0 -1068
- package/runtime/engine/db/provision-authority.mjs +0 -76
- package/runtime/engine/db/provision-operations.mjs +0 -146
- package/runtime/packages/grammar/scripts/build-stamp.mjs +0 -98
- package/runtime/scripts/agent/codex-app-server.mjs +0 -79
- package/runtime/scripts/agent/codex-mirror-source.mjs +0 -34
- package/runtime/scripts/agent/host-agents.mjs +0 -80
- package/runtime/scripts/agent/launch-trust-policy.mjs +0 -53
- package/runtime/scripts/agent/trust-launch.mjs +0 -191
- package/runtime/scripts/agent/trust.mjs +0 -523
- package/runtime/scripts/api/fs/temp-path.mjs +0 -8
- package/runtime/scripts/api/git/diff-names.mjs +0 -8
- package/runtime/scripts/api/git/head-time.mjs +0 -10
- package/runtime/scripts/api/git/index-lock.mjs +0 -78
- package/runtime/scripts/api/git/log.mjs +0 -5
- package/runtime/scripts/api/git/merge-tree.mjs +0 -5
- package/runtime/scripts/api/git/rev-list.mjs +0 -5
- package/runtime/scripts/api/git/rev-parse.mjs +0 -13
- package/runtime/scripts/api/git/status-query.mjs +0 -5
- package/runtime/scripts/api/git/worktree-list-query.mjs +0 -5
- package/runtime/scripts/api/node/spawn-node.mjs +0 -6
- package/runtime/scripts/api/npm/dist-tag.mjs +0 -8
- package/runtime/scripts/api/npm/lib.mjs +0 -32
- package/runtime/scripts/api/npm/pack-dry-run.mjs +0 -9
- package/runtime/scripts/api/npm/run-npm.mjs +0 -6
- package/runtime/scripts/api/npm/view.mjs +0 -30
- package/runtime/scripts/api/npm/whoami.mjs +0 -8
- package/runtime/scripts/api/process/hide-child-windows.mjs +0 -31
- package/runtime/scripts/api/process/kill-tree.mjs +0 -22
- package/runtime/scripts/api/process/lib.mjs +0 -125
- package/runtime/scripts/api/process/probe-guard-command.mjs +0 -74
- package/runtime/scripts/api/process/process-list.mjs +0 -28
- package/runtime/scripts/api/process/system-tool.mjs +0 -37
- package/runtime/scripts/gates/canon-digest.mjs +0 -81
- package/runtime/scripts/gates/canon-pins.mjs +0 -119
- package/runtime/scripts/gates/gate-input.mjs +0 -98
- package/runtime/scripts/gates/gate.mjs +0 -636
- package/runtime/scripts/gates/grammar-dist.mjs +0 -206
- package/runtime/scripts/gates/output.mjs +0 -40
- package/runtime/scripts/gates/runtime-package-selection.mjs +0 -27
- package/runtime/scripts/gates/test-world-run.mjs +0 -252
- package/runtime/scripts/gates/type-impact.mjs +0 -277
- package/runtime/scripts/gates/type-program.mjs +0 -113
- package/runtime/scripts/guards/assigned-command.mjs +0 -12
- package/runtime/scripts/guards/call-rights.mjs +0 -47
- package/runtime/scripts/guards/command-guard.mjs +0 -468
- package/runtime/scripts/guards/command-policy.mjs +0 -342
- package/runtime/scripts/guards/critic-reach.mjs +0 -82
- package/runtime/scripts/guards/deps-guard.mjs +0 -150
- package/runtime/scripts/guards/env-dump-verdict.mjs +0 -89
- package/runtime/scripts/guards/guards-root.mjs +0 -27
- package/runtime/scripts/guards/hook-install.mjs +0 -343
- package/runtime/scripts/guards/hook-verdict.mjs +0 -12
- package/runtime/scripts/guards/install-verdict.mjs +0 -41
- package/runtime/scripts/guards/kernel-seat.mjs +0 -63
- package/runtime/scripts/guards/launch-verdict.mjs +0 -74
- package/runtime/scripts/guards/op-starci.mjs +0 -16
- package/runtime/scripts/guards/op-write-scope.mjs +0 -27
- package/runtime/scripts/guards/orca-self-lifecycle.mjs +0 -39
- package/runtime/scripts/guards/process-kill-verdict.mjs +0 -106
- package/runtime/scripts/guards/protected-zone.mjs +0 -56
- package/runtime/scripts/guards/refusals.mjs +0 -27
- package/runtime/scripts/guards/rights.mjs +0 -268
- package/runtime/scripts/guards/seat-tools.mjs +0 -47
- package/runtime/scripts/guards/shell-commands.mjs +0 -328
- package/runtime/scripts/guards/supervisor-seat.mjs +0 -80
- package/runtime/scripts/guards/verdict-process.mjs +0 -81
- package/runtime/scripts/lib/app-data.mjs +0 -11
- package/runtime/scripts/lib/canon-binding-text.mjs +0 -129
- package/runtime/scripts/lib/check-format.mjs +0 -7
- package/runtime/scripts/lib/failure-code-reads.mjs +0 -100
- package/runtime/scripts/lib/failure-code-template-errors.mjs +0 -161
- package/runtime/scripts/lib/git-dir.mjs +0 -120
- package/runtime/scripts/lib/guard-command.mjs +0 -24
- package/runtime/scripts/lib/interrupt-cleanup.mjs +0 -38
- package/runtime/scripts/lib/json-formats.mjs +0 -34
- package/runtime/scripts/lib/managed-json.mjs +0 -51
- package/runtime/scripts/lib/menu-parts.mjs +0 -11
- package/runtime/scripts/lib/op-gate.mjs +0 -15
- package/runtime/scripts/lib/provision-control-failure.mjs +0 -32
- package/runtime/scripts/lib/release-version.mjs +0 -134
- package/runtime/scripts/lib/runtime-location.mjs +0 -21
- package/runtime/scripts/lib/runtime-package-inventory.mjs +0 -21
- package/runtime/scripts/lib/runtime-package-manifests.mjs +0 -84
- package/runtime/scripts/lib/runtime-publication-tags.mjs +0 -56
- package/runtime/scripts/lib/seat-env.mjs +0 -43
- package/runtime/scripts/lib/sonar-new-code.mjs +0 -58
- package/runtime/scripts/lib/source-package-imports.mjs +0 -111
- package/runtime/scripts/lib/tar-files.mjs +0 -39
- package/runtime/scripts/lib/uat-run-folder.mjs +0 -118
- package/runtime/scripts/lib/worktree-record.mjs +0 -14
- package/runtime/scripts/machine/ask-receipts.mjs +0 -113
- package/runtime/scripts/machine/evidence-store.mjs +0 -223
- package/runtime/scripts/machine/home.mjs +0 -216
- package/runtime/scripts/machine/host-lock.mjs +0 -184
- package/runtime/scripts/machine/lock-recovery.mjs +0 -55
- package/runtime/scripts/machine/roles-contract.mjs +0 -90
- package/runtime/scripts/machine/runtime-change.mjs +0 -20
- package/runtime/scripts/machine/workflow-tree.mjs +0 -122
- package/runtime/scripts/reconciler/checklist-items.mjs +0 -7
- package/runtime/scripts/reconciler/guard-row.mjs +0 -25
- package/runtime/scripts/work/brand/brand-colour.mjs +0 -125
- package/runtime/scripts/work/brand/brand-tokens.mjs +0 -167
- package/runtime/scripts/work/brand/brand-value-source.mjs +0 -106
- package/runtime/scripts/work/brand/brand.mjs +0 -892
- package/runtime/scripts/work/draw/draw-layer.mjs +0 -306
- package/runtime/scripts/work/draw/draw-rationale-page.mjs +0 -237
- package/runtime/scripts/work/draw/draw-rationale-ref.mjs +0 -14
- package/runtime/scripts/work/draw/draw-rationale.mjs +0 -353
- package/runtime/scripts/work/draw/draw-source.mjs +0 -412
- package/runtime/scripts/work/draw/draw-taste.mjs +0 -278
- package/runtime/scripts/work/draw/draw-typecheck.mjs +0 -72
- package/runtime/scripts/work/draw/rationale-findings.mjs +0 -205
- package/runtime/scripts/work/draw-grammar.mjs +0 -175
- package/runtime/scripts/work/draw-render-page.mjs +0 -188
- package/runtime/scripts/work/draw-render.mjs +0 -626
- package/runtime/scripts/work/png.mjs +0 -237
- package/runtime/scripts/work/render-tools.mjs +0 -64
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// seat-tools.mjs — the Claude Code PreToolUse hook that enforces a seat's denied tools (.claude/settings.json).
|
|
3
|
-
// worker-start takes no provider argv, so a seat launched through it cannot carry --disallowedTools: the runtime binds
|
|
4
|
-
// the seat's terminal instead (scripts/guards/hook-install.mjs bindSeatGuard -> <guards root>/seats/<handle>.json) and this
|
|
5
|
-
// hook denies exactly those tools for exactly that terminal (ORCA_TERMINAL_HANDLE, which Orca exports into it).
|
|
6
|
-
// Every other session - no Orca handle, or a handle with no seat guard - passes untouched.
|
|
7
|
-
import path from 'node:path';
|
|
8
|
-
import { fileURLToPath } from 'node:url';
|
|
9
|
-
import { boundGuard, boundSeat } from './rights.mjs';
|
|
10
|
-
import { hookErrorMessage, readInput, writeHookOutput } from './hook-io.mjs';
|
|
11
|
-
import { isMain } from '../lib/is-main.mjs';
|
|
12
|
-
import { readEnv } from '../lib/env.mjs';
|
|
13
|
-
|
|
14
|
-
const skillRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
15
|
-
|
|
16
|
-
/** The deny decision for one hook input, or null to pass: {reason} when `toolName` is denied for `handle`. */
|
|
17
|
-
function seatToolDecision({ handle, toolName, guard }) {
|
|
18
|
-
if (!handle || !toolName) return null;
|
|
19
|
-
if (!Array.isArray(guard?.deniedTools) || !guard.deniedTools.includes(toolName)) return null;
|
|
20
|
-
return { reason: `${toolName} is denied for the ${guard.role ?? 'seat'} (${handle}): in-process subagents are outside the Supervisor's menu - answer the menu with starci supervisor decide, or record a runtime defect with starci supervisor actions record --item runtime-defect:<cause> (modules/supervisor/supervisor-prompt.md).` };
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/** Exported hook entry for the published CLI's in-process guard fast path. */
|
|
24
|
-
export async function main({ stdin = process.stdin, stdout = process.stdout, stderr = process.stderr, env = process.env, root = skillRoot } = {}) {
|
|
25
|
-
const handle = readEnv('ORCA_TERMINAL_HANDLE', env);
|
|
26
|
-
const seat = boundSeat(handle, { root, env });
|
|
27
|
-
const enforced = Boolean(seat || boundGuard(handle, { root, env }) || readEnv('STARCI_ROLE', env));
|
|
28
|
-
let toolName = null;
|
|
29
|
-
try { toolName = JSON.parse(await readInput(stdin))?.tool_name ?? null; }
|
|
30
|
-
catch (error) {
|
|
31
|
-
const reason = `seat-tools error (${hookErrorMessage(error)})`;
|
|
32
|
-
const outcome = enforced ? 'refusing the tool' : 'passing the tool through';
|
|
33
|
-
await writeHookOutput(stderr, `starci guard: ${reason}; ${outcome}\n`).catch(() => false);
|
|
34
|
-
if (enforced) await denyTool(stdout, reason);
|
|
35
|
-
return enforced ? 2 : 0;
|
|
36
|
-
}
|
|
37
|
-
if (toolName) {
|
|
38
|
-
const decision = seatToolDecision({ handle, toolName, guard: seat });
|
|
39
|
-
if (decision && !await denyTool(stdout, decision.reason)) return 2;
|
|
40
|
-
}
|
|
41
|
-
return 0;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** Deliver a Claude denial within the output budget; failed delivery keeps the hook's blocking exit. */
|
|
45
|
-
const denyTool = (stdout, reason) => writeHookOutput(stdout, `${JSON.stringify({ hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: reason } })}\n`).catch(() => false);
|
|
46
|
-
|
|
47
|
-
if (isMain(import.meta.url)) process.exit(await main());
|
|
@@ -1,328 +0,0 @@
|
|
|
1
|
-
// shell-commands.mjs - the shell text of one agent call read as commands: quotes group, separators split, redirections and comments drop,
|
|
2
|
-
// command substitutions, wrappers (bash -c, powershell -Command, cmd /c, env, xargs, npx, command/exec/...), assignments and cd are opened,
|
|
3
|
-
// so the command guard (command-guard.mjs) and its rule modules judge the commands that would run, never the text around them.
|
|
4
|
-
// Pure: text in, [{program, args, cwd, env, word, dialect}] out. The guard re-exports simpleCommands, programOf and commandsOf.
|
|
5
|
-
import fs from 'node:fs';
|
|
6
|
-
import os from 'node:os';
|
|
7
|
-
import path from 'node:path';
|
|
8
|
-
|
|
9
|
-
/* ------------------------------------------------------------ command text */
|
|
10
|
-
|
|
11
|
-
const SEPARATORS = new Set([';', '&', '|', '(', ')', '{', '}', '\n']);
|
|
12
|
-
|
|
13
|
-
/** The index of the ')' closing the '(' just before `from` in `text` (text.length when none does). */
|
|
14
|
-
function closingParenIn(text, from) {
|
|
15
|
-
let depth = 1;
|
|
16
|
-
for (let j = from; j < text.length; j += 1) {
|
|
17
|
-
if (text[j] === '(') depth += 1;
|
|
18
|
-
else if (text[j] === ')' && --depth === 0) return j;
|
|
19
|
-
}
|
|
20
|
-
return text.length;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* The simple commands of one command line, in order: [[word, ...], ...]. Quotes group, separators (; & | && || ( ) { }
|
|
25
|
-
* newline) split, redirections and comments drop, and a command substitution $(...) is a command of its own. Bash
|
|
26
|
-
* and PowerShell text are both read: a backslash is a literal (Windows paths), a backtick splits in bash and escapes
|
|
27
|
-
* in PowerShell. $VAR, ${VAR}, $env:VAR and a leading ~ expand from `env`.
|
|
28
|
-
*/
|
|
29
|
-
export function simpleCommands(text, { dialect = 'bash', env = process.env } = {}) {
|
|
30
|
-
const s = String(text ?? '');
|
|
31
|
-
const st = { out: [], words: [], word: null, nested: [], heredocs: [] };
|
|
32
|
-
const endWord = () => { if (st.word != null) { st.words.push(st.word); } st.word = null; };
|
|
33
|
-
const endCommand = () => { endWord(); if (st.words.length) { st.out.push(st.words); } st.words = []; };
|
|
34
|
-
const expand = (name) => env?.[name] ?? env?.[Object.keys(env ?? {}).find((k) => k.toLowerCase() === name.toLowerCase())] ?? '';
|
|
35
|
-
const closingParen = (from) => closingParenIn(s, from);
|
|
36
|
-
// $NAME, ${NAME}, $env:NAME at s[i] ('$'); returns [value, next index] or null for a lone '$'.
|
|
37
|
-
const variable = (i) => {
|
|
38
|
-
const m = /^\$(?:\{([A-Z_]\w*)\}|(env:)?([A-Z_]\w*))/i.exec(s.slice(i));
|
|
39
|
-
if (!m) return null;
|
|
40
|
-
// PowerShell's $true, $false and $null are literals, never environment variables.
|
|
41
|
-
if (dialect === 'powershell' && !m[2] && m[3] && /^(?:true|false|null)$/i.test(m[3])) return [m[0], i + m[0].length];
|
|
42
|
-
return [expand(m[1] ?? m[3]), i + m[0].length];
|
|
43
|
-
};
|
|
44
|
-
/* Each `take*` below handles one special construct at s[i] and returns the index of its last consumed
|
|
45
|
-
character (the loop's += 1 resumes after it), or -1 when the construct does not apply at s[i]. */
|
|
46
|
-
|
|
47
|
-
// $(...) substitution (nested command), $env:NAME = value assignment, $VAR expansion.
|
|
48
|
-
const takeDollar = (i) => {
|
|
49
|
-
if (s[i + 1] === '(') {
|
|
50
|
-
const end = closingParen(i + 2);
|
|
51
|
-
st.nested.push(s.slice(i + 2, end));
|
|
52
|
-
st.word = (st.word ?? '') + '$()';
|
|
53
|
-
return end;
|
|
54
|
-
}
|
|
55
|
-
// PowerShell's `$env:NAME = value` is an assignment (NAME=value), not an expansion.
|
|
56
|
-
const assign = st.word == null ? /^\$env:([A-Z_]\w*)\s*=(?!=)\s*/i.exec(s.slice(i)) : null;
|
|
57
|
-
if (assign) { st.word = `${assign[1]}=`; return i + assign[0].length - 1; }
|
|
58
|
-
const v = variable(i);
|
|
59
|
-
if (v) { st.word = (st.word ?? '') + v[0]; return v[1] - 1; }
|
|
60
|
-
return -1;
|
|
61
|
-
};
|
|
62
|
-
const isQuotedEscape = (j) => (s[j] === '\\' && dialect !== 'powershell' && (s[j + 1] === '"' || s[j + 1] === '$' || s[j + 1] === '\\'))
|
|
63
|
-
|| (s[j] === '`' && dialect === 'powershell');
|
|
64
|
-
const queueQuotedSubstitution = (j) => {
|
|
65
|
-
const end = closingParen(j + 2);
|
|
66
|
-
st.nested.push(s.slice(j + 2, end));
|
|
67
|
-
return end;
|
|
68
|
-
};
|
|
69
|
-
const doubleQuotedText = (i) => {
|
|
70
|
-
let j = i + 1;
|
|
71
|
-
let v = '';
|
|
72
|
-
while (j < s.length && s[j] !== '"') {
|
|
73
|
-
if (isQuotedEscape(j)) { v += s[j + 1] ?? ''; j += 2; continue; }
|
|
74
|
-
if (s[j] === '$' && s[j + 1] === '(') { const end = queueQuotedSubstitution(j); v += '$()'; j = end + 1; continue; }
|
|
75
|
-
const expanded = s[j] === '$' ? variable(j) : null;
|
|
76
|
-
if (expanded) { v += expanded[0]; j = expanded[1]; continue; }
|
|
77
|
-
v += s[j];
|
|
78
|
-
j += 1;
|
|
79
|
-
}
|
|
80
|
-
return { value: v, end: j };
|
|
81
|
-
};
|
|
82
|
-
const takeQuote = (i) => {
|
|
83
|
-
if (s[i] === "'") {
|
|
84
|
-
const end = s.indexOf("'", i + 1);
|
|
85
|
-
st.word = (st.word ?? '') + s.slice(i + 1, end < 0 ? s.length : end);
|
|
86
|
-
return end < 0 ? s.length : end;
|
|
87
|
-
}
|
|
88
|
-
const quoted = doubleQuotedText(i);
|
|
89
|
-
st.word = (st.word ?? '') + quoted.value;
|
|
90
|
-
return quoted.end;
|
|
91
|
-
};
|
|
92
|
-
const takeBacktick = (i) => {
|
|
93
|
-
if (dialect === 'powershell') { st.word = (st.word ?? '') + (s[i + 1] ?? ''); return i + 1; }
|
|
94
|
-
endCommand();
|
|
95
|
-
return i;
|
|
96
|
-
};
|
|
97
|
-
const takeComment = (i) => {
|
|
98
|
-
if (st.word != null) return -1;
|
|
99
|
-
const nl = s.indexOf('\n', i);
|
|
100
|
-
return nl < 0 ? s.length : nl - 1;
|
|
101
|
-
};
|
|
102
|
-
// A bash heredoc mark (<<WORD, <<-WORD, <<'WORD') queues the body the next newline skips.
|
|
103
|
-
const takeHeredocMark = (i) => {
|
|
104
|
-
const heredoc = dialect !== 'powershell' && s[i + 1] === '<' && s[i + 2] !== '<'
|
|
105
|
-
? /^<<(-?)[ \t]*(?:'([^'\n]*)'|"([^"\n]*)"|\\?([^\s;&|()<>'"]+))/.exec(s.slice(i)) : null;
|
|
106
|
-
if (!heredoc) return -1;
|
|
107
|
-
endWord();
|
|
108
|
-
st.heredocs.push({ strip: heredoc[1] === '-', delimiter: heredoc[2] ?? heredoc[3] ?? heredoc[4], expands: heredoc[4] != null && !heredoc[0].includes('\\') });
|
|
109
|
-
return i + heredoc[0].length - 1;
|
|
110
|
-
};
|
|
111
|
-
// At a newline, each queued heredoc body is skipped to its delimiter; an unquoted body still runs its $(...) substitutions.
|
|
112
|
-
const queueHeredocSubstitutions = (line) => {
|
|
113
|
-
for (const m of line.matchAll(/\$\(/g)) st.nested.push(line.slice(m.index + 2, closingParenIn(line, m.index + 2)));
|
|
114
|
-
};
|
|
115
|
-
const skipHeredocBody = (doc, at) => {
|
|
116
|
-
while (at < s.length) {
|
|
117
|
-
const nl = s.indexOf('\n', at);
|
|
118
|
-
const line = s.slice(at, nl < 0 ? s.length : nl).replace(/\r$/, '');
|
|
119
|
-
at = nl < 0 ? s.length : nl + 1;
|
|
120
|
-
if ((doc.strip ? line.replace(/^\t+/, '') : line) === doc.delimiter) break;
|
|
121
|
-
if (doc.expands) queueHeredocSubstitutions(line);
|
|
122
|
-
}
|
|
123
|
-
return at;
|
|
124
|
-
};
|
|
125
|
-
const takeHeredocBody = (i) => {
|
|
126
|
-
if (!st.heredocs.length) return -1;
|
|
127
|
-
endCommand();
|
|
128
|
-
let at = i + 1;
|
|
129
|
-
for (const doc of st.heredocs.splice(0)) at = skipHeredocBody(doc, at);
|
|
130
|
-
return at - 1;
|
|
131
|
-
};
|
|
132
|
-
// A redirection and its target are not arguments: `2>&1`, `>> log`, `*> $null`, `< in`.
|
|
133
|
-
const takeRedirect = (i) => {
|
|
134
|
-
if (st.word != null && /^(?:\d|\*)$/.test(st.word)) st.word = null;
|
|
135
|
-
endWord();
|
|
136
|
-
let j = i + 1;
|
|
137
|
-
while (s[j] === '>' || s[j] === '&') j += 1;
|
|
138
|
-
if (/\d/.test(s[j] ?? '') && s[j - 1] === '&') return j;
|
|
139
|
-
while (s[j] === ' ' || s[j] === '\t') j += 1;
|
|
140
|
-
if (s[j] === '"' || s[j] === "'") { const q = s[j]; const end = s.indexOf(q, j + 1); j = end < 0 ? s.length : end + 1; }
|
|
141
|
-
else while (j < s.length && !/[\s;&|()<>]/.test(s[j])) j += 1;
|
|
142
|
-
return j - 1;
|
|
143
|
-
};
|
|
144
|
-
const takeTilde = (i) => {
|
|
145
|
-
if (st.word != null || !(s[i + 1] === '/' || s[i + 1] === '\\' || s[i + 1] == null || /\s/.test(s[i + 1]))) return -1;
|
|
146
|
-
st.word = os.homedir();
|
|
147
|
-
return i;
|
|
148
|
-
};
|
|
149
|
-
// s[i] as a special construct; -1 when it is an ordinary character.
|
|
150
|
-
const takeSpecial = (i) => {
|
|
151
|
-
const c = s[i];
|
|
152
|
-
if (c === '$') return takeDollar(i);
|
|
153
|
-
if (c === "'" || c === '"') return takeQuote(i);
|
|
154
|
-
if (c === '`') return takeBacktick(i);
|
|
155
|
-
if (c === '#') return takeComment(i);
|
|
156
|
-
if (c === '<') { const h = takeHeredocMark(i); return h >= 0 ? h : takeRedirect(i); }
|
|
157
|
-
if (c === '>') return takeRedirect(i);
|
|
158
|
-
if (c === '\n') return takeHeredocBody(i);
|
|
159
|
-
if (c === '~') return takeTilde(i);
|
|
160
|
-
return -1;
|
|
161
|
-
};
|
|
162
|
-
for (let i = 0, step = 1; i < s.length; i += step) {
|
|
163
|
-
step = 1;
|
|
164
|
-
const n = takeSpecial(i);
|
|
165
|
-
if (n >= 0) { step = n - i + 1; continue; }
|
|
166
|
-
const c = s[i];
|
|
167
|
-
if (c === '\r') continue;
|
|
168
|
-
if (SEPARATORS.has(c)) { endCommand(); continue; }
|
|
169
|
-
if (c === ' ' || c === '\t') { endWord(); continue; }
|
|
170
|
-
st.word = (st.word ?? '') + c;
|
|
171
|
-
}
|
|
172
|
-
endCommand();
|
|
173
|
-
for (const inner of st.nested) st.out.push(...simpleCommands(inner, { dialect, env }));
|
|
174
|
-
return st.out;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/** A command word as the program it names: basename, lowercase, without .exe/.cmd/.bat/.ps1. */
|
|
178
|
-
export const programOf = (word) => path.basename(String(word ?? '').replaceAll('\\', '/')).toLowerCase().replace(/\.(?:exe|cmd|bat|ps1)$/, '');
|
|
179
|
-
|
|
180
|
-
const ASSIGNMENT = /^([A-Za-z_]\w*)=(.*)$/s;
|
|
181
|
-
const PREFIX_PROGRAMS = new Set(['command', 'exec', 'time', 'nohup', 'sudo', 'builtin']);
|
|
182
|
-
const SHELLS = new Set(['bash', 'sh', 'zsh', 'dash']);
|
|
183
|
-
const POWERSHELLS = new Set(['powershell', 'pwsh']);
|
|
184
|
-
const CD_PROGRAMS = new Set(['cd', 'set-location', 'sl', 'chdir', 'pushd', 'push-location']);
|
|
185
|
-
|
|
186
|
-
// command/exec/time/nohup/sudo/builtin <maybe one flag> <real command> - drop the wrapper word.
|
|
187
|
-
const unPrefix = (w) => w.slice(1).filter((a, i, all) => !(i === 0 && a.startsWith('-') && all.length > 1));
|
|
188
|
-
// env [-u NAME | -C dir | -S str | VAR=value]... <real command>: the options and assignments apply to what follows.
|
|
189
|
-
const unEnv = (w, cmdEnv) => {
|
|
190
|
-
w = w.slice(1);
|
|
191
|
-
while (w.length && (w[0].startsWith('-') || ASSIGNMENT.test(w[0]))) {
|
|
192
|
-
const m = ASSIGNMENT.exec(w[0]);
|
|
193
|
-
if (m) cmdEnv[m[1]] = m[2];
|
|
194
|
-
const takesValue = /^-[uCS]$/.test(w[0]);
|
|
195
|
-
w.shift();
|
|
196
|
-
if (takesValue) w.shift();
|
|
197
|
-
}
|
|
198
|
-
return w;
|
|
199
|
-
};
|
|
200
|
-
const unXargs = (w) => {
|
|
201
|
-
w = w.slice(1);
|
|
202
|
-
while (w.length && w[0].startsWith('-')) {
|
|
203
|
-
const takesValue = /^-(?:[IdEnLPs]|-(?:replace|delimiter|eof|max-args|max-lines|max-procs|max-chars|arg-file))$/.test(w[0]);
|
|
204
|
-
w.shift();
|
|
205
|
-
if (takesValue) w.shift();
|
|
206
|
-
}
|
|
207
|
-
return w;
|
|
208
|
-
};
|
|
209
|
-
// npx [-y] [--package <pkg>] <bin> args: the package's bin is the program (@openai/codex -> codex); corepack
|
|
210
|
-
// <pnpm|yarn> args runs that package manager. Same option-skip for bunx.
|
|
211
|
-
const unRun = (w) => {
|
|
212
|
-
w = w.slice(1);
|
|
213
|
-
while (w.length && w[0].startsWith('-')) {
|
|
214
|
-
const takesValue = /^(?:-p|--package)$/.test(w[0]);
|
|
215
|
-
w.shift();
|
|
216
|
-
if (takesValue) w.shift();
|
|
217
|
-
}
|
|
218
|
-
return w;
|
|
219
|
-
};
|
|
220
|
-
// The inner command a wrapper program runs: {text, dialect} to recurse on, {} for a wrapper that runs
|
|
221
|
-
// nothing visible (powershell -File with an unreadable body), null when the program is no open wrapper.
|
|
222
|
-
const shellInner = (args) => {
|
|
223
|
-
const at = args.findIndex((a) => /^-[a-z]+$/.test(a) && a.includes('c'));
|
|
224
|
-
return at >= 0 && args[at + 1] != null ? { text: args[at + 1], dialect: 'bash' } : null;
|
|
225
|
-
};
|
|
226
|
-
const powershellInner = (args, dir) => {
|
|
227
|
-
const flagAt = (re) => args.findIndex((a) => re.test(a));
|
|
228
|
-
const enc = flagAt(/^-(?:e|ec|enc|encodedcommand)$/i);
|
|
229
|
-
if (enc >= 0 && args[enc + 1]) return { text: Buffer.from(args[enc + 1], 'base64').toString('utf16le'), dialect: 'powershell' };
|
|
230
|
-
const file = flagAt(/^-(?:f|file)$/i);
|
|
231
|
-
if (file >= 0 && args[file + 1]) {
|
|
232
|
-
let body = null;
|
|
233
|
-
try { body = fs.readFileSync(path.resolve(dir, args[file + 1]), 'utf8'); } catch { /* an unreadable script runs nothing we can see */ }
|
|
234
|
-
return body == null ? {} : { text: body, dialect: 'powershell' };
|
|
235
|
-
}
|
|
236
|
-
const command = flagAt(/^-(?:c|command)$/i);
|
|
237
|
-
if (command >= 0) return { text: args.slice(command + 1).join(' '), dialect: 'powershell' };
|
|
238
|
-
const bare = args.filter((a) => !a.startsWith('-'));
|
|
239
|
-
return bare.length ? { text: bare.join(' '), dialect: 'powershell' } : null;
|
|
240
|
-
};
|
|
241
|
-
const cmdInner = (args) => {
|
|
242
|
-
// Git Bash spells cmd's switch //c (MSYS would rewrite a single /c as a path).
|
|
243
|
-
const at = args.findIndex((a) => /^\/\/?[ck]$/i.test(a));
|
|
244
|
-
return at >= 0 ? { text: args.slice(at + 1).join(' '), dialect: 'cmd' } : null;
|
|
245
|
-
};
|
|
246
|
-
const wrappedInner = (program, args, dir) => {
|
|
247
|
-
if (SHELLS.has(program)) return shellInner(args);
|
|
248
|
-
if (POWERSHELLS.has(program)) return powershellInner(args, dir);
|
|
249
|
-
if (program === 'cmd') return cmdInner(args);
|
|
250
|
-
return null;
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* The commands one agent shell call runs, flattened: [{program, args, cwd, env, word, dialect}] with wrappers opened
|
|
255
|
-
* (bash -c, powershell -Command/-EncodedCommand/-File, cmd /c, env, xargs, npx/bunx, command/exec/...), leading VAR=value and
|
|
256
|
-
* export/$env: assignments applied to the commands after them, and cd/Set-Location/pushd moving the cwd.
|
|
257
|
-
*/
|
|
258
|
-
const leadingAssignments = (words) => {
|
|
259
|
-
const local = {};
|
|
260
|
-
while (words.length && ASSIGNMENT.test(words[0])) { const [, key, value] = ASSIGNMENT.exec(words[0]); local[key] = value; words.shift(); }
|
|
261
|
-
return local;
|
|
262
|
-
};
|
|
263
|
-
|
|
264
|
-
const recordEnvironmentCommand = (program, words, state) => {
|
|
265
|
-
if (program !== 'export' && program !== 'set') return false;
|
|
266
|
-
if (words.length === 1 || (words.length === 2 && words[1] === '-p'))
|
|
267
|
-
state.out.push({ program, args: words.slice(1), cwd: state.dir, env: state.scopeEnv, word: words[0], dialect: state.dialect });
|
|
268
|
-
for (const argument of words.slice(1)) {
|
|
269
|
-
const assignment = ASSIGNMENT.exec(argument);
|
|
270
|
-
if (assignment) state.scopeEnv[assignment[1]] = assignment[2];
|
|
271
|
-
}
|
|
272
|
-
return true;
|
|
273
|
-
};
|
|
274
|
-
|
|
275
|
-
const unwrapCommandWrappers = (words, program, cmdEnv, state) => {
|
|
276
|
-
for (let guard = 0; guard < 6; guard += 1) {
|
|
277
|
-
if (PREFIX_PROGRAMS.has(program)) words = unPrefix(words);
|
|
278
|
-
else if (program === 'env') {
|
|
279
|
-
const envWord = words[0];
|
|
280
|
-
words = unEnv(words, cmdEnv);
|
|
281
|
-
// `env` and `env -u NAME` with no command to run print the whole environment: they stay a command for ENV_DUMP.
|
|
282
|
-
if (!words.length) state.out.push({ program: 'env', args: [], cwd: state.dir, env: cmdEnv, word: envWord, dialect: state.dialect });
|
|
283
|
-
} else if (program === 'xargs') words = unXargs(words);
|
|
284
|
-
else if (program === 'npx' || program === 'bunx' || program === 'corepack') words = unRun(words);
|
|
285
|
-
else break;
|
|
286
|
-
if (!words.length) break;
|
|
287
|
-
program = programOf(words[0]);
|
|
288
|
-
}
|
|
289
|
-
return { words, program };
|
|
290
|
-
};
|
|
291
|
-
|
|
292
|
-
const changeDirectory = (program, args, state) => {
|
|
293
|
-
if (!CD_PROGRAMS.has(program)) return false;
|
|
294
|
-
// cmd's `cd /d <dir>` switches the drive too: /d is a switch there, not the target.
|
|
295
|
-
const target = args.find((a) => !a.startsWith('-') && !(state.dialect === 'cmd' && /^\/d$/i.test(a)));
|
|
296
|
-
if (target) state.dir = path.resolve(state.dir, target);
|
|
297
|
-
return true;
|
|
298
|
-
};
|
|
299
|
-
|
|
300
|
-
const openWrapper = (program, args, cmdEnv, state) => {
|
|
301
|
-
const inner = wrappedInner(program, args, state.dir);
|
|
302
|
-
if (!inner) return false;
|
|
303
|
-
if (inner.text != null) state.out.push(...commandsOf(inner.text, { cwd: state.dir, env: cmdEnv, dialect: inner.dialect, depth: state.depth + 1 }));
|
|
304
|
-
return true;
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
const processWords = (sourceWords, state) => {
|
|
308
|
-
let words = [...sourceWords];
|
|
309
|
-
const local = leadingAssignments(words);
|
|
310
|
-
if (!words.length) { Object.assign(state.scopeEnv, local); return; }
|
|
311
|
-
let program = programOf(words[0]);
|
|
312
|
-
if (recordEnvironmentCommand(program, words, state)) return;
|
|
313
|
-
const cmdEnv = { ...state.scopeEnv, ...local };
|
|
314
|
-
({ words, program } = unwrapCommandWrappers(words, program, cmdEnv, state));
|
|
315
|
-
if (!words.length) return;
|
|
316
|
-
const args = words.slice(1);
|
|
317
|
-
if (changeDirectory(program, args, state)) return;
|
|
318
|
-
if (openWrapper(program, args, cmdEnv, state)) return;
|
|
319
|
-
state.out.push({ program, args, cwd: state.dir, env: cmdEnv, word: words[0], dialect: state.dialect });
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
export function commandsOf(text, { cwd, env = process.env, dialect = 'bash', depth = 0 } = {}) {
|
|
323
|
-
const out = [];
|
|
324
|
-
if (depth > 4) return out;
|
|
325
|
-
const state = { dir: cwd, scopeEnv: { ...env }, out, dialect, depth };
|
|
326
|
-
for (const words of simpleCommands(text, { dialect, env: state.scopeEnv })) processWords(words, state);
|
|
327
|
-
return out;
|
|
328
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
// supervisor-seat.mjs — what the Supervisor seat (a bound caller of role supervisor) may run, from the `supervisor` table of
|
|
2
|
-
// modules/kernel/command-policy.yaml. The PreToolUse guard and the PATH shims evaluate it (command-policy.mjs policyVerdict): the
|
|
3
|
-
// seat reads, answers the items of its menu with `starci supervisor decide`, records a runtime defect for Debug and runs the
|
|
4
|
-
// collectors it owns. Every refusal names the status and decision verbs without running them.
|
|
5
|
-
import { plainWords } from '../lib/menu-parts.mjs';
|
|
6
|
-
import { refusal } from './rights.mjs';
|
|
7
|
-
import { runtimeChangeRefusal, RUNTIME_CHANGE_CODE } from '../machine/runtime-change.mjs';
|
|
8
|
-
|
|
9
|
-
const OPTION = /^--?([A-Za-z][\w-]*)(?:=(.*))?$/;
|
|
10
|
-
|
|
11
|
-
/** The option names and the values of the options that carry one (`--item x` or `--item=x`) of a call. */
|
|
12
|
-
function optionsOf(args) {
|
|
13
|
-
const names = new Set();
|
|
14
|
-
const values = new Map();
|
|
15
|
-
args.map(String).forEach((word, index, all) => {
|
|
16
|
-
const hit = OPTION.exec(word);
|
|
17
|
-
if (!hit) return;
|
|
18
|
-
names.add(hit[1]);
|
|
19
|
-
const next = all[index + 1];
|
|
20
|
-
const value = hit[2] ?? (next?.startsWith('-') === false ? next : undefined);
|
|
21
|
-
if (value !== undefined) values.set(hit[1], value);
|
|
22
|
-
});
|
|
23
|
-
return { names, values };
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/** The first three non-option words of a starci call: its group, verb and action. */
|
|
27
|
-
const listed = (table, group, verb) => (table?.verbs?.[group] ?? []).includes(verb);
|
|
28
|
-
|
|
29
|
-
/** The reason a listed verb is still refused for its action or options, or null. */
|
|
30
|
-
function narrowReason(table, { group, verb, action }, options) {
|
|
31
|
-
const actions = table.actions?.[verb];
|
|
32
|
-
if (actions?.includes(action) === false) return `starci ${[group, verb, action].filter(Boolean).join(' ')} is not an action of the Supervisor seat (${actions.join(', ')})`;
|
|
33
|
-
const blocked = (table['read-flags']?.[verb] ?? []).find((flag) => options.names.has(flag));
|
|
34
|
-
if (blocked) return `starci ${group} ${verb} --${blocked} changes state: the menu's decision verb does it`;
|
|
35
|
-
if (verb === 'actions' && action === 'record' && !new RegExp(table['record-items']).test(options.values.get('item') ?? '')) {
|
|
36
|
-
return 'starci supervisor actions record is allowed for a runtime defect only: --item runtime-defect:<cause>';
|
|
37
|
-
}
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** Whether the seat may run `starci <group> <verb> [action]` with these options: {allowed, reason?}. */
|
|
42
|
-
function supervisorSeatAllows(table, words, args = []) {
|
|
43
|
-
const [group, verb, action] = words;
|
|
44
|
-
if (!listed(table, group, verb)) return { allowed: false, reason: `starci ${group} ${verb} is not a verb of the Supervisor seat` };
|
|
45
|
-
const reason = narrowReason(table, { group, verb, action }, optionsOf(args));
|
|
46
|
-
return reason ? { allowed: false, reason } : { allowed: true };
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const useOf = (table) => table.use;
|
|
50
|
-
|
|
51
|
-
/** A runtime-change verb the seat tried (land, a fix worker): the Debug code, with the menu as the way on. */
|
|
52
|
-
function runtimeChangeVerdict({ table, words, text }) {
|
|
53
|
-
const change = runtimeChangeRefusal(words);
|
|
54
|
-
if (!change) return null;
|
|
55
|
-
const use = useOf(table);
|
|
56
|
-
return refusal(RUNTIME_CHANGE_CODE, text, change.reason, `${change.remedy}\n${use}`, use);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* The refusal of a call the Supervisor seat may not make, or null when it may run it. A program other than starci passes only as a
|
|
61
|
-
* pure read or as a program whose read forms the general policy judges (orca lifecycle, git, npm); the rest is refused.
|
|
62
|
-
*/
|
|
63
|
-
export function supervisorSeatVerdict({ policy, program, args, text }) {
|
|
64
|
-
const table = policy?.supervisor;
|
|
65
|
-
if (!table) return null;
|
|
66
|
-
if (program === 'starci') {
|
|
67
|
-
const words = plainWords(args, 3);
|
|
68
|
-
const changed = runtimeChangeVerdict({ table, words, text });
|
|
69
|
-
if (changed) return changed;
|
|
70
|
-
const verdict = supervisorSeatAllows(table, words, args);
|
|
71
|
-
if (verdict.allowed) return null;
|
|
72
|
-
const use = useOf(table);
|
|
73
|
-
return refusal('SUPERVISOR_USE_DECIDE', text, `${verdict.reason}: the runtime runs it, and the Supervisor answers the items of its menu`, use, use);
|
|
74
|
-
}
|
|
75
|
-
const reads = new Set([...(policy.read ?? []), ...Object.keys(policy['guarded-read'] ?? {}), ...(table.defer ?? [])].map(String));
|
|
76
|
-
const denied = new Set((table['deny-programs'] ?? []).map(String));
|
|
77
|
-
if (reads.has(program) && !denied.has(program)) return null;
|
|
78
|
-
const use = useOf(table);
|
|
79
|
-
return refusal('SUPERVISOR_STARCI_ONLY', text, `the Supervisor seat runs starci verbs and pure reads: ${program} is neither`, use, use);
|
|
80
|
-
}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { fileURLToPath } from 'node:url';
|
|
2
|
-
import { spawnNode } from '../api/node/spawn-node.mjs';
|
|
3
|
-
import { killTree } from '../api/process/kill-tree.mjs';
|
|
4
|
-
import { HOOK_INPUT_LIMITS } from './hook-io.mjs';
|
|
5
|
-
|
|
6
|
-
const entry = fileURLToPath(new URL('./command-guard.mjs', import.meta.url));
|
|
7
|
-
|
|
8
|
-
/** Request forced teardown of this call's verifier tree; a failed tree stop is reported after the direct child is stopped. */
|
|
9
|
-
function stopVerifier(child, teardownTimeoutMs) {
|
|
10
|
-
if (!Number.isInteger(child?.pid) || child.pid <= 0 || child.exitCode !== null) return;
|
|
11
|
-
let failure;
|
|
12
|
-
if (process.platform === 'win32') {
|
|
13
|
-
const stopped = killTree(child.pid, { timeoutMs: teardownTimeoutMs });
|
|
14
|
-
if (!stopped.ok) {
|
|
15
|
-
child.kill('SIGKILL');
|
|
16
|
-
const detail = stopped.output || `exit ${stopped.status}`;
|
|
17
|
-
failure = new Error(`verifier tree stop failed (${detail})`);
|
|
18
|
-
}
|
|
19
|
-
} else {
|
|
20
|
-
try { process.kill(-child.pid, 'SIGKILL'); }
|
|
21
|
-
catch (error) {
|
|
22
|
-
if (error.code !== 'ESRCH') {
|
|
23
|
-
child.kill('SIGKILL');
|
|
24
|
-
failure = error;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
if (child.connected) child.disconnect();
|
|
29
|
-
child.unref();
|
|
30
|
-
if (failure) throw failure;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Evaluate a hook in an owned child so synchronous policy work cannot block its caller's deadline.
|
|
35
|
-
* The promise resolves only after the child sends a decision and exits; startup, IPC and deadline failures reject.
|
|
36
|
-
* A deadline requests bounded forced teardown of that child's tree and never accepts a late decision.
|
|
37
|
-
*/
|
|
38
|
-
export function runHookVerdict({ input, root, bindings, env = process.env, start = spawnNode,
|
|
39
|
-
timeoutMs = HOOK_INPUT_LIMITS.verdictTimeoutMs, teardownTimeoutMs = HOOK_INPUT_LIMITS.teardownTimeoutMs } = {}) {
|
|
40
|
-
return new Promise((resolve, reject) => {
|
|
41
|
-
const deadline = performance.now() + timeoutMs;
|
|
42
|
-
let child;
|
|
43
|
-
let settled = false;
|
|
44
|
-
let response;
|
|
45
|
-
const finish = (error, decision) => {
|
|
46
|
-
if (settled) return;
|
|
47
|
-
// Event-loop delays can deliver close before the expired timer; elapsed time still owns admission.
|
|
48
|
-
if (!error && performance.now() >= deadline) error = new Error(`hook verdict deadline exceeded (${timeoutMs} ms)`);
|
|
49
|
-
settled = true;
|
|
50
|
-
clearTimeout(timer);
|
|
51
|
-
if (error) {
|
|
52
|
-
try { stopVerifier(child, teardownTimeoutMs); }
|
|
53
|
-
catch (cleanupError) {
|
|
54
|
-
child?.channel?.unref();
|
|
55
|
-
child?.unref();
|
|
56
|
-
reject(new Error(`${error.message}; verifier cleanup failed (${cleanupError.message})`));
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
reject(error);
|
|
60
|
-
} else resolve(decision);
|
|
61
|
-
};
|
|
62
|
-
const timer = setTimeout(() => finish(new Error(`hook verdict deadline exceeded (${timeoutMs} ms)`)), timeoutMs);
|
|
63
|
-
try {
|
|
64
|
-
child = start([entry], { env, detached: process.platform !== 'win32', stdio: ['ignore', 'ignore', 'ignore', 'ipc'] });
|
|
65
|
-
child.once('error', (error) => finish(error));
|
|
66
|
-
child.on('message', (message) => {
|
|
67
|
-
if (settled || response) return;
|
|
68
|
-
if (message?.ok !== true || !Object.hasOwn(message, 'decision')) {
|
|
69
|
-
finish(new Error(String(message?.error ?? 'hook verifier returned an invalid decision')));
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
response = message;
|
|
73
|
-
});
|
|
74
|
-
child.once('close', (code, signal) => {
|
|
75
|
-
if (response && code === 0 && !signal) finish(null, response.decision);
|
|
76
|
-
else finish(new Error(`hook verifier exited before its decision (exit ${code}, signal ${signal ?? 'none'})`));
|
|
77
|
-
});
|
|
78
|
-
child.send({ input, root, bindings }, (error) => { if (error) finish(error); });
|
|
79
|
-
} catch (error) { finish(error); }
|
|
80
|
-
});
|
|
81
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
// app-data.mjs — the per-OS application-data base directory (the parent of 'orca', 'codex-runtime-home'
|
|
2
|
-
// and friends): %APPDATA% on Windows, ~/Library/Application Support on macOS, $XDG_CONFIG_HOME|~/.config elsewhere.
|
|
3
|
-
import os from 'node:os';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
|
|
6
|
-
/** The application-data base directory of this platform: {env, platform, home}: the caller passes the environment. */
|
|
7
|
-
export const appDataBase = ({ env, platform = process.platform, home = os.homedir() } = {}) => {
|
|
8
|
-
if (platform === 'win32') return env.APPDATA || path.join(home, 'AppData', 'Roaming');
|
|
9
|
-
if (platform === 'darwin') return path.join(home, 'Library', 'Application Support');
|
|
10
|
-
return env.XDG_CONFIG_HOME || path.join(home, '.config');
|
|
11
|
-
};
|