@namzu/cli 1.0.1 → 2.1.1
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.map +1 -1
- package/dist/cli.js +0 -2
- package/dist/cli.js.map +1 -1
- package/dist/integrations/providers/index.d.ts +0 -3
- package/dist/integrations/providers/index.d.ts.map +1 -1
- package/dist/integrations/providers/index.js +0 -5
- package/dist/integrations/providers/index.js.map +1 -1
- package/dist/tui/App.d.ts.map +1 -1
- package/dist/tui/App.js +8 -0
- package/dist/tui/App.js.map +1 -1
- package/dist/tui/agent.d.ts +10 -0
- package/dist/tui/agent.d.ts.map +1 -1
- package/dist/tui/agent.js +10 -0
- package/dist/tui/agent.js.map +1 -1
- package/dist/tui/slashCommands.d.ts +46 -0
- package/dist/tui/slashCommands.d.ts.map +1 -1
- package/dist/tui/slashCommands.js +96 -0
- package/dist/tui/slashCommands.js.map +1 -1
- package/package.json +11 -6
- package/dist/__tests__/environment-reaches-the-turn.test.d.ts +0 -17
- package/dist/__tests__/environment-reaches-the-turn.test.d.ts.map +0 -1
- package/dist/__tests__/environment-reaches-the-turn.test.js +0 -128
- package/dist/__tests__/environment-reaches-the-turn.test.js.map +0 -1
- package/dist/__tests__/headless-trust-gate.test.d.ts +0 -23
- package/dist/__tests__/headless-trust-gate.test.d.ts.map +0 -1
- package/dist/__tests__/headless-trust-gate.test.js +0 -227
- package/dist/__tests__/headless-trust-gate.test.js.map +0 -1
- package/dist/__tests__/mcp-servers-reach-the-session.test.d.ts +0 -17
- package/dist/__tests__/mcp-servers-reach-the-session.test.d.ts.map +0 -1
- package/dist/__tests__/mcp-servers-reach-the-session.test.js +0 -171
- package/dist/__tests__/mcp-servers-reach-the-session.test.js.map +0 -1
- package/dist/__tests__/permission-rules-reach-the-turn.test.d.ts +0 -31
- package/dist/__tests__/permission-rules-reach-the-turn.test.d.ts.map +0 -1
- package/dist/__tests__/permission-rules-reach-the-turn.test.js +0 -132
- package/dist/__tests__/permission-rules-reach-the-turn.test.js.map +0 -1
- package/dist/__tests__/project-instructions-reach-the-turn.test.d.ts +0 -24
- package/dist/__tests__/project-instructions-reach-the-turn.test.d.ts.map +0 -1
- package/dist/__tests__/project-instructions-reach-the-turn.test.js +0 -153
- package/dist/__tests__/project-instructions-reach-the-turn.test.js.map +0 -1
- package/dist/cli.test.d.ts +0 -2
- package/dist/cli.test.d.ts.map +0 -1
- package/dist/cli.test.js +0 -100
- package/dist/cli.test.js.map +0 -1
- package/dist/commands/__tests__/eval.test.d.ts +0 -2
- package/dist/commands/__tests__/eval.test.d.ts.map +0 -1
- package/dist/commands/__tests__/eval.test.js +0 -165
- package/dist/commands/__tests__/eval.test.js.map +0 -1
- package/dist/commands/__tests__/passthrough-help.test.d.ts +0 -2
- package/dist/commands/__tests__/passthrough-help.test.d.ts.map +0 -1
- package/dist/commands/__tests__/passthrough-help.test.js +0 -96
- package/dist/commands/__tests__/passthrough-help.test.js.map +0 -1
- package/dist/commands/__tests__/resume.test.d.ts +0 -15
- package/dist/commands/__tests__/resume.test.d.ts.map +0 -1
- package/dist/commands/__tests__/resume.test.js +0 -116
- package/dist/commands/__tests__/resume.test.js.map +0 -1
- package/dist/commands/__tests__/run-exit-code.test.d.ts +0 -2
- package/dist/commands/__tests__/run-exit-code.test.d.ts.map +0 -1
- package/dist/commands/__tests__/run-exit-code.test.js +0 -100
- package/dist/commands/__tests__/run-exit-code.test.js.map +0 -1
- package/dist/commands/__tests__/run-flags.test.d.ts +0 -11
- package/dist/commands/__tests__/run-flags.test.d.ts.map +0 -1
- package/dist/commands/__tests__/run-flags.test.js +0 -238
- package/dist/commands/__tests__/run-flags.test.js.map +0 -1
- package/dist/commands/__tests__/run-stream-flags.test.d.ts +0 -2
- package/dist/commands/__tests__/run-stream-flags.test.d.ts.map +0 -1
- package/dist/commands/__tests__/run-stream-flags.test.js +0 -295
- package/dist/commands/__tests__/run-stream-flags.test.js.map +0 -1
- package/dist/commands/doctor.test.d.ts +0 -2
- package/dist/commands/doctor.test.d.ts.map +0 -1
- package/dist/commands/doctor.test.js +0 -82
- package/dist/commands/doctor.test.js.map +0 -1
- package/dist/commands/providers.d.ts +0 -17
- package/dist/commands/providers.d.ts.map +0 -1
- package/dist/commands/providers.js +0 -274
- package/dist/commands/providers.js.map +0 -1
- package/dist/config/load.test.d.ts +0 -2
- package/dist/config/load.test.d.ts.map +0 -1
- package/dist/config/load.test.js +0 -57
- package/dist/config/load.test.js.map +0 -1
- package/dist/context/__tests__/environment.test.d.ts +0 -10
- package/dist/context/__tests__/environment.test.d.ts.map +0 -1
- package/dist/context/__tests__/environment.test.js +0 -84
- package/dist/context/__tests__/environment.test.js.map +0 -1
- package/dist/context/__tests__/project.test.d.ts +0 -12
- package/dist/context/__tests__/project.test.d.ts.map +0 -1
- package/dist/context/__tests__/project.test.js +0 -201
- package/dist/context/__tests__/project.test.js.map +0 -1
- package/dist/doctor/checks/credentials.test.d.ts +0 -10
- package/dist/doctor/checks/credentials.test.d.ts.map +0 -1
- package/dist/doctor/checks/credentials.test.js +0 -47
- package/dist/doctor/checks/credentials.test.js.map +0 -1
- package/dist/doctor/checks/sandbox.test.d.ts +0 -2
- package/dist/doctor/checks/sandbox.test.d.ts.map +0 -1
- package/dist/doctor/checks/sandbox.test.js +0 -59
- package/dist/doctor/checks/sandbox.test.js.map +0 -1
- package/dist/doctor/registry.test.d.ts +0 -7
- package/dist/doctor/registry.test.d.ts.map +0 -1
- package/dist/doctor/registry.test.js +0 -379
- package/dist/doctor/registry.test.js.map +0 -1
- package/dist/integrations/mcp/__tests__/connect.test.d.ts +0 -11
- package/dist/integrations/mcp/__tests__/connect.test.d.ts.map +0 -1
- package/dist/integrations/mcp/__tests__/connect.test.js +0 -231
- package/dist/integrations/mcp/__tests__/connect.test.js.map +0 -1
- package/dist/integrations/providers/discover.test.d.ts +0 -2
- package/dist/integrations/providers/discover.test.d.ts.map +0 -1
- package/dist/integrations/providers/discover.test.js +0 -93
- package/dist/integrations/providers/discover.test.js.map +0 -1
- package/dist/integrations/providers/keychain.test.d.ts +0 -2
- package/dist/integrations/providers/keychain.test.d.ts.map +0 -1
- package/dist/integrations/providers/keychain.test.js +0 -21
- package/dist/integrations/providers/keychain.test.js.map +0 -1
- package/dist/integrations/providers/mask.d.ts +0 -7
- package/dist/integrations/providers/mask.d.ts.map +0 -1
- package/dist/integrations/providers/mask.js +0 -14
- package/dist/integrations/providers/mask.js.map +0 -1
- package/dist/integrations/providers/mask.test.d.ts +0 -2
- package/dist/integrations/providers/mask.test.d.ts.map +0 -1
- package/dist/integrations/providers/mask.test.js +0 -20
- package/dist/integrations/providers/mask.test.js.map +0 -1
- package/dist/integrations/providers/oauth.test.d.ts +0 -2
- package/dist/integrations/providers/oauth.test.d.ts.map +0 -1
- package/dist/integrations/providers/oauth.test.js +0 -86
- package/dist/integrations/providers/oauth.test.js.map +0 -1
- package/dist/integrations/providers/preferences.test.d.ts +0 -2
- package/dist/integrations/providers/preferences.test.d.ts.map +0 -1
- package/dist/integrations/providers/preferences.test.js +0 -76
- package/dist/integrations/providers/preferences.test.js.map +0 -1
- package/dist/integrations/providers/schema.d.ts +0 -73
- package/dist/integrations/providers/schema.d.ts.map +0 -1
- package/dist/integrations/providers/schema.js +0 -70
- package/dist/integrations/providers/schema.js.map +0 -1
- package/dist/integrations/providers/schema.test.d.ts +0 -2
- package/dist/integrations/providers/schema.test.d.ts.map +0 -1
- package/dist/integrations/providers/schema.test.js +0 -43
- package/dist/integrations/providers/schema.test.js.map +0 -1
- package/dist/integrations/providers/store.d.ts +0 -32
- package/dist/integrations/providers/store.d.ts.map +0 -1
- package/dist/integrations/providers/store.js +0 -133
- package/dist/integrations/providers/store.js.map +0 -1
- package/dist/integrations/providers/store.test.d.ts +0 -2
- package/dist/integrations/providers/store.test.d.ts.map +0 -1
- package/dist/integrations/providers/store.test.js +0 -135
- package/dist/integrations/providers/store.test.js.map +0 -1
- package/dist/integrations/sessions/__tests__/archived-workspace.test.d.ts +0 -21
- package/dist/integrations/sessions/__tests__/archived-workspace.test.d.ts.map +0 -1
- package/dist/integrations/sessions/__tests__/archived-workspace.test.js +0 -62
- package/dist/integrations/sessions/__tests__/archived-workspace.test.js.map +0 -1
- package/dist/integrations/subagents/__tests__/delegation-trace.test.d.ts +0 -2
- package/dist/integrations/subagents/__tests__/delegation-trace.test.d.ts.map +0 -1
- package/dist/integrations/subagents/__tests__/delegation-trace.test.js +0 -114
- package/dist/integrations/subagents/__tests__/delegation-trace.test.js.map +0 -1
- package/dist/integrations/trust/store.test.d.ts +0 -2
- package/dist/integrations/trust/store.test.d.ts.map +0 -1
- package/dist/integrations/trust/store.test.js +0 -50
- package/dist/integrations/trust/store.test.js.map +0 -1
- package/dist/memory/store.test.d.ts +0 -2
- package/dist/memory/store.test.d.ts.map +0 -1
- package/dist/memory/store.test.js +0 -63
- package/dist/memory/store.test.js.map +0 -1
- package/dist/output/formatter.test.d.ts +0 -2
- package/dist/output/formatter.test.d.ts.map +0 -1
- package/dist/output/formatter.test.js +0 -88
- package/dist/output/formatter.test.js.map +0 -1
- package/dist/output/rendered-payload.test.d.ts +0 -2
- package/dist/output/rendered-payload.test.d.ts.map +0 -1
- package/dist/output/rendered-payload.test.js +0 -61
- package/dist/output/rendered-payload.test.js.map +0 -1
- package/dist/permissions/__tests__/mode.test.d.ts +0 -10
- package/dist/permissions/__tests__/mode.test.d.ts.map +0 -1
- package/dist/permissions/__tests__/mode.test.js +0 -115
- package/dist/permissions/__tests__/mode.test.js.map +0 -1
- package/dist/permissions/__tests__/rules.test.d.ts +0 -10
- package/dist/permissions/__tests__/rules.test.d.ts.map +0 -1
- package/dist/permissions/__tests__/rules.test.js +0 -196
- package/dist/permissions/__tests__/rules.test.js.map +0 -1
- package/dist/skills/store.test.d.ts +0 -2
- package/dist/skills/store.test.d.ts.map +0 -1
- package/dist/skills/store.test.js +0 -80
- package/dist/skills/store.test.js.map +0 -1
- package/dist/tui/__tests__/context-gauge.test.d.ts +0 -2
- package/dist/tui/__tests__/context-gauge.test.d.ts.map +0 -1
- package/dist/tui/__tests__/context-gauge.test.js +0 -83
- package/dist/tui/__tests__/context-gauge.test.js.map +0 -1
- package/dist/tui/__tests__/deferred-tool-discovery.test.d.ts +0 -24
- package/dist/tui/__tests__/deferred-tool-discovery.test.d.ts.map +0 -1
- package/dist/tui/__tests__/deferred-tool-discovery.test.js +0 -111
- package/dist/tui/__tests__/deferred-tool-discovery.test.js.map +0 -1
- package/dist/tui/__tests__/to-agent-event.test.d.ts +0 -2
- package/dist/tui/__tests__/to-agent-event.test.d.ts.map +0 -1
- package/dist/tui/__tests__/to-agent-event.test.js +0 -230
- package/dist/tui/__tests__/to-agent-event.test.js.map +0 -1
- package/dist/tui/__tests__/working-directory.test.d.ts +0 -13
- package/dist/tui/__tests__/working-directory.test.d.ts.map +0 -1
- package/dist/tui/__tests__/working-directory.test.js +0 -109
- package/dist/tui/__tests__/working-directory.test.js.map +0 -1
- package/dist/tui/agent.test.d.ts +0 -2
- package/dist/tui/agent.test.d.ts.map +0 -1
- package/dist/tui/agent.test.js +0 -225
- package/dist/tui/agent.test.js.map +0 -1
- package/dist/tui/markdownParser.test.d.ts +0 -2
- package/dist/tui/markdownParser.test.d.ts.map +0 -1
- package/dist/tui/markdownParser.test.js +0 -90
- package/dist/tui/markdownParser.test.js.map +0 -1
- package/dist/tui/mentions.test.d.ts +0 -2
- package/dist/tui/mentions.test.d.ts.map +0 -1
- package/dist/tui/mentions.test.js +0 -35
- package/dist/tui/mentions.test.js.map +0 -1
- package/dist/tui/slashCommands.test.d.ts +0 -2
- package/dist/tui/slashCommands.test.d.ts.map +0 -1
- package/dist/tui/slashCommands.test.js +0 -114
- package/dist/tui/slashCommands.test.js.map +0 -1
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The rule compiler.
|
|
3
|
-
*
|
|
4
|
-
* The load-bearing case is the LAST one: a tool nobody wrote a rule for must
|
|
5
|
-
* still be asked about. A declarative permission model that quietly widened the
|
|
6
|
-
* default would be a security downgrade wearing a feature's clothes, and it
|
|
7
|
-
* would look exactly like this change from the outside.
|
|
8
|
-
*/
|
|
9
|
-
import { VerificationGate, configureLogger, getRootLogger } from '@namzu/sdk';
|
|
10
|
-
import { describe, expect, it } from 'vitest';
|
|
11
|
-
import { bySpecificity, compilePermissions, matchesPattern, patternToRegExpSource, } from '../rules.js';
|
|
12
|
-
describe('pattern matching', () => {
|
|
13
|
-
it('matches a literal command exactly', () => {
|
|
14
|
-
expect(matchesPattern('git status', 'git status')).toBe(true);
|
|
15
|
-
expect(matchesPattern('git statuses', 'git status')).toBe(false);
|
|
16
|
-
});
|
|
17
|
-
it('lets * stand for any run of characters and ? for one', () => {
|
|
18
|
-
expect(matchesPattern('git push --force', 'git push*')).toBe(true);
|
|
19
|
-
expect(matchesPattern('cat a.ts', 'cat ?.ts')).toBe(true);
|
|
20
|
-
expect(matchesPattern('cat ab.ts', 'cat ?.ts')).toBe(false);
|
|
21
|
-
});
|
|
22
|
-
it('matches the bare command for a trailing " *"', () => {
|
|
23
|
-
// `git push *` is the obvious way to write "any git push". Without this
|
|
24
|
-
// the rule silently misses the exact invocation it was written for.
|
|
25
|
-
expect(matchesPattern('git push', 'git push *')).toBe(true);
|
|
26
|
-
expect(matchesPattern('git push origin main', 'git push *')).toBe(true);
|
|
27
|
-
});
|
|
28
|
-
it('treats a backslash path as its forward-slash self, both sides', () => {
|
|
29
|
-
// One rule has to work on every platform; an operator should not have to
|
|
30
|
-
// write the same path twice.
|
|
31
|
-
expect(matchesPattern('src\\index.ts', 'src/*.ts')).toBe(true);
|
|
32
|
-
expect(matchesPattern('src/index.ts', 'src\\*.ts')).toBe(true);
|
|
33
|
-
});
|
|
34
|
-
it('does not let a pattern character smuggle in a regex', () => {
|
|
35
|
-
// `.` is a literal dot, not "any character", or a rule meant to allow
|
|
36
|
-
// `a.ts` would also allow `axts`.
|
|
37
|
-
expect(matchesPattern('axts', 'a.ts')).toBe(false);
|
|
38
|
-
expect(matchesPattern('a.ts', 'a.ts')).toBe(true);
|
|
39
|
-
expect(patternToRegExpSource('a+b')).toBe('^a\\+b$');
|
|
40
|
-
});
|
|
41
|
-
it('anchors, so a pattern is not a substring search', () => {
|
|
42
|
-
expect(matchesPattern('sudo git status', 'git status')).toBe(false);
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
describe('specificity ordering', () => {
|
|
46
|
-
// The kernel evaluates first-match-wins, so order decides meaning: a bare
|
|
47
|
-
// `*` sorted first would make every rule after it dead config.
|
|
48
|
-
it('puts a bare star last', () => {
|
|
49
|
-
expect(['*', 'git push*', 'git status'].sort(bySpecificity)).toEqual([
|
|
50
|
-
'git status',
|
|
51
|
-
'git push*',
|
|
52
|
-
'*',
|
|
53
|
-
]);
|
|
54
|
-
});
|
|
55
|
-
it('puts a longer literal prefix first', () => {
|
|
56
|
-
expect(['git*', 'git push*'].sort(bySpecificity)).toEqual(['git push*', 'git*']);
|
|
57
|
-
});
|
|
58
|
-
it('is stable for two patterns of equal specificity', () => {
|
|
59
|
-
expect(['b*', 'a*'].sort(bySpecificity)).toEqual(['a*', 'b*']);
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
describe('compiling a permissions table', () => {
|
|
63
|
-
it('turns a bare allow and deny into by-name rules', () => {
|
|
64
|
-
const { rules } = compilePermissions({ read: 'allow', bash: 'deny' });
|
|
65
|
-
expect(rules).toEqual([
|
|
66
|
-
{ type: 'allow_by_name', toolNames: ['read'] },
|
|
67
|
-
{ type: 'deny_by_name', toolNames: ['bash'] },
|
|
68
|
-
]);
|
|
69
|
-
});
|
|
70
|
-
it('emits pattern rules most-specific-first', () => {
|
|
71
|
-
const { rules } = compilePermissions({ bash: { '*': 'deny', 'git status*': 'allow' } });
|
|
72
|
-
expect(rules.map((r) => ('decision' in r ? r.decision : r.type))).toEqual(['allow', 'deny']);
|
|
73
|
-
});
|
|
74
|
-
it('emits nothing for ask, because the gate already asks by default', () => {
|
|
75
|
-
// If "ask" emitted a rule it would have to mean something different from
|
|
76
|
-
// the absence of a rule, and it does not.
|
|
77
|
-
const { rules } = compilePermissions({ edit: 'ask', bash: { 'rm *': 'ask' } });
|
|
78
|
-
expect(rules).toEqual([]);
|
|
79
|
-
});
|
|
80
|
-
it('reports a bad effect instead of dropping it in silence', () => {
|
|
81
|
-
// A permission someone believes is in force and which was quietly
|
|
82
|
-
// discarded is the worst outcome here — worse than refusing to start.
|
|
83
|
-
const { rules, diagnostics } = compilePermissions({
|
|
84
|
-
bash: { 'git push*': 'maybe' },
|
|
85
|
-
});
|
|
86
|
-
expect(rules).toEqual([]);
|
|
87
|
-
expect(diagnostics).toHaveLength(1);
|
|
88
|
-
expect(diagnostics[0]?.tool).toBe('bash');
|
|
89
|
-
expect(diagnostics[0]?.pattern).toBe('git push*');
|
|
90
|
-
});
|
|
91
|
-
it('keeps the other rules when one line is unreadable', () => {
|
|
92
|
-
const { rules, diagnostics } = compilePermissions({
|
|
93
|
-
read: 'allow',
|
|
94
|
-
bash: 7,
|
|
95
|
-
glob: 'allow',
|
|
96
|
-
});
|
|
97
|
-
expect(rules).toHaveLength(2);
|
|
98
|
-
expect(diagnostics).toHaveLength(1);
|
|
99
|
-
});
|
|
100
|
-
it('a tool nobody wrote a rule for produces no rule, so the gate asks', () => {
|
|
101
|
-
// THE case. The kernel's fallback for an unmatched call is `review`, so
|
|
102
|
-
// emitting nothing is what makes an unconfigured or newly bridged tool
|
|
103
|
-
// prompt. There is deliberately no way to spell "allow by omission":
|
|
104
|
-
// widening the default has to be something an operator typed.
|
|
105
|
-
const { rules } = compilePermissions({ read: 'allow' });
|
|
106
|
-
expect(rules.some((r) => 'toolNames' in r && r.toolNames.includes('some_new_tool'))).toBe(false);
|
|
107
|
-
expect(rules).toHaveLength(1);
|
|
108
|
-
});
|
|
109
|
-
it('is empty for absent config, which is the same as asking about everything', () => {
|
|
110
|
-
expect(compilePermissions(undefined)).toEqual({ rules: [], diagnostics: [] });
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
configureLogger({ level: 'silent' });
|
|
114
|
-
describe('the rules actually decide a real call', () => {
|
|
115
|
-
// The compiler tests above assert the SHAPE of the emitted rules. Shape is
|
|
116
|
-
// not behaviour: the first version of this compiler emitted `target: 'args'`
|
|
117
|
-
// with an anchored pattern, which matches `JSON.stringify(input)` — so
|
|
118
|
-
// `^git push.*$` could never match `{"command":"git push origin main"}` and
|
|
119
|
-
// every pattern rule decided nothing at all. It looked correct in every
|
|
120
|
-
// shape assertion. These drive the kernel's own gate instead.
|
|
121
|
-
function decide(config, tool, input) {
|
|
122
|
-
const { rules } = compilePermissions(config);
|
|
123
|
-
const gate = new VerificationGate({
|
|
124
|
-
enabled: true,
|
|
125
|
-
rules: [...rules],
|
|
126
|
-
allowReadOnlyTools: false,
|
|
127
|
-
denyDangerousPatterns: false,
|
|
128
|
-
logDecisions: false,
|
|
129
|
-
}, getRootLogger());
|
|
130
|
-
return gate.evaluate({ toolName: tool, toolInput: input, toolDef: undefined });
|
|
131
|
-
}
|
|
132
|
-
it('allows the command an operator allowed', () => {
|
|
133
|
-
expect(decide({ bash: { 'git status*': 'allow' } }, 'bash', { command: 'git status' }).decision).toBe('allow');
|
|
134
|
-
});
|
|
135
|
-
it('denies the command an operator denied', () => {
|
|
136
|
-
const result = decide({ bash: { 'git push*': 'deny' } }, 'bash', {
|
|
137
|
-
command: 'git push origin main',
|
|
138
|
-
});
|
|
139
|
-
expect(result.decision).toBe('deny');
|
|
140
|
-
});
|
|
141
|
-
it('does not let one tool rule decide another tool call', () => {
|
|
142
|
-
// A rule written under `bash` must not decide an `edit` call whose
|
|
143
|
-
// arguments happen to contain the same text. The kernel's pattern rules
|
|
144
|
-
// carry no tool scope, so this is the CLI's job and it is easy to lose.
|
|
145
|
-
const result = decide({ bash: { 'git push*': 'deny' } }, 'edit', {
|
|
146
|
-
content: 'run git push to deploy',
|
|
147
|
-
});
|
|
148
|
-
expect(result.decision).toBe('review');
|
|
149
|
-
});
|
|
150
|
-
it('asks about a tool nobody wrote a rule for', () => {
|
|
151
|
-
// The load-bearing case, driven end to end rather than inferred.
|
|
152
|
-
expect(decide({ read: 'allow' }, 'some_new_bridged_tool', { x: 1 }).decision).toBe('review');
|
|
153
|
-
});
|
|
154
|
-
it('prefers the specific rule over the catch-all, whatever the config order', () => {
|
|
155
|
-
const config = { bash: { '*': 'allow', 'git push*': 'deny' } };
|
|
156
|
-
expect(decide(config, 'bash', { command: 'git push --force' }).decision).toBe('deny');
|
|
157
|
-
expect(decide(config, 'bash', { command: 'ls' }).decision).toBe('allow');
|
|
158
|
-
});
|
|
159
|
-
});
|
|
160
|
-
describe('what a denial actually says to the model', () => {
|
|
161
|
-
// The decision was never the part that was broken. A model told only
|
|
162
|
-
// "denied" rewords the command and tries again; a model told WHICH rule
|
|
163
|
-
// stopped it, and whether a different input could ever pass, can stop and
|
|
164
|
-
// say so. These assert the sentence, not the verdict.
|
|
165
|
-
function reasonFor(config, tool, input) {
|
|
166
|
-
const { rules } = compilePermissions(config);
|
|
167
|
-
const gate = new VerificationGate({
|
|
168
|
-
enabled: true,
|
|
169
|
-
rules: [...rules],
|
|
170
|
-
allowReadOnlyTools: false,
|
|
171
|
-
denyDangerousPatterns: false,
|
|
172
|
-
logDecisions: false,
|
|
173
|
-
}, getRootLogger());
|
|
174
|
-
return gate.evaluate({ toolName: tool, toolInput: input, toolDef: undefined }).reason;
|
|
175
|
-
}
|
|
176
|
-
it('a by-name denial says the tool is refused, so a reworded input will not help', () => {
|
|
177
|
-
const reason = reasonFor({ bash: 'deny' }, 'bash', { command: 'ls' });
|
|
178
|
-
expect(reason).toContain('bash');
|
|
179
|
-
// The part that stops the retry loop.
|
|
180
|
-
expect(reason).toContain('a different input will not change it');
|
|
181
|
-
});
|
|
182
|
-
it('a pattern denial quotes the rule that matched', () => {
|
|
183
|
-
const reason = reasonFor({ bash: { 'git push*': 'deny' } }, 'bash', {
|
|
184
|
-
command: 'git push --force',
|
|
185
|
-
});
|
|
186
|
-
expect(reason).toContain('pattern rule');
|
|
187
|
-
// The model is shown the rule itself, not the name of its category, so
|
|
188
|
-
// it can tell which of its options are closed and which are not.
|
|
189
|
-
expect(reason).toContain('git push');
|
|
190
|
-
});
|
|
191
|
-
it('an unmatched call says nothing matched, rather than naming a rule', () => {
|
|
192
|
-
const reason = reasonFor({ read: 'allow' }, 'some_new_tool', {});
|
|
193
|
-
expect(reason).toBe('No matching rule found');
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
//# sourceMappingURL=rules.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rules.test.js","sourceRoot":"","sources":["../../../src/permissions/__tests__/rules.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"store.test.d.ts","sourceRoot":"","sources":["../../src/skills/store.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
-
import { tmpdir } from 'node:os';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
5
|
-
import { composeSkillsPrompt, discoverSkills, loadSkillBody, parseSkillMarkdown } from './store.js';
|
|
6
|
-
let home;
|
|
7
|
-
let cwd;
|
|
8
|
-
function writeSkill(root, dir, contents) {
|
|
9
|
-
const skillDir = join(root, 'skills', dir);
|
|
10
|
-
mkdirSync(skillDir, { recursive: true });
|
|
11
|
-
writeFileSync(join(skillDir, 'SKILL.md'), contents);
|
|
12
|
-
}
|
|
13
|
-
beforeEach(() => {
|
|
14
|
-
home = mkdtempSync(join(tmpdir(), 'namzu-skh-'));
|
|
15
|
-
cwd = mkdtempSync(join(tmpdir(), 'namzu-skc-'));
|
|
16
|
-
mkdirSync(join(home, '.namzu'), { recursive: true });
|
|
17
|
-
});
|
|
18
|
-
afterEach(() => {
|
|
19
|
-
rmSync(home, { recursive: true, force: true });
|
|
20
|
-
rmSync(cwd, { recursive: true, force: true });
|
|
21
|
-
});
|
|
22
|
-
describe('parseSkillMarkdown', () => {
|
|
23
|
-
it('splits frontmatter from body', () => {
|
|
24
|
-
const parsed = parseSkillMarkdown('---\nname: Pirate\ndescription: talk like a pirate\n---\nArr matey.');
|
|
25
|
-
expect(parsed.name).toBe('Pirate');
|
|
26
|
-
expect(parsed.description).toBe('talk like a pirate');
|
|
27
|
-
expect(parsed.body).toBe('Arr matey.');
|
|
28
|
-
});
|
|
29
|
-
it('treats a file with no frontmatter as all body', () => {
|
|
30
|
-
const parsed = parseSkillMarkdown('just a body');
|
|
31
|
-
expect(parsed.name).toBeUndefined();
|
|
32
|
-
expect(parsed.body).toBe('just a body');
|
|
33
|
-
});
|
|
34
|
-
it('tolerates malformed frontmatter', () => {
|
|
35
|
-
const parsed = parseSkillMarkdown('---\n: : bad yaml :\n---\nbody here');
|
|
36
|
-
expect(parsed.body).toBe('body here');
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
describe('discoverSkills', () => {
|
|
40
|
-
it('returns empty when no skill dirs exist', () => {
|
|
41
|
-
expect(discoverSkills({ home, cwd })).toEqual([]);
|
|
42
|
-
});
|
|
43
|
-
it('discovers user + project skills, falling back to dir name', () => {
|
|
44
|
-
writeSkill(join(home, '.namzu'), 'greet', '---\ndescription: greets\n---\nSay hi.');
|
|
45
|
-
writeSkill(cwd, 'lint', 'no frontmatter body');
|
|
46
|
-
const skills = discoverSkills({ home, cwd });
|
|
47
|
-
expect(skills.map((s) => s.name).sort()).toEqual(['greet', 'lint']);
|
|
48
|
-
const greet = skills.find((s) => s.name === 'greet');
|
|
49
|
-
expect(greet?.source).toBe('user');
|
|
50
|
-
expect(greet?.description).toBe('greets');
|
|
51
|
-
expect(skills.find((s) => s.name === 'lint')?.description).toBe('(no description)');
|
|
52
|
-
});
|
|
53
|
-
it('lets a project skill shadow a user skill of the same name', () => {
|
|
54
|
-
writeSkill(join(home, '.namzu'), 'dup', '---\nname: dup\ndescription: user version\n---\nU');
|
|
55
|
-
writeSkill(cwd, 'dup', '---\nname: dup\ndescription: project version\n---\nP');
|
|
56
|
-
const skills = discoverSkills({ home, cwd });
|
|
57
|
-
const dup = skills.filter((s) => s.name === 'dup');
|
|
58
|
-
expect(dup).toHaveLength(1);
|
|
59
|
-
expect(dup[0]?.source).toBe('project');
|
|
60
|
-
expect(dup[0]?.description).toBe('project version');
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
describe('loadSkillBody', () => {
|
|
64
|
-
it('returns the body of a discovered skill', () => {
|
|
65
|
-
writeSkill(cwd, 'greet', '---\nname: greet\n---\nAlways greet warmly.');
|
|
66
|
-
const info = discoverSkills({ home, cwd }).find((s) => s.name === 'greet');
|
|
67
|
-
expect(info && loadSkillBody(info)).toBe('Always greet warmly.');
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
describe('composeSkillsPrompt', () => {
|
|
71
|
-
it('returns null when nothing is active', () => {
|
|
72
|
-
expect(composeSkillsPrompt([])).toBeNull();
|
|
73
|
-
});
|
|
74
|
-
it('frames each active skill', () => {
|
|
75
|
-
const prompt = composeSkillsPrompt([{ name: 'greet', body: 'Say hi.' }]);
|
|
76
|
-
expect(prompt).toContain('### Skill: greet');
|
|
77
|
-
expect(prompt).toContain('Say hi.');
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
//# sourceMappingURL=store.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"store.test.js","sourceRoot":"","sources":["../../src/skills/store.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"context-gauge.test.d.ts","sourceRoot":"","sources":["../../../src/tui/__tests__/context-gauge.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { buildGauge } from '../StatusBar.js';
|
|
3
|
-
/**
|
|
4
|
-
* What the footer's context bar may honestly claim.
|
|
5
|
-
*
|
|
6
|
-
* Two rules, and the second is the one with a history. The gauge must show no
|
|
7
|
-
* proportion it cannot ground — it previously grounded one on a two-branch
|
|
8
|
-
* guess at the window and a counter that measured something else entirely. And
|
|
9
|
-
* when it does show one, the reader must be able to tell a measurement from an
|
|
10
|
-
* inference, because a bar that looks measured and is not is the same defect
|
|
11
|
-
* in miniature.
|
|
12
|
-
*/
|
|
13
|
-
describe('buildGauge grounds the ratio or shows nothing', () => {
|
|
14
|
-
const measured = { measuredBy: 'provider', windowSource: 'model-table' };
|
|
15
|
-
it('divides context by window, not spend by a guess', () => {
|
|
16
|
-
const gauge = buildGauge({ tokens: 50_000, windowTokens: 200_000, ...measured });
|
|
17
|
-
expect(gauge?.pct).toBe(25);
|
|
18
|
-
});
|
|
19
|
-
it('shows nothing when the window is missing', () => {
|
|
20
|
-
expect(buildGauge({ tokens: 50_000, ...measured })).toBeNull();
|
|
21
|
-
});
|
|
22
|
-
it('shows nothing when the measurement is missing', () => {
|
|
23
|
-
expect(buildGauge({ windowTokens: 200_000, ...measured })).toBeNull();
|
|
24
|
-
});
|
|
25
|
-
it('shows nothing at all rather than a partial gauge', () => {
|
|
26
|
-
expect(buildGauge(null)).toBeNull();
|
|
27
|
-
expect(buildGauge(undefined)).toBeNull();
|
|
28
|
-
expect(buildGauge({})).toBeNull();
|
|
29
|
-
});
|
|
30
|
-
it('refuses a zero window instead of dividing by it', () => {
|
|
31
|
-
// Not reachable from today's kernel, which always resolves a positive
|
|
32
|
-
// window when it reports one — but Infinity rendered as a bar is a
|
|
33
|
-
// worse outcome than silence, and the guard costs one comparison.
|
|
34
|
-
expect(buildGauge({ tokens: 10, windowTokens: 0, ...measured })).toBeNull();
|
|
35
|
-
});
|
|
36
|
-
it('clamps rather than reporting over 100 percent', () => {
|
|
37
|
-
const gauge = buildGauge({ tokens: 400_000, windowTokens: 200_000, ...measured });
|
|
38
|
-
expect(gauge?.pct).toBe(100);
|
|
39
|
-
expect(gauge?.bar).not.toContain('░');
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
describe('buildGauge marks a ratio no stronger than its weaker term', () => {
|
|
43
|
-
it('presents a provider count against a known window as measured', () => {
|
|
44
|
-
const gauge = buildGauge({
|
|
45
|
-
tokens: 50_000,
|
|
46
|
-
windowTokens: 200_000,
|
|
47
|
-
measuredBy: 'provider',
|
|
48
|
-
windowSource: 'model-table',
|
|
49
|
-
});
|
|
50
|
-
expect(gauge?.approximate).toBe(false);
|
|
51
|
-
});
|
|
52
|
-
it('marks an estimated numerator', () => {
|
|
53
|
-
const gauge = buildGauge({
|
|
54
|
-
tokens: 50_000,
|
|
55
|
-
windowTokens: 200_000,
|
|
56
|
-
measuredBy: 'estimate',
|
|
57
|
-
windowSource: 'model-table',
|
|
58
|
-
});
|
|
59
|
-
expect(gauge?.approximate).toBe(true);
|
|
60
|
-
});
|
|
61
|
-
it('marks an assumed window even when the count is exact', () => {
|
|
62
|
-
// The case that makes this rule more than a rename of `measuredBy`.
|
|
63
|
-
// `windowSource: 'default'` is the kernel falling back to an assumed
|
|
64
|
-
// 128k because it recognised neither a configured window nor the
|
|
65
|
-
// model. An exact numerator over an invented denominator is a guess
|
|
66
|
-
// wearing a precise-looking figure, and marking only the numerator
|
|
67
|
-
// would repeat one level down the error this whole change fixes.
|
|
68
|
-
const gauge = buildGauge({
|
|
69
|
-
tokens: 50_000,
|
|
70
|
-
windowTokens: 128_000,
|
|
71
|
-
measuredBy: 'provider',
|
|
72
|
-
windowSource: 'default',
|
|
73
|
-
});
|
|
74
|
-
expect(gauge?.approximate).toBe(true);
|
|
75
|
-
});
|
|
76
|
-
it('treats an unstated provenance as not measured', () => {
|
|
77
|
-
// Silence is not a claim of accuracy. Defaulting the other way would
|
|
78
|
-
// let any future producer that forgets the field render as exact.
|
|
79
|
-
const gauge = buildGauge({ tokens: 50_000, windowTokens: 200_000 });
|
|
80
|
-
expect(gauge?.approximate).toBe(true);
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
//# sourceMappingURL=context-gauge.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"context-gauge.test.js","sourceRoot":"","sources":["../../../src/tui/__tests__/context-gauge.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `search_tools` is mounted where there is something to find, and not where
|
|
3
|
-
* there is not.
|
|
4
|
-
*
|
|
5
|
-
* The two registries come from the same builder, so "does the CLI register
|
|
6
|
-
* search_tools" has one answer from one call site and a different correct
|
|
7
|
-
* answer from the other — the shape that hides behind a green suite. The
|
|
8
|
-
* session passes a `taskStore`, so `query()` registers the task tools deferred
|
|
9
|
-
* and the search has a roster of three. A sub-agent is built with no task
|
|
10
|
-
* store, so nothing in its registry is deferred and the tool could only ever
|
|
11
|
-
* answer "no deferred tools matching X" — a capability advertised every turn
|
|
12
|
-
* that costs a turn to discover it is unusable.
|
|
13
|
-
*
|
|
14
|
-
* Removing the peer-daemon catalog is what created the asymmetry: that catalog
|
|
15
|
-
* used to supply BOTH registries, so both were justified in mounting the tool.
|
|
16
|
-
* Only one lost its supplier, and from the side that still works the loss is
|
|
17
|
-
* invisible.
|
|
18
|
-
*
|
|
19
|
-
* So this asserts the CONTRAST, not either half: a test that only checked the
|
|
20
|
-
* session would pass with the tool wrongly mounted on the sub-agent, which is
|
|
21
|
-
* exactly the state this file was written against.
|
|
22
|
-
*/
|
|
23
|
-
export {};
|
|
24
|
-
//# sourceMappingURL=deferred-tool-discovery.test.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"deferred-tool-discovery.test.d.ts","sourceRoot":"","sources":["../../../src/tui/__tests__/deferred-tool-discovery.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG"}
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `search_tools` is mounted where there is something to find, and not where
|
|
3
|
-
* there is not.
|
|
4
|
-
*
|
|
5
|
-
* The two registries come from the same builder, so "does the CLI register
|
|
6
|
-
* search_tools" has one answer from one call site and a different correct
|
|
7
|
-
* answer from the other — the shape that hides behind a green suite. The
|
|
8
|
-
* session passes a `taskStore`, so `query()` registers the task tools deferred
|
|
9
|
-
* and the search has a roster of three. A sub-agent is built with no task
|
|
10
|
-
* store, so nothing in its registry is deferred and the tool could only ever
|
|
11
|
-
* answer "no deferred tools matching X" — a capability advertised every turn
|
|
12
|
-
* that costs a turn to discover it is unusable.
|
|
13
|
-
*
|
|
14
|
-
* Removing the peer-daemon catalog is what created the asymmetry: that catalog
|
|
15
|
-
* used to supply BOTH registries, so both were justified in mounting the tool.
|
|
16
|
-
* Only one lost its supplier, and from the side that still works the loss is
|
|
17
|
-
* invisible.
|
|
18
|
-
*
|
|
19
|
-
* So this asserts the CONTRAST, not either half: a test that only checked the
|
|
20
|
-
* session would pass with the tool wrongly mounted on the sub-agent, which is
|
|
21
|
-
* exactly the state this file was written against.
|
|
22
|
-
*/
|
|
23
|
-
import { mkdtempSync, rmSync } from 'node:fs';
|
|
24
|
-
import { tmpdir } from 'node:os';
|
|
25
|
-
import { join } from 'node:path';
|
|
26
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
27
|
-
vi.mock('@namzu/sdk', async (importOriginal) => {
|
|
28
|
-
const actual = await importOriginal();
|
|
29
|
-
return {
|
|
30
|
-
...actual,
|
|
31
|
-
query: () => (async function* () { })(),
|
|
32
|
-
};
|
|
33
|
-
});
|
|
34
|
-
// The sub-agent's registry is built by a callback the session hands to this
|
|
35
|
-
// factory, so capturing the callback is the only way to see what a sub-agent
|
|
36
|
-
// would actually get — and it is the real wiring, not a re-derivation of it.
|
|
37
|
-
let capturedBuildTools = null;
|
|
38
|
-
vi.mock('../../integrations/subagents/runtime.js', () => ({
|
|
39
|
-
createSubagentRuntime: async (opts) => {
|
|
40
|
-
capturedBuildTools = opts.buildTools;
|
|
41
|
-
return {
|
|
42
|
-
gateway: {},
|
|
43
|
-
agentTool: {
|
|
44
|
-
name: 'Agent',
|
|
45
|
-
description: 'stub',
|
|
46
|
-
inputSchema: { type: 'object', properties: {} },
|
|
47
|
-
execute: async () => ({ success: true, output: '' }),
|
|
48
|
-
},
|
|
49
|
-
allowedAgentIds: [],
|
|
50
|
-
};
|
|
51
|
-
},
|
|
52
|
-
}));
|
|
53
|
-
let workDir;
|
|
54
|
-
beforeEach(() => {
|
|
55
|
-
capturedBuildTools = null;
|
|
56
|
-
workDir = mkdtempSync(join(tmpdir(), 'namzu-deferred-'));
|
|
57
|
-
});
|
|
58
|
-
afterEach(() => {
|
|
59
|
-
rmSync(workDir, { recursive: true, force: true });
|
|
60
|
-
});
|
|
61
|
-
const prefs = {
|
|
62
|
-
version: 2,
|
|
63
|
-
provider: 'anthropic',
|
|
64
|
-
subagents: { active: [] },
|
|
65
|
-
};
|
|
66
|
-
function detectedAnthropic() {
|
|
67
|
-
return [
|
|
68
|
-
{
|
|
69
|
-
entry: {
|
|
70
|
-
id: 'anthropic',
|
|
71
|
-
label: 'Anthropic',
|
|
72
|
-
defaultModel: 'claude-sonnet-4-5',
|
|
73
|
-
requiresApiKey: true,
|
|
74
|
-
envVars: ['ANTHROPIC_API_KEY'],
|
|
75
|
-
},
|
|
76
|
-
source: 'env',
|
|
77
|
-
apiKey: 'sk-ant-not-a-real-key',
|
|
78
|
-
alternatives: [],
|
|
79
|
-
},
|
|
80
|
-
];
|
|
81
|
-
}
|
|
82
|
-
describe('search_tools is mounted only where a deferred roster exists', () => {
|
|
83
|
-
it('the session offers it and a sub-agent does not', async () => {
|
|
84
|
-
const { createAgentSession } = await import('../agent.js');
|
|
85
|
-
const session = await createAgentSession(prefs, detectedAnthropic(), { cwd: workDir });
|
|
86
|
-
expect(session.hasProvider).toBe(true);
|
|
87
|
-
// The session opens a task store, so query() defers the task tools and
|
|
88
|
-
// the search has a roster.
|
|
89
|
-
expect(session.toolNames).toContain('search_tools');
|
|
90
|
-
expect(capturedBuildTools, 'the session must hand the sub-agent factory a tool builder').not.toBeNull();
|
|
91
|
-
const subagentTools = capturedBuildTools();
|
|
92
|
-
const subagentNames = subagentTools.listNames();
|
|
93
|
-
// The whole point: same builder, and this side must NOT have it.
|
|
94
|
-
expect(subagentNames).not.toContain('search_tools');
|
|
95
|
-
// And the sub-agent is not stripped of everything else in the process —
|
|
96
|
-
// an assertion that would still hold if `buildTools` returned an empty
|
|
97
|
-
// registry, which would "pass" the line above for the wrong reason.
|
|
98
|
-
expect(subagentNames).toContain('bash');
|
|
99
|
-
expect(subagentNames).toContain('read');
|
|
100
|
-
});
|
|
101
|
-
it('leaves the sub-agent nothing deferred, which is why the tool is withheld', async () => {
|
|
102
|
-
const { createAgentSession } = await import('../agent.js');
|
|
103
|
-
await createAgentSession(prefs, detectedAnthropic(), { cwd: workDir });
|
|
104
|
-
const subagentTools = capturedBuildTools();
|
|
105
|
-
const deferred = subagentTools
|
|
106
|
-
.listNames()
|
|
107
|
-
.filter((n) => subagentTools.getAvailability(n) === 'deferred');
|
|
108
|
-
expect(deferred).toEqual([]);
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
//# sourceMappingURL=deferred-tool-discovery.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"deferred-tool-discovery.test.js","sourceRoot":"","sources":["../../../src/tui/__tests__/deferred-tool-discovery.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"to-agent-event.test.d.ts","sourceRoot":"","sources":["../../../src/tui/__tests__/to-agent-event.test.ts"],"names":[],"mappings":""}
|