@sema-agent/core 7.29.0 → 7.30.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 +361 -0
- package/dist/agents/child-model-seat.d.ts +59 -7
- package/dist/agents/child-model-seat.js +33 -7
- package/dist/agents/cross-session-judge.d.ts +27 -5
- package/dist/agents/cross-session-judge.js +23 -12
- package/dist/agents/list-agents-tool.d.ts +31 -6
- package/dist/agents/list-agents-tool.js +66 -14
- package/dist/agents/peer-admission.d.ts +20 -8
- package/dist/agents/peer-admission.js +21 -12
- package/dist/agents/peer-directory.d.ts +29 -0
- package/dist/agents/peer-directory.js +20 -0
- package/dist/agents/peer-held-queue.d.ts +31 -4
- package/dist/agents/peer-held-queue.js +32 -1
- package/dist/agents/peer-session-drain.d.ts +91 -13
- package/dist/agents/peer-session-drain.js +409 -102
- package/dist/agents/read-inbox-tool.d.ts +22 -0
- package/dist/agents/read-inbox-tool.js +39 -0
- package/dist/agents/roster-store.d.ts +2 -4
- package/dist/agents/roster-store.js +1 -1
- package/dist/agents/send-message-tool.d.ts +4 -4
- package/dist/agents/send-message-tool.js +22 -2
- package/dist/agents/subagent.js +62 -44
- package/dist/agents/team-role.d.ts +34 -0
- package/dist/agents/team-role.js +17 -0
- package/dist/agents/verify.js +2 -2
- package/dist/core/auto-compaction.d.ts +42 -0
- package/dist/core/auto-compaction.js +35 -2
- package/dist/core/child-clamp-seats.d.ts +11 -0
- package/dist/core/child-clamp-seats.js +3 -0
- package/dist/core/context-edit.d.ts +3 -2
- package/dist/core/context-edit.js +6 -5
- package/dist/core/context-guard.d.ts +2 -1
- package/dist/core/context-guard.js +3 -2
- package/dist/core/context-settings.d.ts +103 -0
- package/dist/core/context-settings.js +60 -0
- package/dist/core/engine-notice.d.ts +11 -1
- package/dist/core/frame-settlement.d.ts +97 -0
- package/dist/core/frame-settlement.js +36 -0
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +2 -0
- package/dist/core/hooks.d.ts +10 -10
- package/dist/core/mailbox-store.d.ts +41 -7
- package/dist/core/mailbox-store.js +9 -3
- package/dist/core/parent-spec-seat.d.ts +14 -1
- package/dist/core/parent-spec-seat.js +6 -0
- package/dist/core/permission-rule-model.js +1 -1
- package/dist/core/roles.d.ts +20 -0
- package/dist/core/roles.js +35 -16
- package/dist/core/runner/assemble-result.d.ts +2 -0
- package/dist/core/runner/assemble-result.js +1 -1
- package/dist/core/runner/compaction-call-options.d.ts +26 -2
- package/dist/core/runner/compaction-call-options.js +9 -0
- package/dist/core/runner/compaction-seams.d.ts +4 -3
- package/dist/core/runner/compaction-seams.js +13 -6
- package/dist/core/runner/contracts.d.ts +56 -7
- package/dist/core/runner/decide-continuation.d.ts +2 -1
- package/dist/core/runner/decide-continuation.js +11 -10
- package/dist/core/runner/initial-run-state.js +1 -1
- package/dist/core/runner/prepare-caps-and-workflow.js +9 -1
- package/dist/core/runner/prepare-config-doors.js +4 -1
- package/dist/core/runner/prepare-context-lane.d.ts +9 -2
- package/dist/core/runner/prepare-context-lane.js +15 -10
- package/dist/core/runner/prepare-delegation-surface.d.ts +3 -2
- package/dist/core/runner/prepare-delegation-surface.js +9 -3
- package/dist/core/runner/prepare-file-history.js +10 -8
- package/dist/core/runner/prepare-gate-stations.d.ts +4 -0
- package/dist/core/runner/prepare-gate-stations.js +3 -1
- package/dist/core/runner/prepare-inherited-gate.js +1 -0
- package/dist/core/runner/prepare-prompt-inputs.d.ts +6 -0
- package/dist/core/runner/prepare-prompt-inputs.js +2 -1
- package/dist/core/runner/prepare-task.js +22 -9
- package/dist/core/runner/prepare-wiring-manifest.d.ts +6 -1
- package/dist/core/runner/prepare-wiring-manifest.js +6 -1
- package/dist/core/runner/retired-runner-deps.d.ts +3 -1
- package/dist/core/runner/retired-runner-deps.js +1 -0
- package/dist/core/runner/run-compaction-machinery.d.ts +4 -2
- package/dist/core/runner/run-compaction-machinery.js +5 -3
- package/dist/core/runner/run-harness-handlers.js +1 -1
- package/dist/core/runner/run-identity-wiring.js +19 -1
- package/dist/core/runner/run-leg.js +1 -1
- package/dist/core/runner/run-notification-lane.js +8 -2
- package/dist/core/runner/run-recovery-lanes.js +9 -5
- package/dist/core/runner/run-settle-and-teardown.d.ts +4 -22
- package/dist/core/runner/run-settle-and-teardown.js +6 -10
- package/dist/core/runner/run-stop-and-final-verify.js +1 -0
- package/dist/core/runner/run-terminal-adoption.js +5 -5
- package/dist/core/runner/run-turn-boundary.js +32 -5
- package/dist/core/runner/runtask.d.ts +8 -1
- package/dist/core/runner/runtask.js +32 -1
- package/dist/core/runner-deps.d.ts +44 -13
- package/dist/core/swappable-deps.d.ts +20 -4
- package/dist/core/swappable-deps.js +6 -1
- package/dist/core/task-event.d.ts +5 -0
- package/dist/core/task-notification.d.ts +144 -2
- package/dist/core/task-notification.js +154 -5
- package/dist/core/task-result.d.ts +14 -0
- package/dist/core/task-spec.d.ts +7 -0
- package/dist/core/tool-catalog-entries.js +2 -1
- package/dist/core/tool-conformance.js +1 -0
- package/dist/core/tool-policy.d.ts +9 -4
- package/dist/core/tool-spec.d.ts +14 -0
- package/dist/core/wiring-manifest.d.ts +12 -0
- package/dist/core/wiring-manifest.js +5 -1
- package/dist/engine/harness/agent-harness.d.ts +14 -1
- package/dist/engine/harness/agent-harness.js +28 -3
- package/dist/engine/harness/types.d.ts +3 -1
- package/dist/engine/llm/types.d.ts +8 -7
- package/dist/engine/llm/types.js +1 -1
- package/dist/engine/session/import-validate.js +4 -0
- package/dist/index.d.ts +10 -7
- package/dist/index.js +7 -5
- package/dist/orchestration/run-workflow-tool.d.ts +4 -4
- package/dist/orchestration/workflow.d.ts +1 -1
- package/dist/orchestration/workflow.js +8 -5
- package/dist/shell/lex.d.ts +30 -51
- package/dist/shell/lex.js +237 -284
- package/dist/stores/cc/mailbox-store.js +3 -2
- package/dist/stores/file/corrupt-read-sink.d.ts +28 -2
- package/dist/stores/file/file-history-store.d.ts +2 -4
- package/dist/stores/file/file-history-store.js +1 -1
- package/dist/stores/file/fs-atomic.d.ts +16 -11
- package/dist/stores/file/fs-atomic.js +17 -6
- package/dist/stores/file/index.d.ts +5 -6
- package/dist/stores/file/index.js +1 -1
- package/dist/stores/file/mailbox-store.d.ts +4 -9
- package/dist/stores/file/mailbox-store.js +29 -30
- package/dist/stores/file/session-policy-store.d.ts +2 -0
- package/dist/stores/file/session-policy-store.js +1 -1
- package/dist/stores/file/session-store.d.ts +11 -7
- package/dist/stores/file/session-store.js +23 -16
- package/dist/stores/file/shared-ledger.js +3 -14
- package/dist/stores/file/tool-result-store.d.ts +2 -0
- package/dist/stores/file/tool-result-store.js +5 -2
- package/dist/stores/file/workflow-journal-store.js +14 -9
- package/dist/tools/fs/bash-program-position.d.ts +15 -24
- package/dist/tools/fs/bash-program-position.js +11 -29
- package/dist/tools/fs/bash-readonly-classifier.d.ts +125 -117
- package/dist/tools/fs/bash-readonly-classifier.js +400 -272
- package/dist/tools/fs/shell-read-check.js +3 -3
- package/dist/tools/fs/shell-read-establish.js +1 -1
- package/dist/tools/fs/shell-read-plan.d.ts +11 -6
- package/dist/tools/fs/shell-read-plan.js +167 -183
- package/dist/tools/fs/shell-read-word.d.ts +50 -0
- package/dist/tools/fs/shell-read-word.js +59 -0
- package/dist/tools/fs/shell-reentry-text.d.ts +10 -0
- package/dist/tools/fs/shell-reentry-text.js +73 -0
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +58 -8
- package/wire-changes/core@7.30.0.json +1821 -0
package/dist/shell/lex.js
CHANGED
|
@@ -2,13 +2,6 @@ export const SHELL_LEX_DIALECT = "bash";
|
|
|
2
2
|
export const QUOTE_KINDS = ["'", '"', "$'", '$"', "\\"];
|
|
3
3
|
export const EXPANSION_KINDS = ["parameter", "command", "process", "arithmetic", "tilde", "brace", "glob"];
|
|
4
4
|
export const UNRESOLVED_REASONS = ["ansi_c_escape", "locale_string", "parameter_text", "subscript_text", "compound_array_value"];
|
|
5
|
-
export const UNRESOLVED_VIEW = {
|
|
6
|
-
ansi_c_escape: "refuse",
|
|
7
|
-
locale_string: "fold",
|
|
8
|
-
parameter_text: "refuse",
|
|
9
|
-
subscript_text: "refuse",
|
|
10
|
-
compound_array_value: "refuse",
|
|
11
|
-
};
|
|
12
5
|
export const UNRESOLVED_SENTENCE = {
|
|
13
6
|
ansi_c_escape: "an ANSI-C quoted word carries an escape this lexer does not fold",
|
|
14
7
|
locale_string: "a locale-translated string carries text this lexer does not read",
|
|
@@ -30,7 +23,6 @@ export const PARSE_FAILURE_REASONS = [
|
|
|
30
23
|
"redirect_without_target",
|
|
31
24
|
"heredoc_without_delimiter",
|
|
32
25
|
"heredoc_delimiter_expands",
|
|
33
|
-
"background_operator",
|
|
34
26
|
];
|
|
35
27
|
export const PARSE_FAILURE_SENTENCE = {
|
|
36
28
|
unterminated_single_quote: "a single-quoted word never closes",
|
|
@@ -46,7 +38,6 @@ export const PARSE_FAILURE_SENTENCE = {
|
|
|
46
38
|
redirect_without_target: "a redirection names no target",
|
|
47
39
|
heredoc_without_delimiter: "a here-document operator names no delimiter",
|
|
48
40
|
heredoc_delimiter_expands: "a here-document delimiter is not a plain word",
|
|
49
|
-
background_operator: "`&` backgrounding is not allowed — a backgrounded process outlives the command",
|
|
50
41
|
};
|
|
51
42
|
export const BASH_ONLY_CONSTRUCTS = [
|
|
52
43
|
"ansi_c_quoting",
|
|
@@ -57,6 +48,7 @@ export const BASH_ONLY_CONSTRUCTS = [
|
|
|
57
48
|
"here_string",
|
|
58
49
|
"process_substitution",
|
|
59
50
|
"redirect_both",
|
|
51
|
+
"pipe_both",
|
|
60
52
|
"brace_expansion",
|
|
61
53
|
];
|
|
62
54
|
export const ANSI_C_ESCAPES = {
|
|
@@ -78,11 +70,6 @@ export const SUBSTITUTION_PLACEHOLDER = " subst";
|
|
|
78
70
|
export const SHELL_CONTROL_PREFIXES = new Set(["!", "if", "then", "elif", "else", "while", "until", "do", "time"]);
|
|
79
71
|
const COMPOUND_OPERAND_COMMANDS = new Set(["alias", "declare", "eval", "export", "let", "local", "readonly", "typeset"]);
|
|
80
72
|
const GROUP_WORDS = new Set(["{", "}"]);
|
|
81
|
-
function quoteStep(open, ch) {
|
|
82
|
-
if (open === undefined)
|
|
83
|
-
return ch === '"' || ch === "'" ? ch : undefined;
|
|
84
|
-
return open === ch ? undefined : open;
|
|
85
|
-
}
|
|
86
73
|
export function dollarOpensQuote(text, at) {
|
|
87
74
|
const next = text[at + 1];
|
|
88
75
|
if (next !== "'" && next !== '"')
|
|
@@ -103,32 +90,6 @@ export function ansiCQuoteClose(source, open) {
|
|
|
103
90
|
}
|
|
104
91
|
return undefined;
|
|
105
92
|
}
|
|
106
|
-
export function quoteMask(s) {
|
|
107
|
-
const openedBy = new Array(s.length).fill(undefined);
|
|
108
|
-
const quoted = new Array(s.length).fill(false);
|
|
109
|
-
let open;
|
|
110
|
-
for (let i = 0; i < s.length; i++) {
|
|
111
|
-
if (open === undefined && s[i] === "$" && s[i + 1] === "'") {
|
|
112
|
-
const close = ansiCQuoteClose(s, i + 1);
|
|
113
|
-
for (let k = i; k <= (close ?? s.length - 1); k++) {
|
|
114
|
-
openedBy[k] = "'";
|
|
115
|
-
quoted[k] = true;
|
|
116
|
-
}
|
|
117
|
-
if (close === undefined) {
|
|
118
|
-
open = "'";
|
|
119
|
-
break;
|
|
120
|
-
}
|
|
121
|
-
i = close;
|
|
122
|
-
continue;
|
|
123
|
-
}
|
|
124
|
-
const before = open;
|
|
125
|
-
open = quoteStep(open, s[i]);
|
|
126
|
-
const holds = before ?? open;
|
|
127
|
-
openedBy[i] = holds;
|
|
128
|
-
quoted[i] = holds !== undefined;
|
|
129
|
-
}
|
|
130
|
-
return { openedBy, quoted, balanced: open === undefined };
|
|
131
|
-
}
|
|
132
93
|
export function literalTildeSpelling(word) {
|
|
133
94
|
return word.startsWith("~") ? `./${word}` : word;
|
|
134
95
|
}
|
|
@@ -153,6 +114,9 @@ function decodeAnsiC(body) {
|
|
|
153
114
|
}
|
|
154
115
|
return { text, unresolved };
|
|
155
116
|
}
|
|
117
|
+
export function argvOf(segment) {
|
|
118
|
+
return segment.tokens.filter((t) => t.kind === "word");
|
|
119
|
+
}
|
|
156
120
|
export function wordsOf(segment) {
|
|
157
121
|
const folded = [];
|
|
158
122
|
const raw = [];
|
|
@@ -355,7 +319,6 @@ export function substitutionsInHeredocBody(body) {
|
|
|
355
319
|
}
|
|
356
320
|
return out;
|
|
357
321
|
}
|
|
358
|
-
const WHITESPACE = /\s/;
|
|
359
322
|
const BLANK = /[ \t]/;
|
|
360
323
|
const fail = (reason, at) => ({ parseFailure: { reason, at } });
|
|
361
324
|
const publicPiece = (p) => ({
|
|
@@ -681,8 +644,7 @@ class WordBuilder {
|
|
|
681
644
|
this.carveBraceAndGlob();
|
|
682
645
|
if (assignment !== undefined && this.subscript?.text)
|
|
683
646
|
this.mark("subscript_text", this.subscript.rawStart);
|
|
684
|
-
const marks = this.marks.filter((m) => m.reason !== "subscript_text" || assignment?.subscript !== undefined)
|
|
685
|
-
.sort((a, b) => (UNRESOLVED_VIEW[a.reason] === UNRESOLVED_VIEW[b.reason] ? a.at - b.at : UNRESOLVED_VIEW[a.reason] === "refuse" ? -1 : 1));
|
|
647
|
+
const marks = this.marks.filter((m) => m.reason !== "subscript_text" || assignment?.subscript !== undefined).sort((a, b) => a.at - b.at);
|
|
686
648
|
const unresolved = marks[0]?.reason ?? (assignment?.value.unresolved);
|
|
687
649
|
const spans = [];
|
|
688
650
|
for (let i = 0; i < this.rawQuoted.length; i++) {
|
|
@@ -709,9 +671,8 @@ class WordBuilder {
|
|
|
709
671
|
};
|
|
710
672
|
}
|
|
711
673
|
}
|
|
712
|
-
function scan(source,
|
|
713
|
-
const
|
|
714
|
-
const split = opts.connectors !== "text";
|
|
674
|
+
function scan(source, connectorsAreText) {
|
|
675
|
+
const split = !connectorsAreText;
|
|
715
676
|
const segments = [{ start: 0, end: source.length, connector: "start", tokens: [], substitutions: [] }];
|
|
716
677
|
const current = () => segments[segments.length - 1];
|
|
717
678
|
const pendingHeredocs = [];
|
|
@@ -723,11 +684,14 @@ function scan(source, opts) {
|
|
|
723
684
|
let regionAt = 0;
|
|
724
685
|
let pendingLocale = false;
|
|
725
686
|
let acceptable = true;
|
|
687
|
+
let timeGrammar;
|
|
688
|
+
let definitionName = false;
|
|
726
689
|
let afterLeadingRedirect = false;
|
|
727
|
-
let
|
|
690
|
+
let assignmentSeen = false;
|
|
691
|
+
let subscriptsClosed = false;
|
|
728
692
|
let commandWord;
|
|
729
693
|
let w = new WordBuilder();
|
|
730
|
-
const next = (j) =>
|
|
694
|
+
const next = (j) => past(source, j + 1);
|
|
731
695
|
const quotedBy = () => (inSingle || inDouble ? regionBy : undefined);
|
|
732
696
|
const take = (ch, src) => w.emit(ch, ch, quotedBy(), src, src + 1);
|
|
733
697
|
const substitution = (from, to, body, kind) => {
|
|
@@ -735,6 +699,7 @@ function scan(source, opts) {
|
|
|
735
699
|
w.opaque(source.slice(from, to), SUBSTITUTION_PLACEHOLDER, by, from, to, { kind, yields: kind === "command" && by === undefined ? "many" : "one", body });
|
|
736
700
|
current().substitutions.push(body);
|
|
737
701
|
};
|
|
702
|
+
const resetPosition = () => { acceptable = true; commandWord = undefined; afterLeadingRedirect = false; assignmentSeen = false; subscriptsClosed = false; timeGrammar = undefined; definitionName = false; };
|
|
738
703
|
const endWord = () => {
|
|
739
704
|
if (!w.started)
|
|
740
705
|
return undefined;
|
|
@@ -751,48 +716,72 @@ function scan(source, opts) {
|
|
|
751
716
|
const token = { kind: "heredoc", delimiter: word.folded, quoted: /["'\\]/.test(word.raw), body: "" };
|
|
752
717
|
current().tokens.push(token);
|
|
753
718
|
pendingHeredocs.push({ delimiter: word.folded, quoted: token.quoted, dash: header.dash, token });
|
|
754
|
-
if (
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
719
|
+
if (acceptable) {
|
|
720
|
+
if (assignmentSeen)
|
|
721
|
+
subscriptsClosed = true;
|
|
722
|
+
else
|
|
723
|
+
afterLeadingRedirect = true;
|
|
724
|
+
}
|
|
758
725
|
return undefined;
|
|
759
726
|
}
|
|
760
727
|
if (pendingRedirect !== undefined) {
|
|
761
|
-
const op = pendingRedirect;
|
|
728
|
+
const { op, fd } = pendingRedirect;
|
|
762
729
|
pendingRedirect = undefined;
|
|
763
730
|
const target = { ...word, atCommandPosition: false };
|
|
731
|
+
const written = fd !== undefined ? { fd } : {};
|
|
764
732
|
if ((op === ">&" || op === "<&") && word.raw === word.folded && /^(?:\d+-?|-)$/.test(word.folded))
|
|
765
|
-
current().tokens.push({ kind: "redirect", op });
|
|
766
|
-
else
|
|
767
|
-
current().tokens.push({ kind: "redirect", op: op === ">&" ? "&>" : op === "<&" ? "<" : op, target });
|
|
768
|
-
if (wordsSoFar > 0)
|
|
769
|
-
acceptable = false;
|
|
733
|
+
current().tokens.push({ kind: "redirect", op, ...written, dup: word.folded });
|
|
770
734
|
else
|
|
771
|
-
|
|
735
|
+
current().tokens.push({ kind: "redirect", op: op === ">&" ? "&>" : op === "<&" ? "<" : op, ...written, target });
|
|
736
|
+
if (acceptable) {
|
|
737
|
+
if (assignmentSeen)
|
|
738
|
+
subscriptsClosed = true;
|
|
739
|
+
else
|
|
740
|
+
afterLeadingRedirect = true;
|
|
741
|
+
}
|
|
772
742
|
return undefined;
|
|
773
743
|
}
|
|
774
744
|
current().tokens.push(word);
|
|
775
|
-
|
|
745
|
+
const bare = word.raw === word.folded;
|
|
746
|
+
if (definitionName) {
|
|
747
|
+
resetPosition();
|
|
748
|
+
return undefined;
|
|
749
|
+
}
|
|
750
|
+
if (acceptable && bare && word.folded === "function") {
|
|
751
|
+
acceptable = false;
|
|
752
|
+
definitionName = true;
|
|
776
753
|
return undefined;
|
|
777
|
-
|
|
778
|
-
if (
|
|
754
|
+
}
|
|
755
|
+
if (acceptable && bare && (SHELL_CONTROL_PREFIXES.has(word.folded) || GROUP_WORDS.has(word.folded))) {
|
|
756
|
+
timeGrammar = word.folded === "time" ? "opt" : undefined;
|
|
757
|
+
return undefined;
|
|
758
|
+
}
|
|
759
|
+
if (acceptable && bare && timeGrammar === "opt" && word.folded === "-p") {
|
|
760
|
+
timeGrammar = "ign";
|
|
761
|
+
return undefined;
|
|
762
|
+
}
|
|
763
|
+
if (acceptable && bare && timeGrammar !== undefined && word.folded === "--") {
|
|
764
|
+
timeGrammar = undefined;
|
|
779
765
|
return undefined;
|
|
766
|
+
}
|
|
767
|
+
timeGrammar = undefined;
|
|
768
|
+
if (word.assignment !== undefined && word.assignment.bare && acceptable) {
|
|
769
|
+
assignmentSeen = true;
|
|
770
|
+
return undefined;
|
|
771
|
+
}
|
|
780
772
|
if (acceptable)
|
|
781
|
-
commandWord =
|
|
773
|
+
commandWord = bare ? word.folded : undefined;
|
|
782
774
|
acceptable = false;
|
|
783
775
|
return undefined;
|
|
784
776
|
};
|
|
785
777
|
const operandOwed = (at) => pendingRedirect !== undefined ? fail("redirect_without_target", at) : pendingHeredoc !== undefined ? fail("heredoc_without_delimiter", at) : undefined;
|
|
786
|
-
const closeSegment = (connector, at, from) => {
|
|
778
|
+
const closeSegment = (connector, at, from, pipesStderr = false) => {
|
|
787
779
|
const refusal = endWord() ?? operandOwed(at);
|
|
788
780
|
if (refusal !== undefined)
|
|
789
781
|
return refusal;
|
|
790
782
|
current().end = at;
|
|
791
|
-
segments.push({ start: from, end: source.length, connector, tokens: [], substitutions: [] });
|
|
792
|
-
|
|
793
|
-
wordsSoFar = 0;
|
|
794
|
-
commandWord = undefined;
|
|
795
|
-
afterLeadingRedirect = false;
|
|
783
|
+
segments.push({ start: from, end: source.length, connector, ...(pipesStderr ? { pipesStderr: true } : {}), tokens: [], substitutions: [] });
|
|
784
|
+
resetPosition();
|
|
796
785
|
return undefined;
|
|
797
786
|
};
|
|
798
787
|
const readHeredocBodies = (from) => {
|
|
@@ -826,7 +815,7 @@ function scan(source, opts) {
|
|
|
826
815
|
return Math.min(p, source.length);
|
|
827
816
|
};
|
|
828
817
|
for (let i = 0; i < source.length; i++) {
|
|
829
|
-
if (
|
|
818
|
+
if (!inSingle) {
|
|
830
819
|
const j = past(source, i);
|
|
831
820
|
if (j >= source.length)
|
|
832
821
|
break;
|
|
@@ -897,7 +886,7 @@ function scan(source, opts) {
|
|
|
897
886
|
take(ch, i);
|
|
898
887
|
continue;
|
|
899
888
|
}
|
|
900
|
-
if (!inDouble && ch === "[" && w.started && w.subscript === undefined && !w.compound && acceptable && IDENTIFIER.test(w.raw)) {
|
|
889
|
+
if (!inDouble && ch === "[" && w.started && w.subscript === undefined && !w.compound && acceptable && !subscriptsClosed && IDENTIFIER.test(w.raw)) {
|
|
901
890
|
w.subscript = { rawStart: w.raw.length, depth: 1, text: false };
|
|
902
891
|
take(ch, i);
|
|
903
892
|
continue;
|
|
@@ -926,250 +915,211 @@ function scan(source, opts) {
|
|
|
926
915
|
take(ch, i);
|
|
927
916
|
continue;
|
|
928
917
|
}
|
|
929
|
-
if (!lenient) {
|
|
930
|
-
take(ch, i);
|
|
931
|
-
continue;
|
|
932
|
-
}
|
|
933
918
|
if (ch === "\\" && source[i + 1] === "]")
|
|
934
919
|
sub.text = true;
|
|
935
920
|
}
|
|
936
921
|
if (w.inSubscript() && inDouble && ch === "]")
|
|
937
922
|
w.subscript.text = true;
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
if (escaped === undefined) {
|
|
943
|
-
take(ch, i);
|
|
944
|
-
continue;
|
|
945
|
-
}
|
|
946
|
-
if (inDouble && !"$`\"\\".includes(escaped))
|
|
947
|
-
w.emit(ch + escaped, ch + escaped, quotedBy(), i, i + 2);
|
|
948
|
-
else
|
|
949
|
-
w.emit(ch + escaped, escaped, inDouble ? quotedBy() : "\\", i, i + 2);
|
|
950
|
-
i++;
|
|
951
|
-
continue;
|
|
952
|
-
}
|
|
953
|
-
if (ch === "`") {
|
|
954
|
-
const tick = backtickBody(source, i);
|
|
955
|
-
if (tick === undefined)
|
|
956
|
-
return fail("unterminated_command_substitution", i);
|
|
957
|
-
substitution(i, tick.close + 1, tick.body, "command");
|
|
958
|
-
i = tick.close;
|
|
959
|
-
continue;
|
|
960
|
-
}
|
|
961
|
-
if (ch === "$" && source[n1] === "(") {
|
|
962
|
-
const arithmetic = source[next(n1)] === "(";
|
|
963
|
-
const end = parenBodyEnd(source, n1);
|
|
964
|
-
if (end === undefined)
|
|
965
|
-
return fail("unterminated_command_substitution", i);
|
|
966
|
-
if (end === "unreadable")
|
|
967
|
-
return fail("undelimited_substitution", i);
|
|
968
|
-
substitution(i, end, source.slice(n1 + 1, end - 1), arithmetic ? "arithmetic" : "command");
|
|
969
|
-
i = end - 1;
|
|
970
|
-
continue;
|
|
971
|
-
}
|
|
972
|
-
if (ch === "$" && source[n1] === "{") {
|
|
973
|
-
const end = braceExpansionEnd(source, n1);
|
|
974
|
-
if (end === undefined)
|
|
975
|
-
return fail("unterminated_parameter_expansion", i);
|
|
976
|
-
const body = source.slice(n1 + 1, end - 1);
|
|
977
|
-
const text = source.slice(i, end);
|
|
978
|
-
const name = EXPANSION_NAME.exec(body)?.[1];
|
|
979
|
-
if (UNREADABLE_EXPANSION_TEXT.test(body))
|
|
980
|
-
w.mark("parameter_text", w.raw.length);
|
|
981
|
-
w.opaque(text, text, quotedBy(), i, end, { kind: "parameter", yields: inDouble ? "one" : "many", body, ...(name !== undefined ? { name } : {}) });
|
|
982
|
-
i = end - 1;
|
|
983
|
-
continue;
|
|
984
|
-
}
|
|
985
|
-
if (ch === "$") {
|
|
986
|
-
const head = BARE_PARAMETER.exec(source.slice(n1, n1 + 64))?.[0];
|
|
987
|
-
if (head !== undefined) {
|
|
988
|
-
const text = source.slice(i, n1 + head.length);
|
|
989
|
-
w.opaque(text, text, quotedBy(), i, n1 + head.length, { kind: "parameter", yields: inDouble ? "one" : "many", name: head });
|
|
990
|
-
i = n1 + head.length - 1;
|
|
991
|
-
continue;
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
if (inDouble) {
|
|
923
|
+
const n1 = next(i);
|
|
924
|
+
if (ch === "\\") {
|
|
925
|
+
const escaped = source[i + 1];
|
|
926
|
+
if (escaped === undefined) {
|
|
995
927
|
take(ch, i);
|
|
996
928
|
continue;
|
|
997
929
|
}
|
|
998
|
-
if (
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
930
|
+
if (inDouble && !"$`\"\\".includes(escaped))
|
|
931
|
+
w.emit(ch + escaped, ch + escaped, quotedBy(), i, i + 2);
|
|
932
|
+
else
|
|
933
|
+
w.emit(ch + escaped, escaped, inDouble ? quotedBy() : "\\", i, i + 2);
|
|
934
|
+
i++;
|
|
935
|
+
continue;
|
|
936
|
+
}
|
|
937
|
+
if (ch === "`") {
|
|
938
|
+
const tick = backtickBody(source, i);
|
|
939
|
+
if (tick === undefined)
|
|
940
|
+
return fail("unterminated_command_substitution", i);
|
|
941
|
+
substitution(i, tick.close + 1, tick.body, "command");
|
|
942
|
+
i = tick.close;
|
|
943
|
+
continue;
|
|
944
|
+
}
|
|
945
|
+
if (ch === "$" && source[n1] === "(") {
|
|
946
|
+
const arithmetic = source[next(n1)] === "(";
|
|
947
|
+
const end = parenBodyEnd(source, n1);
|
|
948
|
+
if (end === undefined)
|
|
949
|
+
return fail("unterminated_command_substitution", i);
|
|
950
|
+
if (end === "unreadable")
|
|
951
|
+
return fail("undelimited_substitution", i);
|
|
952
|
+
substitution(i, end, source.slice(n1 + 1, end - 1), arithmetic ? "arithmetic" : "command");
|
|
953
|
+
i = end - 1;
|
|
954
|
+
continue;
|
|
955
|
+
}
|
|
956
|
+
if (ch === "$" && source[n1] === "{") {
|
|
957
|
+
const end = braceExpansionEnd(source, n1);
|
|
958
|
+
if (end === undefined)
|
|
959
|
+
return fail("unterminated_parameter_expansion", i);
|
|
960
|
+
const body = source.slice(n1 + 1, end - 1);
|
|
961
|
+
const text = source.slice(i, end);
|
|
962
|
+
const name = EXPANSION_NAME.exec(body)?.[1];
|
|
963
|
+
if (UNREADABLE_EXPANSION_TEXT.test(body))
|
|
964
|
+
w.mark("parameter_text", w.raw.length);
|
|
965
|
+
w.opaque(text, text, quotedBy(), i, end, { kind: "parameter", yields: inDouble ? "one" : "many", body, ...(name !== undefined ? { name } : {}) });
|
|
966
|
+
i = end - 1;
|
|
967
|
+
continue;
|
|
968
|
+
}
|
|
969
|
+
if (ch === "$") {
|
|
970
|
+
const head = BARE_PARAMETER.exec(source.slice(n1, n1 + 64))?.[0];
|
|
971
|
+
if (head !== undefined) {
|
|
972
|
+
const text = source.slice(i, n1 + head.length);
|
|
973
|
+
w.opaque(text, text, quotedBy(), i, n1 + head.length, { kind: "parameter", yields: inDouble ? "one" : "many", name: head });
|
|
974
|
+
i = n1 + head.length - 1;
|
|
1021
975
|
continue;
|
|
1022
976
|
}
|
|
1023
|
-
|
|
1024
|
-
|
|
977
|
+
}
|
|
978
|
+
if (inDouble) {
|
|
979
|
+
take(ch, i);
|
|
980
|
+
continue;
|
|
981
|
+
}
|
|
982
|
+
if ((ch === "<" || ch === ">") && source[n1] === "(") {
|
|
983
|
+
const end = parenBodyEnd(source, n1);
|
|
984
|
+
if (end === undefined)
|
|
985
|
+
return fail("unterminated_process_substitution", i);
|
|
986
|
+
if (end === "unreadable")
|
|
987
|
+
return fail("undelimited_substitution", i);
|
|
988
|
+
substitution(i, end, source.slice(n1 + 1, end - 1), "process");
|
|
989
|
+
i = end - 1;
|
|
990
|
+
continue;
|
|
991
|
+
}
|
|
992
|
+
if (ch === "(" && w.started && w.isAssignmentHead() && (acceptable || (commandWord !== undefined && COMPOUND_OPERAND_COMMANDS.has(commandWord)))) {
|
|
993
|
+
const end = matchedParen(source, i);
|
|
994
|
+
if (end === undefined)
|
|
995
|
+
return fail("unterminated_compound_value", i);
|
|
996
|
+
const inner = scan(source.slice(i + 1, end - 1), true);
|
|
997
|
+
if ("parseFailure" in inner)
|
|
998
|
+
return fail(inner.parseFailure.reason, i + 1 + inner.parseFailure.at);
|
|
999
|
+
const text = source.slice(i, end);
|
|
1000
|
+
w.mark("compound_array_value", w.raw.length);
|
|
1001
|
+
w.opaque(text, text, undefined, i, end, undefined);
|
|
1002
|
+
w.compound = true;
|
|
1003
|
+
current().substitutions.push(...inner.command.substitutions);
|
|
1004
|
+
i = end - 1;
|
|
1005
|
+
continue;
|
|
1006
|
+
}
|
|
1007
|
+
if (w.inSubscript()) {
|
|
1008
|
+
take(ch, i);
|
|
1009
|
+
continue;
|
|
1010
|
+
}
|
|
1011
|
+
if (ch === "#" && !w.started) {
|
|
1012
|
+
const nl = source.indexOf("\n", i);
|
|
1013
|
+
i = nl < 0 ? source.length : nl - 1;
|
|
1014
|
+
continue;
|
|
1015
|
+
}
|
|
1016
|
+
if (ch === "\n") {
|
|
1017
|
+
if (!split) {
|
|
1018
|
+
const r = endWord();
|
|
1019
|
+
if (r !== undefined)
|
|
1020
|
+
return r;
|
|
1025
1021
|
continue;
|
|
1026
1022
|
}
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1023
|
+
const refusal = closeSegment("\n", i, i + 1);
|
|
1024
|
+
if (refusal !== undefined)
|
|
1025
|
+
return refusal;
|
|
1026
|
+
if (pendingHeredocs.length > 0) {
|
|
1027
|
+
const resume = readHeredocBodies(i + 1);
|
|
1028
|
+
if (typeof resume !== "number")
|
|
1029
|
+
return resume;
|
|
1030
|
+
current().start = resume;
|
|
1031
|
+
i = resume - 1;
|
|
1031
1032
|
}
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
i = resume - 1;
|
|
1048
|
-
}
|
|
1049
|
-
continue;
|
|
1033
|
+
continue;
|
|
1034
|
+
}
|
|
1035
|
+
if (ch === "(" || ch === ")") {
|
|
1036
|
+
const r = endWord() ?? operandOwed(i);
|
|
1037
|
+
if (r !== undefined)
|
|
1038
|
+
return r;
|
|
1039
|
+
current().tokens.push({ kind: "group", op: ch });
|
|
1040
|
+
resetPosition();
|
|
1041
|
+
continue;
|
|
1042
|
+
}
|
|
1043
|
+
if (ch === "<" || ch === ">" || (ch === "&" && source[n1] === ">")) {
|
|
1044
|
+
let fd;
|
|
1045
|
+
if (ch !== "&" && w.started && w.raw === w.folded && /^\d+$/.test(w.raw)) {
|
|
1046
|
+
fd = Number(w.raw);
|
|
1047
|
+
w = new WordBuilder();
|
|
1050
1048
|
}
|
|
1051
|
-
|
|
1049
|
+
else {
|
|
1052
1050
|
const r = endWord() ?? operandOwed(i);
|
|
1053
1051
|
if (r !== undefined)
|
|
1054
1052
|
return r;
|
|
1055
|
-
current().tokens.push({ kind: "group", op: ch });
|
|
1056
|
-
acceptable = true;
|
|
1057
|
-
wordsSoFar = 0;
|
|
1058
|
-
commandWord = undefined;
|
|
1059
|
-
afterLeadingRedirect = false;
|
|
1060
|
-
continue;
|
|
1061
1053
|
}
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
return r;
|
|
1069
|
-
}
|
|
1070
|
-
const n2 = next(n1);
|
|
1071
|
-
if (ch === "&") {
|
|
1072
|
-
if (source[n2] === ">") {
|
|
1073
|
-
pendingRedirect = "&>>";
|
|
1074
|
-
i = n2;
|
|
1075
|
-
}
|
|
1076
|
-
else {
|
|
1077
|
-
pendingRedirect = "&>";
|
|
1078
|
-
i = n1;
|
|
1079
|
-
}
|
|
1080
|
-
continue;
|
|
1081
|
-
}
|
|
1082
|
-
if (ch === "<" && source[n1] === "<") {
|
|
1083
|
-
if (source[n2] === "<") {
|
|
1084
|
-
pendingRedirect = "<<<";
|
|
1085
|
-
i = n2;
|
|
1086
|
-
continue;
|
|
1087
|
-
}
|
|
1088
|
-
const dash = source[n2] === "-";
|
|
1089
|
-
pendingHeredoc = { dash };
|
|
1090
|
-
i = dash ? n2 : n1;
|
|
1091
|
-
continue;
|
|
1092
|
-
}
|
|
1093
|
-
if (source[n1] === "&") {
|
|
1094
|
-
pendingRedirect = ch === ">" ? ">&" : "<&";
|
|
1095
|
-
i = n1;
|
|
1096
|
-
continue;
|
|
1054
|
+
const written = fd !== undefined ? { fd } : {};
|
|
1055
|
+
const n2 = next(n1);
|
|
1056
|
+
if (ch === "&") {
|
|
1057
|
+
if (source[n2] === ">") {
|
|
1058
|
+
pendingRedirect = { op: "&>>" };
|
|
1059
|
+
i = n2;
|
|
1097
1060
|
}
|
|
1098
|
-
|
|
1099
|
-
pendingRedirect =
|
|
1061
|
+
else {
|
|
1062
|
+
pendingRedirect = { op: "&>" };
|
|
1100
1063
|
i = n1;
|
|
1101
|
-
continue;
|
|
1102
1064
|
}
|
|
1103
|
-
pendingRedirect = ch;
|
|
1104
1065
|
continue;
|
|
1105
1066
|
}
|
|
1106
|
-
if (
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
else if (ch === "&") {
|
|
1111
|
-
if (source[n1] === "&") {
|
|
1112
|
-
refusal = closeSegment("&&", i, n1 + 1);
|
|
1113
|
-
i = n1;
|
|
1114
|
-
}
|
|
1115
|
-
else
|
|
1116
|
-
refusal = closeSegment("&", i, i + 1);
|
|
1117
|
-
}
|
|
1118
|
-
else if (ch === "|") {
|
|
1119
|
-
if (source[n1] === "|" || source[n1] === "&") {
|
|
1120
|
-
refusal = closeSegment(source[n1] === "|" ? "||" : "|", i, n1 + 1);
|
|
1121
|
-
i = n1;
|
|
1122
|
-
}
|
|
1123
|
-
else
|
|
1124
|
-
refusal = closeSegment("|", i, i + 1);
|
|
1125
|
-
}
|
|
1126
|
-
else if (BLANK.test(ch)) {
|
|
1127
|
-
refusal = endWord();
|
|
1128
|
-
}
|
|
1129
|
-
else {
|
|
1130
|
-
take(ch, i);
|
|
1067
|
+
if (ch === "<" && source[n1] === "<") {
|
|
1068
|
+
if (source[n2] === "<") {
|
|
1069
|
+
pendingRedirect = { op: "<<<", ...written };
|
|
1070
|
+
i = n2;
|
|
1131
1071
|
continue;
|
|
1132
1072
|
}
|
|
1133
|
-
|
|
1134
|
-
|
|
1073
|
+
const dash = source[n2] === "-";
|
|
1074
|
+
pendingHeredoc = { dash };
|
|
1075
|
+
i = dash ? n2 : n1;
|
|
1135
1076
|
continue;
|
|
1136
1077
|
}
|
|
1137
|
-
if (
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
return r;
|
|
1078
|
+
if (source[n1] === "&") {
|
|
1079
|
+
pendingRedirect = { op: ch === ">" ? ">&" : "<&", ...written };
|
|
1080
|
+
i = n1;
|
|
1141
1081
|
continue;
|
|
1142
1082
|
}
|
|
1143
|
-
|
|
1083
|
+
if (ch === ">" && (source[n1] === ">" || source[n1] === "|")) {
|
|
1084
|
+
pendingRedirect = { op: source[n1] === ">" ? ">>" : ">", ...written };
|
|
1085
|
+
i = n1;
|
|
1086
|
+
continue;
|
|
1087
|
+
}
|
|
1088
|
+
pendingRedirect = { op: ch, ...written };
|
|
1144
1089
|
continue;
|
|
1145
1090
|
}
|
|
1146
|
-
if (
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
if (
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1091
|
+
if (split) {
|
|
1092
|
+
let refusal;
|
|
1093
|
+
if (ch === ";")
|
|
1094
|
+
refusal = closeSegment(";", i, i + 1);
|
|
1095
|
+
else if (ch === "&") {
|
|
1096
|
+
if (source[n1] === "&") {
|
|
1097
|
+
refusal = closeSegment("&&", i, n1 + 1);
|
|
1098
|
+
i = n1;
|
|
1099
|
+
}
|
|
1100
|
+
else
|
|
1101
|
+
refusal = closeSegment("&", i, i + 1);
|
|
1155
1102
|
}
|
|
1156
|
-
if (ch === "|") {
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
i
|
|
1163
|
-
continue;
|
|
1103
|
+
else if (ch === "|") {
|
|
1104
|
+
if (source[n1] === "|" || source[n1] === "&") {
|
|
1105
|
+
refusal = closeSegment(source[n1] === "|" ? "||" : "|", i, n1 + 1, source[n1] === "&");
|
|
1106
|
+
i = n1;
|
|
1107
|
+
}
|
|
1108
|
+
else
|
|
1109
|
+
refusal = closeSegment("|", i, i + 1);
|
|
1164
1110
|
}
|
|
1165
|
-
if (ch
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1111
|
+
else if (BLANK.test(ch)) {
|
|
1112
|
+
refusal = endWord();
|
|
1113
|
+
}
|
|
1114
|
+
else {
|
|
1115
|
+
take(ch, i);
|
|
1169
1116
|
continue;
|
|
1170
1117
|
}
|
|
1118
|
+
if (refusal !== undefined)
|
|
1119
|
+
return refusal;
|
|
1120
|
+
continue;
|
|
1171
1121
|
}
|
|
1172
|
-
if (
|
|
1122
|
+
if (BLANK.test(ch)) {
|
|
1173
1123
|
const r = endWord();
|
|
1174
1124
|
if (r !== undefined)
|
|
1175
1125
|
return r;
|
|
@@ -1189,23 +1139,24 @@ function scan(source, opts) {
|
|
|
1189
1139
|
const out = segments.map((s) => ({
|
|
1190
1140
|
text: source.slice(s.start, s.end),
|
|
1191
1141
|
connector: s.connector,
|
|
1142
|
+
...(s.pipesStderr ? { pipesStderr: true } : {}),
|
|
1192
1143
|
tokens: s.tokens,
|
|
1193
1144
|
substitutions: s.substitutions,
|
|
1194
1145
|
}));
|
|
1195
1146
|
return { command: { segments: out, substitutions: out.flatMap((s) => s.substitutions) } };
|
|
1196
1147
|
}
|
|
1197
|
-
export function lexShell(source
|
|
1198
|
-
const pass = scan(source,
|
|
1148
|
+
export function lexShell(source) {
|
|
1149
|
+
const pass = scan(source, false);
|
|
1199
1150
|
return "parseFailure" in pass ? pass : pass.command;
|
|
1200
1151
|
}
|
|
1201
|
-
export function lexShellText(source
|
|
1202
|
-
const lexed = lexShell(source
|
|
1152
|
+
export function lexShellText(source) {
|
|
1153
|
+
const lexed = lexShell(source);
|
|
1203
1154
|
if ("parseFailure" in lexed)
|
|
1204
1155
|
return { reject: PARSE_FAILURE_SENTENCE[lexed.parseFailure.reason] };
|
|
1205
1156
|
for (const segment of lexed.segments) {
|
|
1206
1157
|
for (const token of segment.tokens) {
|
|
1207
1158
|
const word = token.kind === "word" ? token : token.kind === "redirect" ? token.target : undefined;
|
|
1208
|
-
if (word?.unresolved !== undefined
|
|
1159
|
+
if (word?.unresolved !== undefined)
|
|
1209
1160
|
return { reject: UNRESOLVED_SENTENCE[word.unresolved] };
|
|
1210
1161
|
}
|
|
1211
1162
|
}
|
|
@@ -1236,6 +1187,8 @@ export function bashOnlyConstructsOf(command) {
|
|
|
1236
1187
|
}
|
|
1237
1188
|
};
|
|
1238
1189
|
for (const segment of command.segments) {
|
|
1190
|
+
if (segment.pipesStderr)
|
|
1191
|
+
found.add("pipe_both");
|
|
1239
1192
|
for (const t of segment.tokens) {
|
|
1240
1193
|
if (t.kind === "word")
|
|
1241
1194
|
word(t);
|