@yagni-app/code 0.2.0 → 0.3.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/dist/cli.d.ts +30 -0
- package/dist/cli.js +135 -3
- package/dist/doctor.d.ts +1 -1
- package/dist/doctor.js +1 -1
- package/dist/extension/advisor.d.ts +4 -4
- package/dist/extension/advisor.js +6 -7
- package/dist/extension/approvedPrefixes.d.ts +92 -0
- package/dist/extension/approvedPrefixes.js +252 -0
- package/dist/extension/askAdvisorTool.d.ts +2 -2
- package/dist/extension/askAdvisorTool.js +5 -5
- package/dist/extension/askYagniTool.js +49 -0
- package/dist/extension/branding.d.ts +24 -3
- package/dist/extension/branding.js +71 -10
- package/dist/extension/chipEditor.d.ts +30 -9
- package/dist/extension/chipEditor.js +173 -59
- package/dist/extension/claudeRules.d.ts +0 -2
- package/dist/extension/claudeRules.js +0 -8
- package/dist/extension/cmux/dispatcher.d.ts +25 -0
- package/dist/extension/cmux/dispatcher.js +266 -0
- package/dist/extension/cmux/hooks.d.ts +12 -0
- package/dist/extension/cmux/hooks.js +192 -0
- package/dist/extension/cmux/index.d.ts +3 -0
- package/dist/extension/cmux/index.js +155 -0
- package/dist/extension/cmux/naming.d.ts +5 -0
- package/dist/extension/cmux/naming.js +23 -0
- package/dist/extension/cmux/state.d.ts +33 -0
- package/dist/extension/cmux/state.js +142 -0
- package/dist/extension/config.d.ts +32 -1
- package/dist/extension/config.js +36 -4
- package/dist/extension/costHud.d.ts +16 -22
- package/dist/extension/costHud.js +8 -47
- package/dist/extension/crashReport.js +1 -3
- package/dist/extension/execPolicy.d.ts +119 -0
- package/dist/extension/execPolicy.js +805 -0
- package/dist/extension/footer.d.ts +111 -0
- package/dist/extension/footer.js +294 -0
- package/dist/extension/guardian.d.ts +129 -0
- package/dist/extension/guardian.js +213 -0
- package/dist/extension/index.d.ts +15 -4
- package/dist/extension/index.js +250 -24
- package/dist/extension/permission.d.ts +123 -10
- package/dist/extension/permission.js +586 -40
- package/dist/extension/pipeline/childRegistry.d.ts +41 -0
- package/dist/extension/pipeline/childRegistry.js +118 -0
- package/dist/extension/pipeline/finish.js +5 -1
- package/dist/extension/pipeline/goCommand.d.ts +1 -1
- package/dist/extension/pipeline/goCommand.js +35 -6
- package/dist/extension/pipeline/goStatusCommands.d.ts +10 -0
- package/dist/extension/pipeline/goStatusCommands.js +61 -1
- package/dist/extension/pipeline/personas.js +25 -0
- package/dist/extension/pipeline/runRegistry.d.ts +14 -0
- package/dist/extension/pipeline/runRegistry.js +35 -0
- package/dist/extension/pipeline/runner.js +4 -0
- package/dist/extension/pipeline/verify.d.ts +4 -0
- package/dist/extension/pipeline/verify.js +48 -26
- package/dist/extension/redact.d.ts +20 -0
- package/dist/extension/redact.js +64 -0
- package/dist/extension/rerouteNotice.d.ts +3 -12
- package/dist/extension/rerouteNotice.js +36 -15
- package/dist/extension/subagentRender.d.ts +129 -0
- package/dist/extension/subagentRender.js +441 -0
- package/dist/extension/subagents.d.ts +4 -7
- package/dist/extension/subagents.js +103 -33
- package/dist/extension/ticketTools.d.ts +37 -0
- package/dist/extension/ticketTools.js +117 -0
- package/dist/extension/tokenProvider.js +46 -5
- package/dist/launch.d.ts +7 -0
- package/dist/launch.js +24 -12
- package/dist/padding.d.ts +22 -0
- package/dist/padding.js +25 -0
- package/dist/promptEnrichment.d.ts +40 -0
- package/dist/promptEnrichment.js +85 -0
- package/dist/signalForward.d.ts +60 -0
- package/dist/signalForward.js +130 -0
- package/package.json +5 -5
- package/dist/extension/boostCommand.d.ts +0 -144
- package/dist/extension/boostCommand.js +0 -263
|
@@ -52,7 +52,20 @@ export interface Headroom {
|
|
|
52
52
|
remaining: number;
|
|
53
53
|
unit: string;
|
|
54
54
|
}
|
|
55
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Render the /cost line. Pure, no em-dashes. `headroom` null means it was
|
|
57
|
+
* unavailable. `advisorLine` is the ask_advisor subtotal (empty when the session
|
|
58
|
+
* never escalated). `source`, when given, is parenthesized right after "Session
|
|
59
|
+
* usage", used to explicitly label this as the LOCAL, driver-only fallback when
|
|
60
|
+
* the server-authoritative spend fetch is unavailable (see
|
|
61
|
+
* {@link formatServerCostLines}, the preferred path).
|
|
62
|
+
*
|
|
63
|
+
* HONESTY NOTE: this counter can only see the DRIVER session, because it is fed
|
|
64
|
+
* by pi's `turn_end`. Work that runs in a child process — every /go stage, and
|
|
65
|
+
* every advisor consult — never emits a parent `turn_end`, so it is invisible
|
|
66
|
+
* here. The advisor subtotal is threaded in explicitly for exactly that reason.
|
|
67
|
+
*/
|
|
68
|
+
export declare function formatCostLine(snap: CostSnapshot, headroom?: Headroom | null, advisorLine?: string, source?: string): string;
|
|
56
69
|
/**
|
|
57
70
|
* One row of `GET /api/yagni-code/spend`'s per-caller x rate-tier breakdown.
|
|
58
71
|
* Mirrored locally rather than importing `@yagni/shared`, since this extension
|
|
@@ -102,25 +115,15 @@ export declare const usd: (millicents: number) => string;
|
|
|
102
115
|
* (joined with "\n").
|
|
103
116
|
*
|
|
104
117
|
* Line order: total spend, one line per rate tier (aggregated across callers,
|
|
105
|
-
* sorted by spend descending),
|
|
106
|
-
* boosted rows, a live-toggle boost line (see below), the savings line (only
|
|
118
|
+
* sorted by spend descending), the savings line (only
|
|
107
119
|
* when a counterfactual total resolved), an incomplete-counterfactual note,
|
|
108
120
|
* an unbilled note, a dropped-run-ids note (carry-over from the /cost
|
|
109
121
|
* re-review — see sessionRuns.ts's `droppedSessionRuns`), then headroom.
|
|
110
122
|
*
|
|
111
123
|
* `droppedRunCount` defaults to 0 (no note) so every existing direct caller
|
|
112
124
|
* of this pure function keeps behaving identically without passing it.
|
|
113
|
-
*
|
|
114
|
-
* `boosted` (default false) is the LIVE client-side toggle from
|
|
115
|
-
* `boostCommand.ts`'s `isBoosted()`, threaded in exactly like `advisorLine`
|
|
116
|
-
* is for {@link formatCostLine}. It is independent of the per-tier "Boosted
|
|
117
|
-
* (tier) spend" subtotal above: that subtotal only reflects server rows
|
|
118
|
-
* carrying `x-yagni-boost`, which only `/go` children (and other spawned
|
|
119
|
-
* processes) ever send — the driver's own turns never do (see
|
|
120
|
-
* boostCommand.ts's KNOWN-asymmetry docblock). Without this flag, a session
|
|
121
|
-
* that only chats while boosted would show no boost line at all.
|
|
122
125
|
*/
|
|
123
|
-
export declare function formatServerCostLines(spend: SpendResponse, headroom?: Headroom | null, droppedRunCount?: number
|
|
126
|
+
export declare function formatServerCostLines(spend: SpendResponse, headroom?: Headroom | null, droppedRunCount?: number): string;
|
|
124
127
|
export interface RegisterCostDeps {
|
|
125
128
|
/** Fetch remaining credit headroom; return null when unavailable. Fail-soft. */
|
|
126
129
|
fetchHeadroom?: (signal?: AbortSignal) => Promise<Headroom | null>;
|
|
@@ -132,15 +135,6 @@ export interface RegisterCostDeps {
|
|
|
132
135
|
* ordinary caller row.
|
|
133
136
|
*/
|
|
134
137
|
advisorSubtotal?: () => string;
|
|
135
|
-
/**
|
|
136
|
-
* Whether the session is currently boosted to Peak (boostCommand.ts's
|
|
137
|
-
* `isBoosted()`, threaded in the same way as `advisorSubtotal`). Unlike
|
|
138
|
-
* `advisorSubtotal`, this is read on BOTH the server-authoritative and the
|
|
139
|
-
* local-fallback branch: the driver's own turns never carry a server-side
|
|
140
|
-
* boost marker (see boostCommand.ts's KNOWN-asymmetry docblock), so this is
|
|
141
|
-
* the only signal that would otherwise be missing from the server branch.
|
|
142
|
-
*/
|
|
143
|
-
isBoosted?: () => boolean;
|
|
144
138
|
/**
|
|
145
139
|
* Fetch the server-authoritative session spend (YAG-383). Absent, throwing,
|
|
146
140
|
* or resolving null all fall back to the local `turn_end` accumulator, with
|
|
@@ -75,21 +75,18 @@ const fmt = (n) => n.toLocaleString("en-US");
|
|
|
75
75
|
* every advisor consult — never emits a parent `turn_end`, so it is invisible
|
|
76
76
|
* here. The advisor subtotal is threaded in explicitly for exactly that reason.
|
|
77
77
|
*/
|
|
78
|
-
|
|
79
|
-
const BOOST_LINE = "Boost is on. Driver turns bill at the peak tier.";
|
|
80
|
-
export function formatCostLine(snap, headroom, advisorLine, source, boosted = false) {
|
|
78
|
+
export function formatCostLine(snap, headroom, advisorLine, source) {
|
|
81
79
|
const turns = `${snap.turns} turn${snap.turns === 1 ? "" : "s"}`;
|
|
82
80
|
const cached = snap.cacheRead > 0 ? ` (${fmt(snap.cacheRead)} cached)` : "";
|
|
83
81
|
const label = source ? `Session usage (${source})` : "Session usage";
|
|
84
82
|
const base = `${label}: ${turns}, ${fmt(snap.input)} in / ${fmt(snap.output)} out tokens${cached}, ` +
|
|
85
83
|
`$${snap.cost.toFixed(2)} this session.`;
|
|
86
84
|
const advisor = advisorLine?.trim() ? ` ${advisorLine.trim()}` : "";
|
|
87
|
-
const boost = boosted ? ` ${BOOST_LINE}` : "";
|
|
88
85
|
if (headroom)
|
|
89
|
-
return `${base}${advisor}
|
|
86
|
+
return `${base}${advisor} Credit headroom: ${headroom.remaining} ${headroom.unit}.`;
|
|
90
87
|
if (headroom === null)
|
|
91
|
-
return `${base}${advisor}
|
|
92
|
-
return `${base}${advisor}
|
|
88
|
+
return `${base}${advisor} Credit headroom unavailable right now.`;
|
|
89
|
+
return `${base}${advisor}`;
|
|
93
90
|
}
|
|
94
91
|
/**
|
|
95
92
|
* Millicents (1/1000 of a cent) -> dollars, 2 decimals. Guards against a
|
|
@@ -115,25 +112,15 @@ export const usd = (millicents) => {
|
|
|
115
112
|
* (joined with "\n").
|
|
116
113
|
*
|
|
117
114
|
* Line order: total spend, one line per rate tier (aggregated across callers,
|
|
118
|
-
* sorted by spend descending),
|
|
119
|
-
* boosted rows, a live-toggle boost line (see below), the savings line (only
|
|
115
|
+
* sorted by spend descending), the savings line (only
|
|
120
116
|
* when a counterfactual total resolved), an incomplete-counterfactual note,
|
|
121
117
|
* an unbilled note, a dropped-run-ids note (carry-over from the /cost
|
|
122
118
|
* re-review — see sessionRuns.ts's `droppedSessionRuns`), then headroom.
|
|
123
119
|
*
|
|
124
120
|
* `droppedRunCount` defaults to 0 (no note) so every existing direct caller
|
|
125
121
|
* of this pure function keeps behaving identically without passing it.
|
|
126
|
-
*
|
|
127
|
-
* `boosted` (default false) is the LIVE client-side toggle from
|
|
128
|
-
* `boostCommand.ts`'s `isBoosted()`, threaded in exactly like `advisorLine`
|
|
129
|
-
* is for {@link formatCostLine}. It is independent of the per-tier "Boosted
|
|
130
|
-
* (tier) spend" subtotal above: that subtotal only reflects server rows
|
|
131
|
-
* carrying `x-yagni-boost`, which only `/go` children (and other spawned
|
|
132
|
-
* processes) ever send — the driver's own turns never do (see
|
|
133
|
-
* boostCommand.ts's KNOWN-asymmetry docblock). Without this flag, a session
|
|
134
|
-
* that only chats while boosted would show no boost line at all.
|
|
135
122
|
*/
|
|
136
|
-
export function formatServerCostLines(spend, headroom, droppedRunCount = 0
|
|
123
|
+
export function formatServerCostLines(spend, headroom, droppedRunCount = 0) {
|
|
137
124
|
const lines = [`Session spend: $${usd(spend.totalSellMillicents)} (server).`];
|
|
138
125
|
const tierTotals = new Map();
|
|
139
126
|
for (const row of spend.rows) {
|
|
@@ -147,23 +134,6 @@ export function formatServerCostLines(spend, headroom, droppedRunCount = 0, boos
|
|
|
147
134
|
const calls = `${fmt(agg.dispatches)} call${agg.dispatches === 1 ? "" : "s"}`;
|
|
148
135
|
lines.push(` ${tier}: $${usd(agg.sellMillicents)} over ${calls}.`);
|
|
149
136
|
}
|
|
150
|
-
const boostTotals = new Map();
|
|
151
|
-
for (const row of spend.rows) {
|
|
152
|
-
if (!row.boost)
|
|
153
|
-
continue;
|
|
154
|
-
boostTotals.set(row.rateTier, (boostTotals.get(row.rateTier) ?? 0) + row.sellMillicents);
|
|
155
|
-
}
|
|
156
|
-
if (boostTotals.size > 0) {
|
|
157
|
-
for (const [tier] of tiersSorted) {
|
|
158
|
-
const tierBoosted = boostTotals.get(tier);
|
|
159
|
-
if (tierBoosted === undefined)
|
|
160
|
-
continue;
|
|
161
|
-
lines.push(` Boosted (${tier}) spend: $${usd(tierBoosted)}.`);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
if (boosted) {
|
|
165
|
-
lines.push(BOOST_LINE);
|
|
166
|
-
}
|
|
167
137
|
// typeof guard (not just !== null): a network response is untyped at
|
|
168
138
|
// runtime, and a stray string/boolean here must never sneak "NN%" into the
|
|
169
139
|
// rendered line.
|
|
@@ -233,15 +203,6 @@ export function registerCostCommand(pi, deps = {}) {
|
|
|
233
203
|
: Promise.resolve(null),
|
|
234
204
|
]);
|
|
235
205
|
const localSnap = acc.snapshot();
|
|
236
|
-
// Read fresh on every /cost call, both branches (see RegisterCostDeps's
|
|
237
|
-
// isBoosted doc comment for why the local fallback needs it too).
|
|
238
|
-
let boosted = false;
|
|
239
|
-
try {
|
|
240
|
-
boosted = deps.isBoosted?.() ?? false;
|
|
241
|
-
}
|
|
242
|
-
catch {
|
|
243
|
-
/* a diagnostic must never break /cost */
|
|
244
|
-
}
|
|
245
206
|
// Quiet divergence check: both totals must be available, and it never
|
|
246
207
|
// affects what the user sees. Scoped to `caller === "driver"` rows only
|
|
247
208
|
// (like-for-like with the local `turn_end` accumulator, which can only
|
|
@@ -276,7 +237,7 @@ export function registerCostCommand(pi, deps = {}) {
|
|
|
276
237
|
catch {
|
|
277
238
|
/* a diagnostic must never break /cost */
|
|
278
239
|
}
|
|
279
|
-
await pi.sendUserMessage(formatServerCostLines(spend, headroom, dropped
|
|
240
|
+
await pi.sendUserMessage(formatServerCostLines(spend, headroom, dropped));
|
|
280
241
|
return;
|
|
281
242
|
}
|
|
282
243
|
let advisorLine = "";
|
|
@@ -286,7 +247,7 @@ export function registerCostCommand(pi, deps = {}) {
|
|
|
286
247
|
catch {
|
|
287
248
|
/* usage accounting must never break /cost */
|
|
288
249
|
}
|
|
289
|
-
await pi.sendUserMessage(formatCostLine(localSnap, headroom, advisorLine, "local, driver only"
|
|
250
|
+
await pi.sendUserMessage(formatCostLine(localSnap, headroom, advisorLine, "local, driver only"));
|
|
290
251
|
},
|
|
291
252
|
});
|
|
292
253
|
}
|
|
@@ -125,8 +125,6 @@ export function makeCrashReporter(opts) {
|
|
|
125
125
|
if (crashReportsDisabled(env))
|
|
126
126
|
return;
|
|
127
127
|
const token = opts.getToken();
|
|
128
|
-
if (!token)
|
|
129
|
-
return;
|
|
130
128
|
const sanitized = sanitizeCrashError(error, { env, repoRoot });
|
|
131
129
|
const payload = {
|
|
132
130
|
client: isDesktopSurface() ? "desktop" : "cli",
|
|
@@ -148,7 +146,7 @@ export function makeCrashReporter(opts) {
|
|
|
148
146
|
method: "POST",
|
|
149
147
|
headers: {
|
|
150
148
|
"content-type": "application/json",
|
|
151
|
-
authorization: `Bearer ${token}
|
|
149
|
+
...(token ? { authorization: `Bearer ${token}` } : {}),
|
|
152
150
|
},
|
|
153
151
|
body: JSON.stringify(payload),
|
|
154
152
|
signal: controller.signal,
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exec policy engine — classifies bash commands via prefix rules + lightweight
|
|
3
|
+
* shell tokenization (YAG-504, restructured in YAG-510).
|
|
4
|
+
*
|
|
5
|
+
* Pure: no I/O, no network, no model. Loads at startup and classifies
|
|
6
|
+
* synchronously. The curated default set auto-allows read-only commands
|
|
7
|
+
* (ls, cat, rg, git status/log/diff), forbids destructive ones (rm -rf,
|
|
8
|
+
* git reset --hard, git push --force, pipe-to-shell), and prompts for the
|
|
9
|
+
* ambiguous middle band (npm install, git commit, curl, …).
|
|
10
|
+
*
|
|
11
|
+
* The `prompt` band is what the Guardian arbitrates — see guardian.ts.
|
|
12
|
+
*
|
|
13
|
+
* Classification composes three signals and takes the STRICTEST:
|
|
14
|
+
* 1. prefix-rule matching on every segment (newlines, ;, &&, ||, | split);
|
|
15
|
+
* 2. a construct floor — commands using redirects, substitution, or
|
|
16
|
+
* background & can never be auto-allowed (floor: prompt);
|
|
17
|
+
* 3. dangerScan — a best-effort sweep of command-substitution inner text
|
|
18
|
+
* ($(...) and backticks, including inside double quotes) against the
|
|
19
|
+
* FORBIDDEN rules only. Danger anywhere upgrades to forbidden; the scan
|
|
20
|
+
* can never make anything more permissive.
|
|
21
|
+
* This is the codex two-parser lesson: fail closed to prove safety, scan
|
|
22
|
+
* best-effort to prove danger. A forbidden match must win even when the
|
|
23
|
+
* command also carries constructs (`rm -rf / &` is forbidden, not prompt).
|
|
24
|
+
*
|
|
25
|
+
* Command words are matched through a leading-token strip (env assignments,
|
|
26
|
+
* sudo/env/command wrappers, shell reserved words, a leading backslash) and
|
|
27
|
+
* basename normalization (/bin/rm → rm) — both applied ASYMMETRICALLY: they
|
|
28
|
+
* can make a command land on forbidden/prompt rules, but a stripped or
|
|
29
|
+
* path-prefixed command is never auto-allowed (`sudo ls` and `./ls` stay in
|
|
30
|
+
* the prompt band; an attacker-named local `./rm` binary must not ride the
|
|
31
|
+
* allow list, and `/bin/ls` pays the same price by design).
|
|
32
|
+
*
|
|
33
|
+
* Tokenization is a lightweight inline parser — not shell-quote — because the
|
|
34
|
+
* extension is bundled into @yagni-app/code's dist (a file copy, not a real
|
|
35
|
+
* bundler), and external dependencies aren't resolvable from the bundled path.
|
|
36
|
+
*/
|
|
37
|
+
export type TokenEntry = string | {
|
|
38
|
+
op: "pipe" | "and" | "or" | "semi" | "redirect" | "substitution";
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Parse a shell command string into tokens and control operators.
|
|
42
|
+
*
|
|
43
|
+
* Handles:
|
|
44
|
+
* - Single and double quoted strings (preserves spaces inside)
|
|
45
|
+
* - Control operators: |, &&, ||, ;, and newlines (a newline separates
|
|
46
|
+
* commands exactly like `;` — treating it as whitespace let multiline
|
|
47
|
+
* commands smuggle anything behind an allow-listed first line)
|
|
48
|
+
* - `#` comments (start-of-word to end-of-line, outside quotes)
|
|
49
|
+
* - Shell constructs we flag as unanalyzable: $(), backticks (INCLUDING
|
|
50
|
+
* inside double quotes — bash executes those), >, <, background &
|
|
51
|
+
*
|
|
52
|
+
* Does NOT handle: variable expansion, glob patterns, heredocs beyond the
|
|
53
|
+
* redirect flag, nested subshells beyond depth tracking. Commands using
|
|
54
|
+
* those are classified as "prompt" at minimum (construct floor).
|
|
55
|
+
*/
|
|
56
|
+
export declare function shellParse(command: string): TokenEntry[];
|
|
57
|
+
/**
|
|
58
|
+
* Extract the inner text of every command substitution — $(...) and
|
|
59
|
+
* backticks — respecting single-quote literalness and backslash escapes.
|
|
60
|
+
* Includes substitutions inside double quotes (bash executes those).
|
|
61
|
+
* Best-effort, used ONLY by dangerScan to prove danger, never safety.
|
|
62
|
+
*/
|
|
63
|
+
export declare function extractSubstitutions(command: string): string[];
|
|
64
|
+
export type ExecDecision = "allow" | "prompt" | "forbidden";
|
|
65
|
+
export interface PrefixRule {
|
|
66
|
+
/** Ordered tokens; a string[] element means alternatives (any match). */
|
|
67
|
+
pattern: (string | string[])[];
|
|
68
|
+
decision: ExecDecision;
|
|
69
|
+
justification: string;
|
|
70
|
+
/**
|
|
71
|
+
* Escape hatch for allow rules whose command has a mutating flag: if any
|
|
72
|
+
* token AFTER the matched prefix equals one of these (or, for entries ending
|
|
73
|
+
* in "*", starts with the part before the star), the rule does NOT match and
|
|
74
|
+
* evaluation falls through to later rules (usually landing in the prompt
|
|
75
|
+
* band). Example: sed is read-only except with -i/--in-place.
|
|
76
|
+
*/
|
|
77
|
+
unlessTokens?: string[];
|
|
78
|
+
/**
|
|
79
|
+
* Position-independent flag requirement: the rule matches only when, in
|
|
80
|
+
* addition to the pattern prefix, at least one token AFTER the prefix
|
|
81
|
+
* matches an entry (same "*"-suffix glob convention as unlessTokens).
|
|
82
|
+
* Used by forbidden rules to catch permuted flags: `git push origin
|
|
83
|
+
* --force` and `rm x -rf` place the dangerous flag after positional args,
|
|
84
|
+
* where exact-position patterns never see it.
|
|
85
|
+
*/
|
|
86
|
+
flagsAnywhere?: string[];
|
|
87
|
+
/** Positive test invocations (validated at load if present). */
|
|
88
|
+
match?: string[][];
|
|
89
|
+
/** Negative test invocations (validated at load if present). */
|
|
90
|
+
notMatch?: string[][];
|
|
91
|
+
}
|
|
92
|
+
export interface ExecPolicy {
|
|
93
|
+
rules: PrefixRule[];
|
|
94
|
+
}
|
|
95
|
+
export interface ExecClassification {
|
|
96
|
+
decision: ExecDecision;
|
|
97
|
+
justification: string;
|
|
98
|
+
matchedRule?: PrefixRule;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Parse a command string into tokens using our lightweight tokenizer. Returns
|
|
102
|
+
* string tokens only (control operators and constructs are filtered out —
|
|
103
|
+
* detected separately).
|
|
104
|
+
*/
|
|
105
|
+
export declare function tokenize(command: string): string[];
|
|
106
|
+
/**
|
|
107
|
+
* Classify a full bash command string against the exec policy.
|
|
108
|
+
*
|
|
109
|
+
* Compound commands (pipes, &&, ||, ;, newlines) are split into segments and
|
|
110
|
+
* each is classified independently; the strictest decision wins (forbidden >
|
|
111
|
+
* prompt > allow). Commands with shell constructs (substitution, redirects,
|
|
112
|
+
* background &) have a floor of `prompt`, and their substitution inner text
|
|
113
|
+
* is danger-scanned against the forbidden rules. Pipe-to-shell is always
|
|
114
|
+
* forbidden.
|
|
115
|
+
*/
|
|
116
|
+
export declare function classifyCommand(command: string, policy: ExecPolicy): ExecClassification;
|
|
117
|
+
/** Curated default rules — the shipped safety floor. */
|
|
118
|
+
export declare const DEFAULT_EXEC_POLICY: ExecPolicy;
|
|
119
|
+
//# sourceMappingURL=execPolicy.d.ts.map
|