@sema-agent/core 7.47.0 → 7.48.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 +54 -0
- package/dist/core/allow-entry.js +1 -1
- package/dist/core/checkpoint-store.d.ts +1 -1
- package/dist/core/mcp-server-spec.d.ts +1 -1
- package/dist/core/runner/park-commit.d.ts +1 -1
- package/dist/core/safety-axis-vocab.d.ts +1 -1
- package/dist/core/safety-merge-corpus.d.ts +1 -1
- package/dist/core/session-policy-store.d.ts +6 -3
- package/dist/core/session-policy-store.js +28 -10
- package/dist/core/shell-wrapper-table.d.ts +2 -2
- package/dist/core/shell-wrapper-table.js +1 -1
- package/dist/core/tool-name-aliases.d.ts +9 -8
- package/dist/core/tool-name-aliases.js +2 -0
- package/dist/core/tool-policy.js +4 -5
- package/dist/orchestration/workflow.js +2 -2
- package/dist/prompt-assembly/types.d.ts +1 -1
- package/dist/shell/lex.js +30 -4
- package/dist/tools/fs/bash-program-position.d.ts +7 -0
- package/dist/tools/fs/bash-program-position.js +5 -2
- package/dist/tools/fs/index.d.ts +3 -2
- package/dist/tools/fs/index.js +2 -2
- package/dist/tools/fs/program-read-model.d.ts +60 -5
- package/dist/tools/fs/program-read-model.js +100 -36
- package/dist/tools/fs/program-read-model.rows.js +13 -5
- package/dist/tools/fs/read-deny-home.d.ts +20 -1
- package/dist/tools/fs/read-deny-home.js +28 -7
- package/dist/tools/fs/read-deny.d.ts +13 -6
- package/dist/tools/fs/read-deny.js +12 -3
- package/dist/tools/fs/removal-targets.js +12 -3
- package/dist/tools/fs/shell-read-argv.d.ts +4 -7
- package/dist/tools/fs/shell-read-argv.js +5 -7
- package/dist/tools/fs/shell-read-bindings.d.ts +34 -9
- package/dist/tools/fs/shell-read-bindings.js +166 -60
- package/dist/tools/fs/shell-read-establish.js +3 -4
- package/dist/tools/fs/shell-read-plan.d.ts +1 -14
- package/dist/tools/fs/shell-read-plan.js +102 -101
- package/dist/tools/fs/shell-read-start.d.ts +57 -0
- package/dist/tools/fs/shell-read-start.js +92 -0
- package/dist/tools/fs/shell-read-word.d.ts +35 -6
- package/dist/tools/fs/shell-read-word.js +85 -9
- package/dist/tools/fs/shell-reentry-text.d.ts +23 -21
- package/dist/tools/fs/shell-reentry-text.js +31 -26
- package/package.json +1 -1
- package/wire-changes/core@7.48.0.json +252 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { argvOf, lexShell, literalTildeSpelling, UNRESOLVED_SENTENCE } from "../../shell/lex.js";
|
|
2
2
|
import { expansionInWord, extglobOpenerIn, globInWord, homeExpansionOf } from "./bash-readonly-classifier.js";
|
|
3
|
-
import { wordFloorValues, wordValues } from "./shell-read-bindings.js";
|
|
4
|
-
export function readsOf(words, table, homeDir) {
|
|
3
|
+
import { floorOf, judgedSpellingOf, namesIn, wordFloorValues, wordValues } from "./shell-read-bindings.js";
|
|
4
|
+
export function readsOf(words, table, homeDir, split = false) {
|
|
5
5
|
return words.map((word) => {
|
|
6
|
-
const refersToName = word.pieces.some((p) => p.expansion?.kind === "parameter");
|
|
6
|
+
const refersToName = word.pieces.some((p) => p.expansion?.kind === "parameter") || namesIn(word).length > 0;
|
|
7
7
|
const asWritten = { word, raw: word.raw, folded: word.folded, ...(word.judged !== undefined ? { pattern: word.judged } : {}) };
|
|
8
8
|
if (!refersToName && table?.homeTouched !== true)
|
|
9
9
|
return { ...asWritten, expands: expandsOf(word) };
|
|
10
|
-
const values = wordValues(word, table, false);
|
|
10
|
+
const values = wordValues(word, table, false, undefined, split);
|
|
11
11
|
if (values === undefined)
|
|
12
12
|
return { ...asWritten, expands: expandsOf(word) };
|
|
13
13
|
if (values === "unknown") {
|
|
@@ -18,7 +18,7 @@ export function readsOf(words, table, homeDir) {
|
|
|
18
18
|
const refersToHome = word.pieces.some((p) => p.expansion?.kind === "tilde" || (p.expansion?.kind === "parameter" && p.expansion.name === "HOME"));
|
|
19
19
|
const declared = refersToHome && homeDir !== undefined && table?.homeTouched === true ? wordValues(word, table, false, new Map([["HOME", [{ spelled: homeDir }]]])) : undefined;
|
|
20
20
|
const floor = declared !== undefined && declared !== "unknown" && declared[0] !== undefined ? spellingOf(declared[0]) : undefined;
|
|
21
|
-
const pattern =
|
|
21
|
+
const pattern = judgedSpellingOf(values[0]);
|
|
22
22
|
return {
|
|
23
23
|
word,
|
|
24
24
|
raw: word.raw,
|
|
@@ -31,12 +31,88 @@ export function readsOf(words, table, homeDir) {
|
|
|
31
31
|
};
|
|
32
32
|
});
|
|
33
33
|
}
|
|
34
|
+
const READINGS_MAX = 64;
|
|
35
|
+
export function segmentReadingsOf(argv, targets, table, homeDir) {
|
|
36
|
+
const several = new Map();
|
|
37
|
+
for (const word of [...argv, ...targets]) {
|
|
38
|
+
for (const name of namesReadIn(word, table)) {
|
|
39
|
+
const state = table.stateOf(name);
|
|
40
|
+
if (state !== "unknown" && state.length > 1)
|
|
41
|
+
several.set(name, state);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
let overlays = [new Map()];
|
|
45
|
+
const past = [];
|
|
46
|
+
for (const [name, values] of several) {
|
|
47
|
+
if (past.length > 0 || overlays.length * values.length > READINGS_MAX) {
|
|
48
|
+
past.push(name);
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
overlays = overlays.flatMap((o) => values.map((v) => new Map(o).set(name, [v])));
|
|
52
|
+
}
|
|
53
|
+
for (const o of overlays)
|
|
54
|
+
for (const name of past)
|
|
55
|
+
o.set(name, "unknown");
|
|
56
|
+
return overlays.map((overlay) => {
|
|
57
|
+
const reading = overlay.size === 0 ? table : overlaidTable(table, overlay);
|
|
58
|
+
const fielded = readsOf(argv, reading, homeDir, true).flatMap((r) => {
|
|
59
|
+
const fields = r.values?.length === 1 ? r.values[0].fields : undefined;
|
|
60
|
+
return fields === undefined ? [r] : fields.map((f) => ({ word: argumentWordOf(f, f) ?? r.word, raw: f, folded: spellingOf({ spelled: f }), expands: false, settled: true, values: [{ spelled: f }] }));
|
|
61
|
+
});
|
|
62
|
+
const floors = new Map();
|
|
63
|
+
for (const r of fielded) {
|
|
64
|
+
if (r.settled === true)
|
|
65
|
+
continue;
|
|
66
|
+
for (const p of r.word.pieces) {
|
|
67
|
+
const name = p.expansion?.kind === "parameter" ? p.expansion.name : undefined;
|
|
68
|
+
if (name === undefined)
|
|
69
|
+
continue;
|
|
70
|
+
const held = reading.stateOf(name);
|
|
71
|
+
const floor = [...(held === "unknown" ? [] : held.map(floorOf)), ...reading.floorOf(name)];
|
|
72
|
+
if (floor.length > 0)
|
|
73
|
+
floors.set(name, floor);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return { table: reading, reads: fielded, argv: fielded.map((r) => valuedWord(r.word, r)), floors };
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
function namesReadIn(word, table) {
|
|
80
|
+
return [...namesIn(word), ...(table.homeTouched && word.pieces.some((p) => p.expansion?.kind === "tilde" && p.expansion.body === "~") ? ["HOME"] : [])];
|
|
81
|
+
}
|
|
82
|
+
function overlaidTable(table, overlay) {
|
|
83
|
+
return {
|
|
84
|
+
stateOf: (name) => overlay.get(name) ?? table.stateOf(name),
|
|
85
|
+
isInteger: (name) => table.isInteger(name),
|
|
86
|
+
ifsTouched: table.ifsTouched,
|
|
87
|
+
homeTouched: table.homeTouched,
|
|
88
|
+
floorOf: (name) => {
|
|
89
|
+
const held = table.stateOf(name);
|
|
90
|
+
return overlay.get(name) !== "unknown" || held === "unknown" ? table.floorOf(name) : [...table.floorOf(name), ...held.map(floorOf)];
|
|
91
|
+
},
|
|
92
|
+
inherited: table.inherited,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function valuedWord(word, r) {
|
|
96
|
+
const v = r.values?.length === 1 ? r.values[0] : undefined;
|
|
97
|
+
if (word.assignment !== undefined || v === undefined || v.glob === true || v.spelled === word.folded)
|
|
98
|
+
return word;
|
|
99
|
+
return argumentWordOf(v.spelled, r.raw) ?? word;
|
|
100
|
+
}
|
|
101
|
+
function argumentWordOf(text, raw) {
|
|
102
|
+
const quoted = text.replace(/[^A-Za-z0-9_./+@:%=,-]/g, (c) => (c === "'" ? "\\'" : `'${c}'`));
|
|
103
|
+
const lexed = lexShell(`: ${quoted === "" ? "''" : quoted}`);
|
|
104
|
+
const words = "parseFailure" in lexed || lexed.segments.length !== 1 ? undefined : argvOf(lexed.segments[0]);
|
|
105
|
+
return words?.length === 2 ? { ...words[1], raw } : undefined;
|
|
106
|
+
}
|
|
34
107
|
export const spellingOf = (v) => literalTildeSpelling(v.spelled);
|
|
35
|
-
export function
|
|
36
|
-
if (
|
|
108
|
+
export function partPattern(word, part) {
|
|
109
|
+
if (word.pattern === undefined || part === word.folded || !word.folded.endsWith(part))
|
|
110
|
+
return undefined;
|
|
111
|
+
const front = word.folded.slice(0, word.folded.length - part.length);
|
|
112
|
+
if (!word.pattern.startsWith(front))
|
|
37
113
|
return undefined;
|
|
38
|
-
const judged =
|
|
39
|
-
return judged ===
|
|
114
|
+
const judged = word.pattern.slice(front.length);
|
|
115
|
+
return judged === part ? undefined : judged;
|
|
40
116
|
}
|
|
41
117
|
export function renamed(c, spelled) {
|
|
42
118
|
const { pattern: _matched, ...rest } = c;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { type LexedSegment, type LexedWord } from "../../shell/lex.js";
|
|
8
8
|
import { type ProgramPosition } from "./bash-program-position.js";
|
|
9
|
+
import { type TargetUser } from "./program-read-model.js";
|
|
9
10
|
import type { BoundValue } from "./shell-read-bindings.js";
|
|
10
11
|
/**
|
|
11
12
|
* The code bodies a builtin keeps among its operands, one per body; empty for every other program. The program read
|
|
@@ -52,6 +53,8 @@ export interface LaunchedCommand {
|
|
|
52
53
|
readonly spelled: string;
|
|
53
54
|
};
|
|
54
55
|
readonly args: readonly LaunchedArg[];
|
|
56
|
+
/** The user the launcher runs it as, where its row says it runs another ({@link targetUserOf}). */
|
|
57
|
+
readonly as?: TargetUser;
|
|
55
58
|
}
|
|
56
59
|
/**
|
|
57
60
|
* What a launcher that runs a program named by no word of its own runs ({@link readProgramPosition}'s `runsShell`: the
|
|
@@ -79,11 +82,11 @@ export declare function launchedReadings(argv: readonly LexedWord[], command: La
|
|
|
79
82
|
readonly shell?: string;
|
|
80
83
|
readonly line?: string;
|
|
81
84
|
};
|
|
82
|
-
/** What a launcher runs ({@link launchedCommands}, {@link launchedReadings}), as a reader of the command line reads it
|
|
83
|
-
* each shell's argv in two spellings — `texts` (a word's literal value, else `spell.text`) and
|
|
84
|
-
* with the argv index each position stands for (`words`),
|
|
85
|
-
*
|
|
86
|
-
* line a program that is no shell runs (`lines`, once each). */
|
|
85
|
+
/** What a launcher runs ({@link launchedCommands}, {@link launchedReadings}), as a reader of the command line reads it in
|
|
86
|
+
* one reading of its names: each shell's argv in two spellings — `texts` (a word's literal value, else `spell.text`) and
|
|
87
|
+
* `args` (`spell.folded`) — with the argv index each position stands for (`words`), its words' fields read off
|
|
88
|
+
* `spell.values` ({@link spellLaunched}) and the user it runs as (`as`); the word naming a shell (`su -s /bin/bash`,
|
|
89
|
+
* `programWords`); and each command line a program that is no shell runs (`lines`, once each). */
|
|
87
90
|
export declare function launchedInput(argv: readonly LexedWord[], pos: ProgramPosition, spell: {
|
|
88
91
|
readonly text: (k: number) => string;
|
|
89
92
|
readonly folded: (k: number) => string;
|
|
@@ -94,6 +97,7 @@ export declare function launchedInput(argv: readonly LexedWord[], pos: ProgramPo
|
|
|
94
97
|
readonly texts: readonly string[];
|
|
95
98
|
readonly args: readonly string[];
|
|
96
99
|
readonly words: readonly (number | undefined)[];
|
|
100
|
+
readonly as?: TargetUser;
|
|
97
101
|
}[];
|
|
98
102
|
readonly programWords: readonly number[];
|
|
99
103
|
readonly lines: readonly string[];
|
|
@@ -107,19 +111,16 @@ export interface LaunchedSpelling {
|
|
|
107
111
|
readonly values?: (at: number) => readonly BoundValue[] | undefined;
|
|
108
112
|
}
|
|
109
113
|
/**
|
|
110
|
-
* The argv a launched shell is handed ({@link launchedCommands}), spelled as `spell` spells the command line's words
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
* combinations reading `i` hands each word its `i`-th value (its last where it holds fewer) — every value of every word is
|
|
121
|
-
* still read, the same word spelled twice takes one value per reading as the shell expands it, and the readings stay as
|
|
122
|
-
* many as one word's values: their cost is linear in the command, whatever its length.
|
|
114
|
+
* The argv a launched shell is handed ({@link launchedCommands}), spelled as `spell` spells the command line's words in ONE
|
|
115
|
+
* reading of their names (the plan reads a segment once per combination of its names' values, `segmentReadingsOf` in
|
|
116
|
+
* shell-read-word.ts — a word's value here is its value in that reading). A word an escaping launcher joins (`sudo -s
|
|
117
|
+
* CMD…`) is handed as the launcher hands it: the FIELD the command line's shell gives the launcher, escaped
|
|
118
|
+
* ({@link sudoEscaped}) — the field a literal word spells, or the field the shell established for a word it rewrites first
|
|
119
|
+
* (`X='#'; sudo -s rm -r "$X" d` hands `rm -r \# d`: the value is an operand, never a comment), a pattern's literal
|
|
120
|
+
* characters escaped and its pattern characters bare ({@link launchedSpellingOf}). A word whose field the reader does not
|
|
121
|
+
* know is handed AS WRITTEN: the launched shell re-reads the expansion with the command line's quoting, so the word stays
|
|
122
|
+
* one argument (an unknown there) and its neighbours stay theirs (`"#$Y" d` is never `#$Y d`). Any other launcher hands a
|
|
123
|
+
* word's text as `spell.word` spells it. One argv, as a list of the one.
|
|
123
124
|
*/
|
|
124
125
|
export declare function spellLaunched(argv: readonly LexedWord[], args: readonly LaunchedArg[], spell: LaunchedSpelling): string[][];
|
|
125
126
|
/** The argv index of the SCRIPT FILE a shell runs where its argv carries no text ({@link scriptSourceOf}). */
|
|
@@ -132,9 +133,10 @@ export interface StdinText {
|
|
|
132
133
|
}
|
|
133
134
|
/** What may arrive on a segment's standard input: each here-document (its descriptor is not read: every one counts) —
|
|
134
135
|
* its body, literal when quoted or when nothing in it expands — each here-string (literal when its word is), `<` file
|
|
135
|
-
* or `<&` descriptor written to no other descriptor, and the pipe it is reached through
|
|
136
|
-
*
|
|
137
|
-
|
|
136
|
+
* or `<&` descriptor written to no other descriptor, and what it inherits (`fed`: the pipe it is reached through, the
|
|
137
|
+
* pipe or the input of the group it stands in, the input of the shell running its text) unless one of those three
|
|
138
|
+
* replaces it. */
|
|
139
|
+
export declare function stdinTextsOf(segment: LexedSegment, fed: readonly StdinText[]): StdinText[];
|
|
138
140
|
/** What one segment (its argv) writes into the pipe after it: the text a printer spells ({@link printedText}) when its
|
|
139
141
|
* program word and every argument are literal, else only the segment as written. */
|
|
140
142
|
export declare function pipedTextOf(segment: LexedSegment, argv: readonly LexedWord[]): StdinText;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { literalHeredocBody, literalTextOf } from "../../shell/lex.js";
|
|
2
2
|
import { programBasename, readProgramPosition } from "./bash-program-position.js";
|
|
3
|
-
import { implementationsOf, printedText, programRow, readArgRoles, selectedOptions } from "./program-read-model.js";
|
|
3
|
+
import { implementationRejects, implementationsOf, printedText, programRow, readArgRoles, selectedOptions, targetUserOf } from "./program-read-model.js";
|
|
4
4
|
import { namesStandardInput } from "./safety.js";
|
|
5
5
|
export function codeBodies(basename, args) {
|
|
6
6
|
const row = programRow(basename);
|
|
@@ -77,17 +77,27 @@ export function launchedCommands(argv, pos = readProgramPosition(argv)) {
|
|
|
77
77
|
const row = programRow(programBasename(argv[runs.launcher].folded));
|
|
78
78
|
const words = argv.slice(runs.from).map((w) => w.folded);
|
|
79
79
|
const at = (k) => runs.from + k;
|
|
80
|
-
if (
|
|
81
|
-
return [{ args: [] }];
|
|
82
|
-
if (row.launcher.shellText === "user")
|
|
80
|
+
if (row?.launcher?.shellText === "user")
|
|
83
81
|
return userCommands(row, words, at);
|
|
82
|
+
const user = row === undefined ? undefined : targetUserOf(row, argv.slice(runs.launcher + 1, runs.from).map((w) => w.folded));
|
|
83
|
+
const as = user !== undefined ? { as: user } : {};
|
|
84
|
+
if (words.length === 0 || row?.launcher === undefined)
|
|
85
|
+
return [{ args: [], ...as }];
|
|
84
86
|
if (row.launcher.shellText === "joined") {
|
|
85
|
-
const
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
const texts = new Map();
|
|
88
|
+
for (const view of implementationsOf(row)) {
|
|
89
|
+
const events = readArgRoles(view, words);
|
|
90
|
+
const first = events.find((e) => e.kind === "operand" || e.kind === "dash-word-operand")?.at ?? words.length;
|
|
91
|
+
if (implementationRejects(row, view, words.slice(0, first)))
|
|
92
|
+
continue;
|
|
93
|
+
const joined = words.slice(first).map((_, k) => at(first + k));
|
|
94
|
+
const values = events.flatMap((e) => (e.kind === "option" || (e.kind === "value" && e.at < first) ? roleValue(e, "shell-text", words, at) ?? [] : []));
|
|
95
|
+
for (const t of [...(joined.length === 0 ? [] : [{ joined }]), ...values])
|
|
96
|
+
texts.set(JSON.stringify(t), t);
|
|
97
|
+
}
|
|
98
|
+
return texts.size === 0 ? [{ args: [], ...as }] : [...texts.values()].map((t) => ({ args: [{ fixed: "-c" }, t], ...as }));
|
|
89
99
|
}
|
|
90
|
-
return [{ args: [{ fixed: "-c" }, { joined: words.map((_, k) => at(k)), escaped: true }] }];
|
|
100
|
+
return [{ args: [{ fixed: "-c" }, { joined: words.map((_, k) => at(k)), escaped: true }], ...as }];
|
|
91
101
|
}
|
|
92
102
|
function roleValue(e, kind, words, at) {
|
|
93
103
|
if (e.kind === "value" && e.role?.kind === kind)
|
|
@@ -109,16 +119,18 @@ function userCommands(row, words, at) {
|
|
|
109
119
|
const named = own.flatMap((e) => valued(e, "command") ?? []).at(-1);
|
|
110
120
|
const fromEnvironment = selectedOptions(view, stops ? words.slice(0, end) : words, "one-implementation").some((o) => o.mode === "names-program");
|
|
111
121
|
const program = named !== undefined ? { program: named } : fromEnvironment ? { program: { spelled: "\"$SHELL\"" } } : {};
|
|
122
|
+
const target = targetUserOf(view, stops ? words.slice(0, end) : words.filter((_, k) => k !== user), { name: user === undefined ? "root" : words[user], dash: operands[0]?.dash !== undefined });
|
|
123
|
+
const runs = { ...program, ...(target !== undefined ? { as: target } : {}) };
|
|
112
124
|
if (stops) {
|
|
113
|
-
out.push({ ...
|
|
125
|
+
out.push({ ...runs, args: user === undefined ? [] : words.slice(user + 1).map((_, k) => ({ at: at(user + 1 + k) })) });
|
|
114
126
|
continue;
|
|
115
127
|
}
|
|
116
128
|
const rest = operands.filter((e) => user !== undefined && e.at > user).map((e) => ({ at: at(e.at) }));
|
|
117
129
|
const texts = own.flatMap((e) => valued(e, "shell-text") ?? []);
|
|
118
130
|
if (texts.length === 0)
|
|
119
|
-
out.push({ ...
|
|
131
|
+
out.push({ ...runs, args: rest });
|
|
120
132
|
for (const text of texts)
|
|
121
|
-
out.push({ ...
|
|
133
|
+
out.push({ ...runs, args: [{ fixed: "-c" }, text, ...rest] });
|
|
122
134
|
}
|
|
123
135
|
return out;
|
|
124
136
|
}
|
|
@@ -156,11 +168,10 @@ export function launchedInput(argv, pos, spell) {
|
|
|
156
168
|
programWords.push(c.program.at);
|
|
157
169
|
const words = c.args.map((x) => ("at" in x && x.from === undefined ? x.at : undefined));
|
|
158
170
|
const args = asArgs(c.args);
|
|
159
|
-
return asTexts(c.args).map((texts, i) => ({ shell, texts, args: args[i], words }));
|
|
171
|
+
return asTexts(c.args).map((texts, i) => ({ shell, texts, args: args[i], words, ...(c.as !== undefined ? { as: c.as } : {}) }));
|
|
160
172
|
});
|
|
161
173
|
return { shells, programWords, lines: [...lines] };
|
|
162
174
|
}
|
|
163
|
-
const LAUNCHED_READINGS_MAX = 64;
|
|
164
175
|
function sudoEscaped(field) {
|
|
165
176
|
return field.replace(/[^A-Za-z0-9_$-]/gu, (c) => `\\${c}`);
|
|
166
177
|
}
|
|
@@ -168,20 +179,14 @@ function launchedSpellingOf(v) {
|
|
|
168
179
|
return v.glob === true ? v.parts.map((p) => (p.pattern ? p.text : sudoEscaped(p.text))).join("") : sudoEscaped(v.spelled);
|
|
169
180
|
}
|
|
170
181
|
export function spellLaunched(argv, args, spell) {
|
|
171
|
-
const
|
|
182
|
+
const field = (k) => {
|
|
172
183
|
const literal = literalTextOf(argv[k].pieces);
|
|
173
184
|
if (literal !== undefined)
|
|
174
|
-
return
|
|
185
|
+
return sudoEscaped(literal);
|
|
175
186
|
const values = spell.values?.(k);
|
|
176
|
-
return values ===
|
|
187
|
+
return values?.length === 1 ? launchedSpellingOf(values[0]) : argv[k].raw;
|
|
177
188
|
};
|
|
178
|
-
|
|
179
|
-
const distinct = new Map([...choices].map(([k, c]) => [argv[k].raw, c]));
|
|
180
|
-
const combinations = [...distinct.values()].reduce((n, c) => n * c.length, 1);
|
|
181
|
-
const readings = combinations <= LAUNCHED_READINGS_MAX
|
|
182
|
-
? [...distinct].reduce((acc, [w, c]) => acc.flatMap((r) => c.map((x) => new Map(r).set(w, x))), [new Map()]).map((r) => new Map([...choices.keys()].map((k) => [k, r.get(argv[k].raw)])))
|
|
183
|
-
: Array.from({ length: [...choices.values()].reduce((n, c) => Math.max(n, c.length), 1) }, (_, i) => new Map([...choices].map(([k, c]) => [k, c[Math.min(i, c.length - 1)]])));
|
|
184
|
-
return readings.map((reading) => args.map((a) => ("fixed" in a ? a.fixed : "at" in a ? spell.word(a.at, a.from ?? 0) : a.joined.map((k) => (a.escaped === true ? reading.get(k) : spell.word(k, 0))).join(" "))));
|
|
189
|
+
return [args.map((a) => ("fixed" in a ? a.fixed : "at" in a ? spell.word(a.at, a.from ?? 0) : a.joined.map((k) => (a.escaped === true ? field(k) : spell.word(k, 0))).join(" ")))];
|
|
185
190
|
}
|
|
186
191
|
export function scriptFileAt(basename, args) {
|
|
187
192
|
const row = programRow(basename);
|
|
@@ -198,7 +203,7 @@ function scriptSourceOf(row, args) {
|
|
|
198
203
|
return { stdin: true };
|
|
199
204
|
return { file: first.at, stdin: options.some((e) => e.kind === "option" && e.reading === "speculative") };
|
|
200
205
|
}
|
|
201
|
-
export function stdinTextsOf(segment,
|
|
206
|
+
export function stdinTextsOf(segment, fed) {
|
|
202
207
|
const out = [];
|
|
203
208
|
let replacesPipe = false;
|
|
204
209
|
for (const t of segment.tokens) {
|
|
@@ -212,7 +217,7 @@ export function stdinTextsOf(segment, piped) {
|
|
|
212
217
|
replacesPipe = true;
|
|
213
218
|
}
|
|
214
219
|
}
|
|
215
|
-
return
|
|
220
|
+
return replacesPipe ? out : [...fed, ...out];
|
|
216
221
|
}
|
|
217
222
|
export function pipedTextOf(segment, argv) {
|
|
218
223
|
const from = `${segment.text.trim()} |`;
|
package/package.json
CHANGED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
{
|
|
2
|
+
"repo": "core",
|
|
3
|
+
"version": "7.48.0",
|
|
4
|
+
"from": "b29642cbf7daa459e48226114b2ac2132e855036",
|
|
5
|
+
"to": "7161f2a278ed51f0154de745fd7f6a4f8d349039",
|
|
6
|
+
"added": [],
|
|
7
|
+
"removed": [],
|
|
8
|
+
"changed": [],
|
|
9
|
+
"narrowed": [
|
|
10
|
+
{
|
|
11
|
+
"id": "F1",
|
|
12
|
+
"source": "behaviorFlips.summary",
|
|
13
|
+
"text": "BEHAVIOR NARROWING, open to veto (#1461): a word that a name's established value spells is read as that value at the program position and as an operand — a `Bash` allow no longer runs `P=rm; $P -rf <root>` without a card."
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"id": "F2",
|
|
17
|
+
"source": "behaviorFlips.summary",
|
|
18
|
+
"text": "BEHAVIOR NARROWING, open to veto (#1461, completed): each field of a split unquoted value is a word of its own — named by its text, its `NAME=value` shape kept — for every reader, the removal guard included."
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "F7",
|
|
22
|
+
"source": "behaviorFlips.summary",
|
|
23
|
+
"text": "BEHAVIOR NARROWING (#1459): every launcher field asks the one value table — `su -c` / `runuser -c` / `watch` / `bash -c` hand every established value of a name, as `sudo -s` already did."
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "F8",
|
|
27
|
+
"source": "behaviorFlips.summary",
|
|
28
|
+
"text": "BEHAVIOR NARROWING (#1460): the combination bound counts distinct names and nothing is sampled past it — the words past the bound are handed on as written with their values as deny floors."
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"id": "F9",
|
|
32
|
+
"source": "behaviorFlips.summary",
|
|
33
|
+
"text": "BEHAVIOR NARROWING (#1446): a launcher option cluster is read as getopt reads it, its command-line letter included."
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"id": "F10",
|
|
37
|
+
"source": "behaviorFlips.summary",
|
|
38
|
+
"text": "BEHAVIOR NARROWING (#1452): `watch` declares its options (procps and BusyBox) and the words from its first operand on are the shell text; the exported `SHELL_WRAPPER_TABLE` `watch` row gains its option sets."
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"id": "F11",
|
|
42
|
+
"source": "behaviorFlips.summary",
|
|
43
|
+
"text": "BEHAVIOR NARROWING (#1463): a shell run as another user starts with that user's home — a login shell stands in it, `HOME` there is the target's."
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"id": "F12",
|
|
47
|
+
"source": "behaviorFlips.summary",
|
|
48
|
+
"text": "BEHAVIOR NARROWING, open to veto (#1424): standard input flows into a group and into an inner shell; a process-substitution script and a stream-fed shell are scripts built when the command runs — also for a harmless text."
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"id": "F13",
|
|
52
|
+
"source": "behaviorFlips.summary",
|
|
53
|
+
"text": "BEHAVIOR NARROWING (#1466): the judge sees every name bash opens — a part of a pattern word carries the word's widened pattern, a malformed bracket unit is a member as bash reads it, a field forms its pattern once it has fully expanded."
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"id": "F14",
|
|
57
|
+
"source": "behaviorFlips.summary",
|
|
58
|
+
"text": "BEHAVIOR NARROWING (#1471): session rules with a bucket that is not a list of strings are refused — a bare `\"rm\"` was stored as `[\"r\",\"m\"]`."
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"id": "F15",
|
|
62
|
+
"source": "behaviorFlips.summary",
|
|
63
|
+
"text": "BEHAVIOR NARROWING (#1400): a protocol name with an empty tool segment (`mcp__srv__`) is judged `unmintable_segment`, not `current` — a rule that guarded nothing in silence is refused or announced."
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"id": "F17",
|
|
67
|
+
"source": "behaviorFlips.summary",
|
|
68
|
+
"text": "BEHAVIOR NARROWING (#1442): a `readDenyPatterns` row that opens with `/` is rooted — it names that one absolute location (placed where its literal head leads through a link), not that tail at any depth."
|
|
69
|
+
}
|
|
70
|
+
],
|
|
71
|
+
"closedSetMembers": [],
|
|
72
|
+
"closedSetsUnreadable": [],
|
|
73
|
+
"typeKeys": [],
|
|
74
|
+
"typeKeysUnreadableArms": [],
|
|
75
|
+
"typeKeysFlattenedCount": 228,
|
|
76
|
+
"signatures": [],
|
|
77
|
+
"declarationMoves": [],
|
|
78
|
+
"mintMoves": [],
|
|
79
|
+
"criteriaObserves": [
|
|
80
|
+
{
|
|
81
|
+
"kind": "taskEventField",
|
|
82
|
+
"name": "tool_end.gate",
|
|
83
|
+
"assertions": [
|
|
84
|
+
"G1.a",
|
|
85
|
+
"G2.a",
|
|
86
|
+
"G2.c",
|
|
87
|
+
"G4.a",
|
|
88
|
+
"G4.b",
|
|
89
|
+
"G5.a",
|
|
90
|
+
"G5.c",
|
|
91
|
+
"G6.a",
|
|
92
|
+
"G7.a",
|
|
93
|
+
"G8.a",
|
|
94
|
+
"G8.b"
|
|
95
|
+
]
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"kind": "taskEventField",
|
|
99
|
+
"name": "tool_end.isError",
|
|
100
|
+
"assertions": [
|
|
101
|
+
"G12.a",
|
|
102
|
+
"G12.b"
|
|
103
|
+
]
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"kind": "taskEventField",
|
|
107
|
+
"name": "tool_end.output",
|
|
108
|
+
"assertions": [
|
|
109
|
+
"G12.a",
|
|
110
|
+
"G12.b"
|
|
111
|
+
]
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"kind": "typeMember",
|
|
115
|
+
"name": "AskRequest.askClass",
|
|
116
|
+
"assertions": [
|
|
117
|
+
"G1.a",
|
|
118
|
+
"G1.b",
|
|
119
|
+
"G1.c",
|
|
120
|
+
"G1.d",
|
|
121
|
+
"G2.a",
|
|
122
|
+
"G3.a",
|
|
123
|
+
"G3.b",
|
|
124
|
+
"G3.c",
|
|
125
|
+
"G3.d",
|
|
126
|
+
"G4.a",
|
|
127
|
+
"G4.c",
|
|
128
|
+
"G5.a",
|
|
129
|
+
"G5.b",
|
|
130
|
+
"G5.c",
|
|
131
|
+
"G5.d",
|
|
132
|
+
"G6.a",
|
|
133
|
+
"G6.b",
|
|
134
|
+
"G6.c",
|
|
135
|
+
"G7.a",
|
|
136
|
+
"G7.b",
|
|
137
|
+
"G8.c"
|
|
138
|
+
]
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"kind": "typeMember",
|
|
142
|
+
"name": "AskRequest.classes",
|
|
143
|
+
"assertions": [
|
|
144
|
+
"G2.b"
|
|
145
|
+
]
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"kind": "typeMember",
|
|
149
|
+
"name": "AskRequest.message",
|
|
150
|
+
"assertions": [
|
|
151
|
+
"G1.c",
|
|
152
|
+
"G3.b",
|
|
153
|
+
"G6.b",
|
|
154
|
+
"G7.b"
|
|
155
|
+
]
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"kind": "typeMember",
|
|
159
|
+
"name": "GateOutcome.disposition",
|
|
160
|
+
"assertions": [
|
|
161
|
+
"G1.b",
|
|
162
|
+
"G2.b",
|
|
163
|
+
"G2.c",
|
|
164
|
+
"G3.a",
|
|
165
|
+
"G3.c",
|
|
166
|
+
"G3.d",
|
|
167
|
+
"G4.b",
|
|
168
|
+
"G4.c",
|
|
169
|
+
"G5.b",
|
|
170
|
+
"G5.d",
|
|
171
|
+
"G6.c",
|
|
172
|
+
"G8.a",
|
|
173
|
+
"G8.b",
|
|
174
|
+
"G8.c"
|
|
175
|
+
]
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"kind": "typeMember",
|
|
179
|
+
"name": "SessionPolicyError.code",
|
|
180
|
+
"assertions": [
|
|
181
|
+
"G9.b",
|
|
182
|
+
"G10.a"
|
|
183
|
+
]
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
"kind": "typeMember",
|
|
187
|
+
"name": "SessionPolicyError.detail.verdict",
|
|
188
|
+
"assertions": [
|
|
189
|
+
"G10.a"
|
|
190
|
+
]
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
"kind": "typeMember",
|
|
194
|
+
"name": "SessionPolicyStore.getRules",
|
|
195
|
+
"assertions": [
|
|
196
|
+
"G9.a"
|
|
197
|
+
]
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
"kind": "typeMember",
|
|
201
|
+
"name": "SessionPolicyStore.putRules",
|
|
202
|
+
"assertions": [
|
|
203
|
+
"G9.a",
|
|
204
|
+
"G9.b"
|
|
205
|
+
]
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"kind": "typeMember",
|
|
209
|
+
"name": "TaskResult.terminal.code",
|
|
210
|
+
"assertions": [
|
|
211
|
+
"G10.b"
|
|
212
|
+
]
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
"kind": "typeMember",
|
|
216
|
+
"name": "TaskResult.terminal.message",
|
|
217
|
+
"assertions": [
|
|
218
|
+
"G10.b"
|
|
219
|
+
]
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
"kind": "typeMember",
|
|
223
|
+
"name": "WorkflowRun.error",
|
|
224
|
+
"assertions": [
|
|
225
|
+
"G11.a",
|
|
226
|
+
"G11.b"
|
|
227
|
+
]
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
"kind": "typeMember",
|
|
231
|
+
"name": "WorkflowRun.result",
|
|
232
|
+
"assertions": [
|
|
233
|
+
"G11.b"
|
|
234
|
+
]
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
"kind": "typeMember",
|
|
238
|
+
"name": "WorkflowRun.status",
|
|
239
|
+
"assertions": [
|
|
240
|
+
"G11.a"
|
|
241
|
+
]
|
|
242
|
+
}
|
|
243
|
+
],
|
|
244
|
+
"consumersHint": {
|
|
245
|
+
"cli": [],
|
|
246
|
+
"server": [],
|
|
247
|
+
"sdk": [],
|
|
248
|
+
"note": "scanned cli@bad8fa4b server@a49a0616 sdk@ae10bec — a name listed is MENTIONED there today, which is evidence of consumption, not proof of it"
|
|
249
|
+
},
|
|
250
|
+
"breakingCount": 0,
|
|
251
|
+
"breakingItems": []
|
|
252
|
+
}
|