dsh-context-memory 0.7.0 → 0.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 +324 -0
- package/README.md +151 -24
- package/icon.svg +16 -0
- package/lib/candidates.d.ts +71 -0
- package/lib/candidates.js +150 -0
- package/lib/capture.d.ts +21 -0
- package/lib/capture.js +86 -0
- package/lib/cli.js +88 -9
- package/lib/client/index.d.ts +17 -23
- package/lib/client/locales.d.ts +11 -0
- package/lib/client/locales.js +22 -0
- package/lib/client/view.d.ts +8 -1
- package/lib/client/view.js +18 -3
- package/lib/guard.d.ts +92 -30
- package/lib/guard.js +539 -73
- package/lib/identity.d.ts +16 -5
- package/lib/identity.js +36 -18
- package/lib/index.d.ts +33 -2
- package/lib/index.js +173 -11
- package/lib/memory.d.ts +131 -9
- package/lib/memory.js +296 -46
- package/lib/memory.web.js +58 -7
- package/lib/memory.web.js.map +2 -2
- package/lib/recall.d.ts +46 -7
- package/lib/recall.js +119 -12
- package/lib/routes.d.ts +1 -0
- package/lib/routes.js +50 -1
- package/lib/scope.d.ts +45 -0
- package/lib/scope.js +75 -0
- package/lib/store.d.ts +25 -2
- package/lib/store.js +45 -7
- package/lib/tools.d.ts +44 -0
- package/lib/tools.js +241 -0
- package/lib/types.d.ts +182 -3
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +25 -14
package/lib/client/view.js
CHANGED
|
@@ -10,6 +10,7 @@ const RECALL_PATH = '/api/context-memory.recall';
|
|
|
10
10
|
const REMEMBER_PATH = '/api/context-memory.remember';
|
|
11
11
|
const FORGET_PATH = '/api/context-memory.forget';
|
|
12
12
|
const PREFS_PATH = '/api/context-memory.prefs';
|
|
13
|
+
const CANDIDATES_PATH = '/api/context-memory.candidates';
|
|
13
14
|
export function checkLabel(memory, t) {
|
|
14
15
|
if (!memory.guard)
|
|
15
16
|
return t('checkNone');
|
|
@@ -21,7 +22,14 @@ export function checkLabel(memory, t) {
|
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
24
|
export function provenance(memory) {
|
|
24
|
-
|
|
25
|
+
// Scope first: the same sentence can be held in both logs with the same id, so without
|
|
26
|
+
// it two rows would read identically and the "forget" button beside them would be a
|
|
27
|
+
// coin flip over which log it retires.
|
|
28
|
+
return [memory.scope, memory.source, memory.sessionId !== undefined ? `session ${memory.sessionId}` : null, memory.step !== undefined ? `step ${String(memory.step)}` : null].filter((part) => part !== null).join(' · ');
|
|
29
|
+
}
|
|
30
|
+
/** A row key that is unique across scopes, where the id alone is not. */
|
|
31
|
+
export function memoryKey(memory) {
|
|
32
|
+
return `${memory.scope}:${memory.id}`;
|
|
25
33
|
}
|
|
26
34
|
function AddMemoryModal({ t, onClose, onAdded }) {
|
|
27
35
|
const toast = useToast();
|
|
@@ -89,13 +97,20 @@ function ContextMemoryPanelInner({ t }) {
|
|
|
89
97
|
reload();
|
|
90
98
|
}
|
|
91
99
|
}, [forgetTarget, t, toast, reload]);
|
|
100
|
+
const decideCandidate = useCallback(async (id, action) => {
|
|
101
|
+
const r = await fetch(CANDIDATES_PATH, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ id, action }) });
|
|
102
|
+
if (r.ok) {
|
|
103
|
+
toast('success', action === 'accept' ? t('candidateAccepted') : t('candidateDismissed'));
|
|
104
|
+
reload();
|
|
105
|
+
}
|
|
106
|
+
}, [t, toast, reload]);
|
|
92
107
|
const header = (_jsxs("header", { style: { display: 'flex', alignItems: 'center', gap: 12, marginBottom: 4 }, children: [_jsxs("strong", { style: { fontSize: 15 }, children: ["\uD83E\uDDE0 ", t('title')] }), _jsx("span", { style: { flex: 1 } }), _jsxs(Button, { variant: "primary", size: "sm", onClick: () => setAddOpen(true), children: ["\u2795 ", t('addMemory')] }), _jsx(Button, { variant: "secondary", size: "sm", onClick: reload, children: t('refresh') })] }));
|
|
93
108
|
if (error !== null)
|
|
94
109
|
return _jsxs("div", { style: { maxWidth: 820 }, children: [header, _jsx(Card, { children: _jsxs("p", { role: "alert", style: { margin: 0, fontSize: 13, color: 'var(--dsw-alias-state-error-primary)' }, children: [t('failed'), ": ", error] }) })] });
|
|
95
110
|
if (payload === null)
|
|
96
111
|
return _jsxs("div", { style: { maxWidth: 820 }, children: [header, _jsx("div", { style: { display: 'flex', justifyContent: 'center', padding: 40 }, children: _jsx(Spinner, { size: 28 }) })] });
|
|
97
|
-
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 14, maxWidth: 820 }, children: [header, _jsxs("div", { style: { display: 'flex', gap: 10, flexWrap: 'wrap' }, children: [_jsx(StatCard, { value: payload.totals.active, label: t('active') }), _jsx(StatCard, { value: payload.totals.verified, label: t('checkPassed') }), _jsx(StatCard, { value: payload.totals.unverified, label: t('unverified') }), _jsx(StatCard, { value: payload.totals.superseded, label: t('superseded') })] }), _jsx(Card, { title: t('integration'), icon: "\u2699\uFE0F", children: _jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: [['autoCapture', 'autoCaptureLabel'], ['recallInject', 'recallInjectLabel']].map(([key, labelKey]) => (_jsxs("label", { style: { display: 'flex', alignItems: 'center', gap: 8, fontSize: 13, cursor: 'pointer' }, children: [_jsx("input", { type: "checkbox", checked: prefs?.[key] ?? true, onChange: () => togglePref(key) }), t(labelKey)] }, key))) }) }), _jsxs(Card, { title: t('search'), icon: "\uD83D\uDD0D", children: [_jsxs("div", { style: { display: 'flex', gap: 8 }, children: [_jsx(Input, { value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), placeholder: t('searchPlaceholder'), onKeyDown: (e) => { if (e.key === 'Enter')
|
|
98
|
-
handleSearch(); } }), _jsxs(Button, { variant: "secondary", onClick: handleSearch, disabled: searching, children: [searching ? _jsx(Spinner, { size: 14 }) : null, " ", t('search')] })] }), searchHits !== null && (_jsx("div", { style: { marginTop: 10 }, children: searchHits.length === 0 ? _jsx(EmptyState, { message: t('noResults') }) : (_jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 6 }, children: searchHits.map((hit) => (_jsxs("div", { style: { display: 'flex', gap: 8, alignItems: 'baseline' }, children: [_jsx(Badge, { color: "info", children: hit.score.toFixed(2) }), _jsx("span", { style: { fontSize: 12 }, children: hit.memory.text }), _jsxs("span", { style: { fontSize: 10, opacity: 0.5 }, children: ["\u2014 ", hit.why] })] }, hit.memory.id))) })) }))] }), payload.conflicts.length > 0 && (_jsxs(_Fragment, { children: [_jsx(SectionTitle, { icon: "\u26A0\uFE0F", children: t('conflicts') }), payload.conflicts.map((conflict) => (_jsx(Card, { title: conflict.subject, children: conflict.memories.map((memory) => (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }, children: [_jsx("code", { style: { fontSize: 11 }, children: memory.id }), _jsx("span", { style: { fontSize: 12, flex: 1 }, children: memory.text }), _jsx(Button, { variant: "danger", size: "sm", onClick: () => setForgetTarget({ id: memory.id, reason: `conflict resolved: ${conflict.subject}` }), children: t('resolve') })] }, memory.id))) }, conflict.subject)))] })), _jsx(SectionTitle, { icon: "\u2705", children: t('active') }), payload.active.length === 0 ? _jsx(EmptyState, { icon: "\uD83D\uDCED", message: t('empty') }) : (_jsx(Card, { padding: 0, children: _jsxs("table", { style: tableStyles.table, children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { style: tableStyles.th, children: "Id" }), _jsx("th", { style: tableStyles.th, children: t('memory') }), _jsx("th", { style: tableStyles.th, children: t('kind') }), _jsx("th", { style: tableStyles.th, children: t('status') }), _jsx("th", { style: tableStyles.th, children: t('from') }), _jsx("th", { style: tableStyles.th })] }) }), _jsx("tbody", { children: payload.active.map((memory) => (_jsxs("tr", { children: [_jsx("td", { style: tableStyles.td, children: _jsx("code", { style: { fontSize: 11 }, children: memory.id }) }), _jsx("td", { style: tableStyles.td, children: memory.text }), _jsx("td", { style: tableStyles.td, children: _jsx(Badge, { children: memory.kind }) }), _jsx("td", { style: tableStyles.td, children: checkLabel(memory, t) }), _jsx("td", { style: { ...tableStyles.td, fontSize: 11, opacity: 0.6 }, children: provenance(memory) }), _jsx("td", { style: tableStyles.td, children: _jsx(Button, { variant: "danger", size: "sm", onClick: () => setForgetTarget({ id: memory.id, reason: 'superseded from panel' }), children: "\uD83D\uDDD1" }) })] }, memory
|
|
112
|
+
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 14, maxWidth: 820 }, children: [header, _jsxs("div", { style: { display: 'flex', gap: 10, flexWrap: 'wrap' }, children: [_jsx(StatCard, { value: payload.totals.active, label: t('active') }), _jsx(StatCard, { value: payload.totals.verified, label: t('checkPassed') }), _jsx(StatCard, { value: payload.totals.unverified, label: t('unverified') }), _jsx(StatCard, { value: payload.totals.superseded, label: t('superseded') }), payload.totals.candidates > 0 && _jsx(StatCard, { value: payload.totals.candidates, label: t('candidates') })] }), _jsx(Card, { title: t('integration'), icon: "\u2699\uFE0F", children: _jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: [['autoCapture', 'autoCaptureLabel'], ['recallInject', 'recallInjectLabel']].map(([key, labelKey]) => (_jsxs("label", { style: { display: 'flex', alignItems: 'center', gap: 8, fontSize: 13, cursor: 'pointer' }, children: [_jsx("input", { type: "checkbox", checked: prefs?.[key] ?? true, onChange: () => togglePref(key) }), t(labelKey)] }, key))) }) }), _jsxs(Card, { title: t('search'), icon: "\uD83D\uDD0D", children: [_jsxs("div", { style: { display: 'flex', gap: 8 }, children: [_jsx(Input, { value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), placeholder: t('searchPlaceholder'), onKeyDown: (e) => { if (e.key === 'Enter')
|
|
113
|
+
handleSearch(); } }), _jsxs(Button, { variant: "secondary", onClick: handleSearch, disabled: searching, children: [searching ? _jsx(Spinner, { size: 14 }) : null, " ", t('search')] })] }), searchHits !== null && (_jsx("div", { style: { marginTop: 10 }, children: searchHits.length === 0 ? _jsx(EmptyState, { message: t('noResults') }) : (_jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 6 }, children: searchHits.map((hit) => (_jsxs("div", { style: { display: 'flex', gap: 8, alignItems: 'baseline' }, children: [_jsx(Badge, { color: "info", children: hit.score.toFixed(2) }), _jsx("span", { style: { fontSize: 12 }, children: hit.memory.text }), _jsxs("span", { style: { fontSize: 10, opacity: 0.5 }, children: ["\u2014 ", hit.why] })] }, hit.memory.id))) })) }))] }), payload.conflicts.length > 0 && (_jsxs(_Fragment, { children: [_jsx(SectionTitle, { icon: "\u26A0\uFE0F", children: t('conflicts') }), payload.conflicts.map((conflict) => (_jsx(Card, { title: conflict.subject, children: conflict.memories.map((memory) => (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }, children: [_jsx("code", { style: { fontSize: 11 }, children: memory.id }), _jsx("span", { style: { fontSize: 12, flex: 1 }, children: memory.text }), _jsx(Button, { variant: "danger", size: "sm", onClick: () => setForgetTarget({ id: memory.id, reason: `conflict resolved: ${conflict.subject}` }), children: t('resolve') })] }, memory.id))) }, conflict.subject)))] })), payload.candidates.length > 0 && (_jsxs(_Fragment, { children: [_jsxs(SectionTitle, { icon: "\uD83D\uDCA1", children: [t('candidates'), " (", payload.candidates.length, ")"] }), _jsxs(Card, { children: [_jsx("p", { style: { margin: '0 0 10px', fontSize: 12, opacity: 0.7 }, children: t('candidatesHint') }), _jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: payload.candidates.map((candidate) => (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8 }, children: [_jsx(Badge, { children: candidate.kind }), _jsx("span", { style: { fontSize: 12, flex: 1 }, children: candidate.text }), _jsx(Button, { variant: "primary", size: "sm", onClick: () => void decideCandidate(candidate.id, 'accept'), children: t('accept') }), _jsx(Button, { variant: "secondary", size: "sm", onClick: () => void decideCandidate(candidate.id, 'dismiss'), children: t('dismiss') })] }, candidate.id))) })] })] })), _jsx(SectionTitle, { icon: "\u2705", children: t('active') }), payload.active.length === 0 ? _jsx(EmptyState, { icon: "\uD83D\uDCED", message: t('empty') }) : (_jsx(Card, { padding: 0, children: _jsxs("table", { style: tableStyles.table, children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { style: tableStyles.th, children: "Id" }), _jsx("th", { style: tableStyles.th, children: t('memory') }), _jsx("th", { style: tableStyles.th, children: t('kind') }), _jsx("th", { style: tableStyles.th, children: t('status') }), _jsx("th", { style: tableStyles.th, children: t('from') }), _jsx("th", { style: tableStyles.th })] }) }), _jsx("tbody", { children: payload.active.map((memory) => (_jsxs("tr", { children: [_jsx("td", { style: tableStyles.td, children: _jsx("code", { style: { fontSize: 11 }, children: memory.id }) }), _jsx("td", { style: tableStyles.td, children: memory.text }), _jsx("td", { style: tableStyles.td, children: _jsx(Badge, { children: memory.kind }) }), _jsx("td", { style: tableStyles.td, children: checkLabel(memory, t) }), _jsx("td", { style: { ...tableStyles.td, fontSize: 11, opacity: 0.6 }, children: provenance(memory) }), _jsx("td", { style: tableStyles.td, children: _jsx(Button, { variant: "danger", size: "sm", onClick: () => setForgetTarget({ id: memory.id, scope: memory.scope, reason: 'superseded from panel' }), children: "\uD83D\uDDD1" }) })] }, memoryKey(memory)))) })] }) })), payload.superseded.length > 0 && (_jsxs(_Fragment, { children: [_jsxs(SectionTitle, { icon: "\uD83D\uDCE6", children: [t('superseded'), " (", payload.superseded.length, ")"] }), _jsx(Card, { padding: 0, children: _jsxs("table", { style: tableStyles.table, children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { style: tableStyles.th, children: "Id" }), _jsx("th", { style: tableStyles.th, children: t('memory') }), _jsx("th", { style: tableStyles.th, children: t('kind') }), _jsx("th", { style: tableStyles.th, children: t('status') })] }) }), _jsx("tbody", { children: payload.superseded.map((memory) => (_jsxs("tr", { children: [_jsx("td", { style: tableStyles.td, children: _jsx("code", { style: { fontSize: 11 }, children: memory.id }) }), _jsx("td", { style: tableStyles.td, children: memory.text }), _jsx("td", { style: tableStyles.td, children: _jsx(Badge, { children: memory.kind }) }), _jsx("td", { style: tableStyles.td, children: checkLabel(memory, t) })] }, memoryKey(memory)))) })] }) })] })), addOpen && _jsx(AddMemoryModal, { t: t, onClose: () => setAddOpen(false), onAdded: reload }), forgetTarget !== null && _jsx(ConfirmDialog, { title: t('forget'), message: t('confirmForget'), confirmLabel: t('forget'), danger: true, onConfirm: handleForget, onClose: () => setForgetTarget(null) })] }));
|
|
99
114
|
}
|
|
100
115
|
export function ContextMemoryPanel({ t }) {
|
|
101
116
|
return _jsx(ToastProvider, { children: _jsx(ContextMemoryPanelInner, { t: t }) });
|
package/lib/guard.d.ts
CHANGED
|
@@ -9,58 +9,120 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Recognition is narrow on purpose. Only statement shapes whose checkable predicate is
|
|
11
11
|
* unambiguous get a guard; everything else stays uncheckable and says so. Emitting an
|
|
12
|
-
* approximate
|
|
13
|
-
*
|
|
12
|
+
* approximate check would produce one that passes while verifying nothing, and every
|
|
13
|
+
* later "verified" would be a lie.
|
|
14
14
|
*
|
|
15
15
|
* There is deliberately **no** shape that runs a command taken from memory text. A
|
|
16
|
-
* memory can be written by the agent itself, so treating its content as
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* memory can be written by the agent itself, so treating its content as executable is
|
|
17
|
+
* an injection path. That is why the checks below are evaluated in-process against the
|
|
18
|
+
* filesystem rather than handed to a shell: the statement supplies operands (a path, a
|
|
19
|
+
* search string) and never a program, an argument list, or shell syntax.
|
|
20
|
+
*
|
|
21
|
+
* **Why not a shell command.** This module used to compile each shape into a shell
|
|
22
|
+
* command string and run it with `spawnSync(..., { shell: true })`, branching on
|
|
23
|
+
* `process.platform`. That design made the central question — did the check run at
|
|
24
|
+
* all? — undecidable on Windows. A guard whose program is missing and a guard whose
|
|
25
|
+
* predicate is simply false both exit 1 through cmd.exe, and cmd.exe's own diagnostic
|
|
26
|
+
* is a localized resource string: on a zh-CN host it arrives as GBK bytes reading
|
|
27
|
+
* 不是内部或外部命令…, which decodes to mojibake under utf8, so neither an English nor
|
|
28
|
+
* a Chinese pattern could reliably tell the two apart. Any classifier built on "what
|
|
29
|
+
* did the shell say" is a classifier that answers differently per host language.
|
|
30
|
+
*
|
|
31
|
+
* The predicates are all filesystem facts. Four of them are about a file's *text*
|
|
32
|
+
* (`exists` / `absent` / `contains` / `lacks`); four are about a file's *structure*
|
|
33
|
+
* (`depends` / `script` / `option` / `lineLimit`), answered by `JSON.parse` and a line
|
|
34
|
+
* count. Answered directly, they need no shell, no locale, no platform branch, and no
|
|
35
|
+
* quoting layer — and the distinction this module exists to protect becomes a fact about
|
|
36
|
+
* a return value instead of a guess about text.
|
|
37
|
+
*
|
|
38
|
+
* The structured predicates exist because the text ones cannot answer structural
|
|
39
|
+
* questions honestly: `contains "tsx" package.json` passes when `tsx` appears in a
|
|
40
|
+
* description or a script string, so it would report `passed` while verifying nothing.
|
|
41
|
+
* Parsing is still in-process, and the memory still supplies only operands.
|
|
19
42
|
*
|
|
20
43
|
* @module context-memory/guard
|
|
21
44
|
*/
|
|
22
45
|
import type { Guard, GuardOutcome } from './types.js';
|
|
23
46
|
/**
|
|
24
|
-
*
|
|
47
|
+
* Every guard kind this version can evaluate.
|
|
48
|
+
*
|
|
49
|
+
* This list is the single definition. `memory.ts` drops a guard whose kind is not in it
|
|
50
|
+
* (a log written by a newer version), and the exhaustiveness assertion below turns
|
|
51
|
+
* "added a kind to the union but forgot to teach the evaluator" into a compile error
|
|
52
|
+
* rather than a runtime fallthrough into whichever branch happens to come last.
|
|
53
|
+
*/
|
|
54
|
+
export declare const GUARD_KINDS: readonly ["exists", "absent", "contains", "lacks", "depends", "script", "option", "lineLimit"];
|
|
55
|
+
/**
|
|
56
|
+
* Compile a memory statement into a check.
|
|
57
|
+
*
|
|
58
|
+
* The returned predicate names operands, never a program: `{ kind: 'exists', path }`
|
|
59
|
+
* says what must be true, and `runGuard` decides it with `fs`. Nothing from the
|
|
60
|
+
* statement text is ever executed, so a memory cannot smuggle in an injection.
|
|
61
|
+
*
|
|
62
|
+
* Operands are stored as written. Resolution against the project directory happens at
|
|
63
|
+
* run time, so a memory stays meaningful if the project is moved or the check is
|
|
64
|
+
* evaluated from a different working directory.
|
|
65
|
+
*/
|
|
66
|
+
export declare function compileGuard(text: string): Guard | undefined;
|
|
67
|
+
/**
|
|
68
|
+
* A human-readable rendering of a check, for the CLI and the log.
|
|
25
69
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* permanently unrunnable, so verification would report on nothing while looking busy.
|
|
70
|
+
* Deliberately not a shell command: printing one would invite the reader to run it,
|
|
71
|
+
* which is the interface this module just removed.
|
|
29
72
|
*/
|
|
30
|
-
export declare function
|
|
73
|
+
export declare function describeGuard(guard: Guard): string;
|
|
31
74
|
/**
|
|
32
|
-
*
|
|
75
|
+
* Why a check contradicted a memory, in one line.
|
|
33
76
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
77
|
+
* `runGuard` reports a reason when the check could not run, but a check that ran and came
|
|
78
|
+
* back false has nothing to report beyond its own verdict — `exists` and `absent` return
|
|
79
|
+
* an empty `output`. Answering "contradicted" with no detail leaves the reader unable to
|
|
80
|
+
* tell which path or which string was actually tested, so this falls back to the check's
|
|
81
|
+
* own description: it names the operand the memory no longer matches, which is the fact
|
|
82
|
+
* that made the memory wrong.
|
|
36
83
|
*/
|
|
37
|
-
export declare function
|
|
84
|
+
export declare function describeContradiction(check: Guard | undefined, output: string | undefined): string;
|
|
38
85
|
/**
|
|
39
|
-
*
|
|
86
|
+
* Resolve a statement's operand against the project directory.
|
|
40
87
|
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
88
|
+
* Absolute operands are kept as written. Anything else is relative to the project the
|
|
89
|
+
* memory was recorded in, because that is the directory the statement was talking
|
|
90
|
+
* about — resolving against `process.cwd()` instead would make the same memory mean
|
|
91
|
+
* different things depending on where the command happened to be invoked.
|
|
92
|
+
*/
|
|
93
|
+
export declare function resolveOperand(path: string, cwd: string): string;
|
|
94
|
+
/**
|
|
95
|
+
* Evaluate one check and say what happened.
|
|
96
|
+
*
|
|
97
|
+
* The three-way split is the whole point. `broken` covers "the check could not be
|
|
98
|
+
* carried out" — an operand that resolves to nothing, a path that is a directory where
|
|
99
|
+
* a file was named, a file too large to scan, a manifest that will not parse — and is
|
|
100
|
+
* never evidence about the memory. `violated` is reserved for a check that ran and came
|
|
101
|
+
* back false.
|
|
102
|
+
*
|
|
103
|
+
* No exit code is invented here. There is no child process, so there is no status to
|
|
104
|
+
* report; `exitCode` is null and `output` carries the reason. The one place a caller
|
|
105
|
+
* needs a process-style signal — `ContextMemory.exitCode` — derives it from the summary,
|
|
106
|
+
* counting only `violated`.
|
|
44
107
|
*/
|
|
45
108
|
export declare function runGuard(id: string, guard: Guard, cwd: string, now?: Date): {
|
|
46
109
|
id: string;
|
|
47
110
|
outcome: GuardOutcome;
|
|
48
|
-
exitCode: null;
|
|
49
|
-
output: string;
|
|
50
|
-
at: string;
|
|
51
|
-
} | {
|
|
52
|
-
id: string;
|
|
53
|
-
outcome: GuardOutcome;
|
|
54
|
-
exitCode: number;
|
|
111
|
+
exitCode: number | null;
|
|
55
112
|
output: string;
|
|
56
113
|
at: string;
|
|
57
114
|
};
|
|
58
115
|
/**
|
|
59
116
|
* Classify an exit code into an outcome.
|
|
60
117
|
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
118
|
+
* Kept for callers holding a status they did not produce themselves — the CLI reports
|
|
119
|
+
* on log entries written by older versions of this plugin, which recorded a child
|
|
120
|
+
* process's status. 127 is the POSIX convention for "command not found" and 9009 is
|
|
121
|
+
* cmd.exe's, and both mean the check never ran.
|
|
122
|
+
*
|
|
123
|
+
* The textual fallback is gone along with the shell. It could only ever have worked on
|
|
124
|
+
* an English host, and a classifier that reads differently depending on the system
|
|
125
|
+
* language is not a classifier. `runGuard` no longer spawns anything, so no new entry
|
|
126
|
+
* can carry a status that needs interpreting here.
|
|
65
127
|
*/
|
|
66
|
-
export declare function classify(code: number
|
|
128
|
+
export declare function classify(code: number): GuardOutcome;
|