@namzu/cli 2.0.0 → 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/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 -108
- 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/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/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/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,231 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* An external tool server declared in the config is actually reachable.
|
|
3
|
-
*
|
|
4
|
-
* The server here is a REAL child process speaking real JSON-RPC over stdio,
|
|
5
|
-
* written to a temp file and spawned by the shipped transport. A stubbed
|
|
6
|
-
* transport would prove the adapter agrees with itself and would say nothing
|
|
7
|
-
* about the thing that actually breaks: a process that spawns, or does not, and
|
|
8
|
-
* a handshake that answers, or does not.
|
|
9
|
-
*/
|
|
10
|
-
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
11
|
-
import { tmpdir } from 'node:os';
|
|
12
|
-
import { join } from 'node:path';
|
|
13
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
14
|
-
import { connectMcpServers, transportFor } from '../servers.js';
|
|
15
|
-
let dir;
|
|
16
|
-
beforeEach(() => {
|
|
17
|
-
dir = mkdtempSync(join(tmpdir(), 'namzu-mcp-'));
|
|
18
|
-
});
|
|
19
|
-
afterEach(async () => {
|
|
20
|
-
// A child's working directory is the temp directory, and `close()` sends
|
|
21
|
-
// SIGTERM without waiting for the exit — so on Windows the directory is
|
|
22
|
-
// still held for a moment after the last assertion. Retried rather than
|
|
23
|
-
// ignored: a directory that never frees would mean a process that never
|
|
24
|
-
// died, which is the failure `close()` exists to prevent.
|
|
25
|
-
for (let attempt = 0; attempt < 20; attempt++) {
|
|
26
|
-
try {
|
|
27
|
-
rmSync(dir, { recursive: true, force: true });
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
catch {
|
|
31
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
rmSync(dir, { recursive: true, force: true });
|
|
35
|
-
});
|
|
36
|
-
/**
|
|
37
|
-
* Resolves once the process is gone, or throws after the deadline.
|
|
38
|
-
*
|
|
39
|
-
* This asserts the SYMPTOM — the pid no longer exists — and not the mechanism,
|
|
40
|
-
* and that distinction is worth stating because the same shape can be sound
|
|
41
|
-
* about the wrong thing: a child that would have exited on its own the moment
|
|
42
|
-
* its stdin pipe closed would satisfy this whether or not anything killed it,
|
|
43
|
-
* and the test would report that `close()` works while `close()` did nothing.
|
|
44
|
-
*
|
|
45
|
-
* MEASURED, not reasoned about. With `close()` replaced by an early `return`,
|
|
46
|
-
* this test fails with "process N was still alive after 5000ms" — so the child
|
|
47
|
-
* does not go away by itself here, and the pid disappearing is caused by the
|
|
48
|
-
* shutdown path under test. The symptom is the right claim for this package:
|
|
49
|
-
* whether the runtime also REAPS the child is the transport's business, and
|
|
50
|
-
* "no process left behind" is what a user is owed.
|
|
51
|
-
*/
|
|
52
|
-
async function waitForExit(pid, deadlineMs = 5_000) {
|
|
53
|
-
const until = Date.now() + deadlineMs;
|
|
54
|
-
for (;;) {
|
|
55
|
-
try {
|
|
56
|
-
// Signal 0 tests for existence without delivering anything.
|
|
57
|
-
process.kill(pid, 0);
|
|
58
|
-
}
|
|
59
|
-
catch {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
if (Date.now() > until)
|
|
63
|
-
throw new Error(`process ${pid} was still alive after ${deadlineMs}ms`);
|
|
64
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* The smallest server that satisfies the protocol: initialize, tools/list,
|
|
69
|
-
* tools/call. Newline-delimited JSON-RPC on stdio, which is the wire the
|
|
70
|
-
* shipped `StdioTransport` speaks.
|
|
71
|
-
*/
|
|
72
|
-
function writeServer(name, body) {
|
|
73
|
-
const path = join(dir, name);
|
|
74
|
-
writeFileSync(path, body);
|
|
75
|
-
return path;
|
|
76
|
-
}
|
|
77
|
-
const WORKING_SERVER = `
|
|
78
|
-
if (process.argv[2]) require('node:fs').writeFileSync(process.argv[2], String(process.pid))
|
|
79
|
-
let buf = ''
|
|
80
|
-
process.stdin.on('data', (chunk) => {
|
|
81
|
-
buf += chunk
|
|
82
|
-
let i
|
|
83
|
-
while ((i = buf.indexOf('\\n')) >= 0) {
|
|
84
|
-
const line = buf.slice(0, i)
|
|
85
|
-
buf = buf.slice(i + 1)
|
|
86
|
-
if (!line.trim()) continue
|
|
87
|
-
const msg = JSON.parse(line)
|
|
88
|
-
if (msg.method === 'initialize') {
|
|
89
|
-
send({ jsonrpc: '2.0', id: msg.id, result: {
|
|
90
|
-
protocolVersion: msg.params.protocolVersion,
|
|
91
|
-
serverInfo: { name: 'tickets', version: '1' },
|
|
92
|
-
capabilities: { tools: {} },
|
|
93
|
-
}})
|
|
94
|
-
} else if (msg.method === 'tools/list') {
|
|
95
|
-
send({ jsonrpc: '2.0', id: msg.id, result: { tools: [
|
|
96
|
-
{ name: 'create', description: 'Open a ticket', inputSchema: {
|
|
97
|
-
type: 'object', properties: { title: { type: 'string' } }, required: ['title'] } },
|
|
98
|
-
{ name: 'close', description: 'Close a ticket', inputSchema: {
|
|
99
|
-
type: 'object', properties: { id: { type: 'string' } }, required: ['id'] } },
|
|
100
|
-
]}})
|
|
101
|
-
} else if (msg.method === 'tools/call') {
|
|
102
|
-
send({ jsonrpc: '2.0', id: msg.id, result: {
|
|
103
|
-
content: [{ type: 'text', text: 'opened ' + msg.params.arguments.title }],
|
|
104
|
-
}})
|
|
105
|
-
} else if (msg.id !== undefined) {
|
|
106
|
-
send({ jsonrpc: '2.0', id: msg.id, result: {} })
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
})
|
|
110
|
-
function send(o) { process.stdout.write(JSON.stringify(o) + '\\n') }
|
|
111
|
-
`;
|
|
112
|
-
/** Starts, and never answers. The case a request timeout cannot cover. */
|
|
113
|
-
const SILENT_SERVER = 'setInterval(() => {}, 1000)\n';
|
|
114
|
-
describe('a declared server', () => {
|
|
115
|
-
it('brings its tools, named after it', async () => {
|
|
116
|
-
const server = writeServer('tickets.js', WORKING_SERVER);
|
|
117
|
-
const mcp = await connectMcpServers({ tickets: { command: process.execPath, args: [server] } }, { cwd: dir });
|
|
118
|
-
try {
|
|
119
|
-
expect(mcp.failed).toEqual([]);
|
|
120
|
-
expect(mcp.connected).toEqual([{ name: 'tickets', toolCount: 2 }]);
|
|
121
|
-
// Prefixed with the server name so two servers offering `create` do
|
|
122
|
-
// not collide and the transcript says where a call went.
|
|
123
|
-
expect(mcp.tools.map((t) => t.name)).toEqual(['mcp_tickets_create', 'mcp_tickets_close']);
|
|
124
|
-
}
|
|
125
|
-
finally {
|
|
126
|
-
await mcp.close();
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
it('produces tools that actually call the server', async () => {
|
|
130
|
-
// The tool objects existing is not the feature. Registering something the
|
|
131
|
-
// model can see and cannot successfully invoke is worse than having
|
|
132
|
-
// nothing, because the failure arrives mid-task.
|
|
133
|
-
const server = writeServer('tickets.js', WORKING_SERVER);
|
|
134
|
-
const mcp = await connectMcpServers({ tickets: { command: process.execPath, args: [server] } }, { cwd: dir });
|
|
135
|
-
try {
|
|
136
|
-
const create = mcp.tools.find((t) => t.name === 'mcp_tickets_create');
|
|
137
|
-
const result = await create?.execute({ title: 'the build is red' }, {});
|
|
138
|
-
expect(result?.success).toBe(true);
|
|
139
|
-
expect(JSON.stringify(result?.output)).toContain('opened the build is red');
|
|
140
|
-
}
|
|
141
|
-
finally {
|
|
142
|
-
await mcp.close();
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
|
-
it('leaves no child process behind when the session closes', async () => {
|
|
146
|
-
// The reason `close()` exists. A stdio server is a child process and
|
|
147
|
-
// nothing else in this package owns one, so before this there was no
|
|
148
|
-
// shutdown path at all: every TUI session and every one-shot left its
|
|
149
|
-
// servers running.
|
|
150
|
-
const server = writeServer('tickets.js', WORKING_SERVER);
|
|
151
|
-
const pidFile = join(dir, 'server.pid');
|
|
152
|
-
const mcp = await connectMcpServers({ tickets: { command: process.execPath, args: [server, pidFile] } }, { cwd: dir });
|
|
153
|
-
const pid = Number(readFileSync(pidFile, 'utf8'));
|
|
154
|
-
expect(Number.isInteger(pid), 'the server must have actually started').toBe(true);
|
|
155
|
-
expect(() => process.kill(pid, 0), 'and must be alive before the close').not.toThrow();
|
|
156
|
-
await mcp.close();
|
|
157
|
-
await waitForExit(pid);
|
|
158
|
-
}, 20_000);
|
|
159
|
-
it('is absent from the roster when nothing is configured', async () => {
|
|
160
|
-
const mcp = await connectMcpServers(undefined, { cwd: dir });
|
|
161
|
-
expect(mcp.tools).toEqual([]);
|
|
162
|
-
expect(mcp.connected).toEqual([]);
|
|
163
|
-
expect(mcp.failed).toEqual([]);
|
|
164
|
-
await mcp.close();
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
describe('a server that does not work is named, never merely absent', () => {
|
|
168
|
-
it('reports a command that cannot be spawned', async () => {
|
|
169
|
-
const mcp = await connectMcpServers({ tickets: { command: join(dir, 'no-such-executable') } }, { cwd: dir });
|
|
170
|
-
expect(mcp.connected).toEqual([]);
|
|
171
|
-
expect(mcp.failed).toHaveLength(1);
|
|
172
|
-
expect(mcp.failed[0]?.name).toBe('tickets');
|
|
173
|
-
expect(mcp.failed[0]?.reason.length).toBeGreaterThan(0);
|
|
174
|
-
await mcp.close();
|
|
175
|
-
});
|
|
176
|
-
it('reports a server that starts and never answers, rather than hanging', async () => {
|
|
177
|
-
// The client's per-request timeout cannot cover this: the process is up,
|
|
178
|
-
// the pipe is open, and nothing is coming. Without the connect deadline a
|
|
179
|
-
// single wedged server holds the whole session open before the first turn
|
|
180
|
-
// — no error, no failure, just a namzu that does not start.
|
|
181
|
-
const server = writeServer('silent.js', SILENT_SERVER);
|
|
182
|
-
const started = Date.now();
|
|
183
|
-
const mcp = await connectMcpServers({ quiet: { command: process.execPath, args: [server] } }, { cwd: dir });
|
|
184
|
-
expect(mcp.failed[0]?.name).toBe('quiet');
|
|
185
|
-
expect(mcp.failed[0]?.reason).toContain('did not answer');
|
|
186
|
-
expect(Date.now() - started, 'the deadline has to actually bound it').toBeLessThan(30_000);
|
|
187
|
-
await mcp.close();
|
|
188
|
-
}, 40_000);
|
|
189
|
-
it('does not let one broken server take the working ones with it', async () => {
|
|
190
|
-
const good = writeServer('tickets.js', WORKING_SERVER);
|
|
191
|
-
const mcp = await connectMcpServers({
|
|
192
|
-
broken: { command: join(dir, 'no-such-executable') },
|
|
193
|
-
tickets: { command: process.execPath, args: [good] },
|
|
194
|
-
}, { cwd: dir });
|
|
195
|
-
try {
|
|
196
|
-
expect(mcp.failed.map((f) => f.name)).toEqual(['broken']);
|
|
197
|
-
expect(mcp.connected.map((c) => c.name)).toEqual(['tickets']);
|
|
198
|
-
}
|
|
199
|
-
finally {
|
|
200
|
-
await mcp.close();
|
|
201
|
-
}
|
|
202
|
-
});
|
|
203
|
-
});
|
|
204
|
-
describe('a spec that is not a server', () => {
|
|
205
|
-
it('refuses one that names neither a command nor a url', () => {
|
|
206
|
-
expect(transportFor({}, dir)).toContain('neither a command nor a url');
|
|
207
|
-
});
|
|
208
|
-
it('refuses one that names both, rather than picking', () => {
|
|
209
|
-
// An operator who edited a url into an entry that already had a command
|
|
210
|
-
// meant one of them. Choosing for them runs something they did not ask
|
|
211
|
-
// to run.
|
|
212
|
-
expect(transportFor({ command: 'x', url: 'https://y' }, dir)).toContain('both');
|
|
213
|
-
});
|
|
214
|
-
it('reports a bad spec by name, alongside the servers that worked', async () => {
|
|
215
|
-
const good = writeServer('tickets.js', WORKING_SERVER);
|
|
216
|
-
const mcp = await connectMcpServers({ nonsense: {}, tickets: { command: process.execPath, args: [good] } }, { cwd: dir });
|
|
217
|
-
try {
|
|
218
|
-
expect(mcp.failed[0]?.name).toBe('nonsense');
|
|
219
|
-
expect(mcp.connected.map((c) => c.name)).toEqual(['tickets']);
|
|
220
|
-
}
|
|
221
|
-
finally {
|
|
222
|
-
await mcp.close();
|
|
223
|
-
}
|
|
224
|
-
});
|
|
225
|
-
it('defaults a stdio server to the agent working directory', () => {
|
|
226
|
-
const transport = transportFor({ command: 'node' }, dir);
|
|
227
|
-
expect(typeof transport).not.toBe('string');
|
|
228
|
-
expect(transport.cwd).toBe(dir);
|
|
229
|
-
});
|
|
230
|
-
});
|
|
231
|
-
//# sourceMappingURL=connect.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"connect.test.js","sourceRoot":"","sources":["../../../../src/integrations/mcp/__tests__/connect.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"discover.test.d.ts","sourceRoot":"","sources":["../../../src/integrations/providers/discover.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import { mkdtempSync } from 'node:fs';
|
|
2
|
-
import { tmpdir } from 'node:os';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
5
|
-
import { discoverProviders, findDetected } from './discover.js';
|
|
6
|
-
function tmpHome() {
|
|
7
|
-
const home = mkdtempSync(join(tmpdir(), 'namzu-discover-'));
|
|
8
|
-
return home;
|
|
9
|
-
}
|
|
10
|
-
// Every test must opt out of host-ambient sources (Keychain, network
|
|
11
|
-
// probes) so the suite stays hermetic regardless of who runs it. The
|
|
12
|
-
// keychain code path is covered by a focused unit test below.
|
|
13
|
-
const HERMETIC = { skipProbes: true, skipKeychain: true };
|
|
14
|
-
describe('discoverProviders — env-var scan', () => {
|
|
15
|
-
it('picks anthropic from ANTHROPIC_API_KEY', async () => {
|
|
16
|
-
const list = await discoverProviders({
|
|
17
|
-
...HERMETIC,
|
|
18
|
-
env: { ANTHROPIC_API_KEY: 'sk-ant-test' },
|
|
19
|
-
home: tmpHome(),
|
|
20
|
-
});
|
|
21
|
-
const anthropic = findDetected(list, 'anthropic');
|
|
22
|
-
expect(anthropic).not.toBeNull();
|
|
23
|
-
expect(anthropic?.apiKey).toBe('sk-ant-test');
|
|
24
|
-
expect(anthropic?.source.kind).toBe('env');
|
|
25
|
-
if (anthropic?.source.kind === 'env') {
|
|
26
|
-
expect(anthropic.source.envName).toBe('ANTHROPIC_API_KEY');
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
it('falls back to CLAUDE_CODE_OAUTH_TOKEN if no anthropic key set', async () => {
|
|
30
|
-
const list = await discoverProviders({
|
|
31
|
-
...HERMETIC,
|
|
32
|
-
env: { CLAUDE_CODE_OAUTH_TOKEN: 'oauth-tok' },
|
|
33
|
-
home: tmpHome(),
|
|
34
|
-
});
|
|
35
|
-
const anthropic = findDetected(list, 'anthropic');
|
|
36
|
-
expect(anthropic?.apiKey).toBe('oauth-tok');
|
|
37
|
-
if (anthropic?.source.kind === 'env') {
|
|
38
|
-
expect(anthropic.source.envName).toBe('CLAUDE_CODE_OAUTH_TOKEN');
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
it('returns empty list when no env + no secrets + no probes + no keychain', async () => {
|
|
42
|
-
const list = await discoverProviders({
|
|
43
|
-
...HERMETIC,
|
|
44
|
-
env: {},
|
|
45
|
-
home: tmpHome(),
|
|
46
|
-
});
|
|
47
|
-
expect(list).toHaveLength(0);
|
|
48
|
-
});
|
|
49
|
-
it('detects multiple providers in one scan', async () => {
|
|
50
|
-
const list = await discoverProviders({
|
|
51
|
-
...HERMETIC,
|
|
52
|
-
env: { ANTHROPIC_API_KEY: 'a', OPENAI_API_KEY: 'o' },
|
|
53
|
-
home: tmpHome(),
|
|
54
|
-
});
|
|
55
|
-
expect(findDetected(list, 'anthropic')).not.toBeNull();
|
|
56
|
-
expect(findDetected(list, 'openai')).not.toBeNull();
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
describe('discoverProviders — local probes', () => {
|
|
60
|
-
it('detects ollama when its probe URL is reachable', async () => {
|
|
61
|
-
const fetchMock = vi.fn().mockResolvedValue(new Response('', { status: 200 }));
|
|
62
|
-
const list = await discoverProviders({
|
|
63
|
-
skipKeychain: true,
|
|
64
|
-
env: {},
|
|
65
|
-
home: tmpHome(),
|
|
66
|
-
fetch: fetchMock,
|
|
67
|
-
});
|
|
68
|
-
const ollama = findDetected(list, 'ollama');
|
|
69
|
-
expect(ollama).not.toBeNull();
|
|
70
|
-
expect(ollama?.source.kind).toBe('probe');
|
|
71
|
-
});
|
|
72
|
-
it('does not include ollama when its probe fails', async () => {
|
|
73
|
-
const fetchMock = vi.fn().mockRejectedValue(new Error('ECONNREFUSED'));
|
|
74
|
-
const list = await discoverProviders({
|
|
75
|
-
skipKeychain: true,
|
|
76
|
-
env: {},
|
|
77
|
-
home: tmpHome(),
|
|
78
|
-
fetch: fetchMock,
|
|
79
|
-
});
|
|
80
|
-
expect(findDetected(list, 'ollama')).toBeNull();
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
describe('discoverProviders — http provider', () => {
|
|
84
|
-
it('is never auto-discovered (no envVars, no probe)', async () => {
|
|
85
|
-
const list = await discoverProviders({
|
|
86
|
-
...HERMETIC,
|
|
87
|
-
env: {},
|
|
88
|
-
home: tmpHome(),
|
|
89
|
-
});
|
|
90
|
-
expect(findDetected(list, 'http')).toBeNull();
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
//# sourceMappingURL=discover.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"discover.test.js","sourceRoot":"","sources":["../../../src/integrations/providers/discover.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"keychain.test.d.ts","sourceRoot":"","sources":["../../../src/integrations/providers/keychain.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { isAnthropicOAuthToken } from './keychain.js';
|
|
3
|
-
describe('isAnthropicOAuthToken', () => {
|
|
4
|
-
it('detects Claude Code OAuth tokens (cc- prefix)', () => {
|
|
5
|
-
expect(isAnthropicOAuthToken('cc-some-opaque-id')).toBe(true);
|
|
6
|
-
});
|
|
7
|
-
it('detects Anthropic setup OAuth tokens (sk-ant-oat- prefix)', () => {
|
|
8
|
-
expect(isAnthropicOAuthToken('sk-ant-oat01-...')).toBe(true);
|
|
9
|
-
});
|
|
10
|
-
it('detects JWT-shaped tokens (eyJ prefix)', () => {
|
|
11
|
-
expect(isAnthropicOAuthToken('eyJhbGciOiJIUzI1NiIs...')).toBe(true);
|
|
12
|
-
});
|
|
13
|
-
it('rejects console API keys (sk-ant-api*)', () => {
|
|
14
|
-
expect(isAnthropicOAuthToken('sk-ant-api03-deadbeef')).toBe(false);
|
|
15
|
-
});
|
|
16
|
-
it('defaults to false (API-key path) for unknown shapes', () => {
|
|
17
|
-
expect(isAnthropicOAuthToken('arbitrary-string')).toBe(false);
|
|
18
|
-
expect(isAnthropicOAuthToken('')).toBe(false);
|
|
19
|
-
});
|
|
20
|
-
});
|
|
21
|
-
//# sourceMappingURL=keychain.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"keychain.test.js","sourceRoot":"","sources":["../../../src/integrations/providers/keychain.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAE7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAErD,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACtC,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACxD,MAAM,CAAC,qBAAqB,CAAC,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC9D,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACpE,MAAM,CAAC,qBAAqB,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC7D,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QACjD,MAAM,CAAC,qBAAqB,CAAC,yBAAyB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACpE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QACjD,MAAM,CAAC,qBAAqB,CAAC,uBAAuB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACnE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC9D,MAAM,CAAC,qBAAqB,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAC7D,MAAM,CAAC,qBAAqB,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC9C,CAAC,CAAC,CAAA;AACH,CAAC,CAAC,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"oauth.test.d.ts","sourceRoot":"","sources":["../../../src/integrations/providers/oauth.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
// Stub the Keychain write so an expired-token refresh under test never touches
|
|
3
|
-
// the real macOS Keychain (this suite can run on the developer's own machine).
|
|
4
|
-
vi.mock('./keychain.js', async (importOriginal) => {
|
|
5
|
-
const actual = await importOriginal();
|
|
6
|
-
return { ...actual, writeAgentKeychainCredential: vi.fn(() => false) };
|
|
7
|
-
});
|
|
8
|
-
import { ensureFreshAnthropicToken, refreshAgentOAuthToken } from './oauth.js';
|
|
9
|
-
function mockFetch(impl) {
|
|
10
|
-
vi.stubGlobal('fetch', impl);
|
|
11
|
-
}
|
|
12
|
-
afterEach(() => {
|
|
13
|
-
vi.unstubAllGlobals();
|
|
14
|
-
});
|
|
15
|
-
describe('refreshAgentOAuthToken', () => {
|
|
16
|
-
it('exchanges the refresh token and maps the response', async () => {
|
|
17
|
-
mockFetch((async () => new Response(JSON.stringify({
|
|
18
|
-
access_token: 'cc-new-access',
|
|
19
|
-
refresh_token: 'rt-new',
|
|
20
|
-
expires_in: 3600,
|
|
21
|
-
}), { status: 200 })));
|
|
22
|
-
const before = Date.now();
|
|
23
|
-
const cred = await refreshAgentOAuthToken('rt-old');
|
|
24
|
-
expect(cred?.accessToken).toBe('cc-new-access');
|
|
25
|
-
expect(cred?.refreshToken).toBe('rt-new');
|
|
26
|
-
expect(cred?.expiresAt).toBeGreaterThanOrEqual(before + 3600 * 1000);
|
|
27
|
-
});
|
|
28
|
-
it('keeps the old refresh token when the response omits one', async () => {
|
|
29
|
-
mockFetch((async () => new Response(JSON.stringify({ access_token: 'cc-new' }), { status: 200 })));
|
|
30
|
-
const cred = await refreshAgentOAuthToken('rt-old');
|
|
31
|
-
expect(cred?.accessToken).toBe('cc-new');
|
|
32
|
-
expect(cred?.refreshToken).toBe('rt-old');
|
|
33
|
-
});
|
|
34
|
-
it('returns null on a non-2xx response', async () => {
|
|
35
|
-
mockFetch((async () => new Response('nope', { status: 401 })));
|
|
36
|
-
expect(await refreshAgentOAuthToken('rt')).toBeNull();
|
|
37
|
-
});
|
|
38
|
-
it('returns null when fetch throws', async () => {
|
|
39
|
-
mockFetch((async () => {
|
|
40
|
-
throw new Error('network down');
|
|
41
|
-
}));
|
|
42
|
-
expect(await refreshAgentOAuthToken('rt')).toBeNull();
|
|
43
|
-
});
|
|
44
|
-
it('returns null when the payload lacks an access token', async () => {
|
|
45
|
-
mockFetch((async () => new Response(JSON.stringify({ foo: 'bar' }), { status: 200 })));
|
|
46
|
-
expect(await refreshAgentOAuthToken('rt')).toBeNull();
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
describe('ensureFreshAnthropicToken', () => {
|
|
50
|
-
it('returns the current token when it is not near expiry (no refresh attempted)', async () => {
|
|
51
|
-
const spy = vi.fn();
|
|
52
|
-
mockFetch(spy);
|
|
53
|
-
const token = await ensureFreshAnthropicToken('cc-current', {
|
|
54
|
-
refreshToken: 'rt',
|
|
55
|
-
expiresAt: Date.now() + 60 * 60 * 1000,
|
|
56
|
-
});
|
|
57
|
-
expect(token).toBe('cc-current');
|
|
58
|
-
expect(spy).not.toHaveBeenCalled();
|
|
59
|
-
});
|
|
60
|
-
it('returns the current token when there is no refresh token', async () => {
|
|
61
|
-
const spy = vi.fn();
|
|
62
|
-
mockFetch(spy);
|
|
63
|
-
const token = await ensureFreshAnthropicToken('cc-current', { expiresAt: 0 });
|
|
64
|
-
expect(token).toBe('cc-current');
|
|
65
|
-
expect(spy).not.toHaveBeenCalled();
|
|
66
|
-
});
|
|
67
|
-
it('refreshes an expired token', async () => {
|
|
68
|
-
mockFetch((async () => new Response(JSON.stringify({ access_token: 'cc-fresh', expires_in: 3600 }), {
|
|
69
|
-
status: 200,
|
|
70
|
-
})));
|
|
71
|
-
const token = await ensureFreshAnthropicToken('cc-stale', {
|
|
72
|
-
refreshToken: 'rt',
|
|
73
|
-
expiresAt: Date.now() - 1000,
|
|
74
|
-
});
|
|
75
|
-
expect(token).toBe('cc-fresh');
|
|
76
|
-
});
|
|
77
|
-
it('falls back to the stale token when refresh fails', async () => {
|
|
78
|
-
mockFetch((async () => new Response('err', { status: 500 })));
|
|
79
|
-
const token = await ensureFreshAnthropicToken('cc-stale', {
|
|
80
|
-
refreshToken: 'rt',
|
|
81
|
-
expiresAt: Date.now() - 1000,
|
|
82
|
-
});
|
|
83
|
-
expect(token).toBe('cc-stale');
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
//# sourceMappingURL=oauth.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"oauth.test.js","sourceRoot":"","sources":["../../../src/integrations/providers/oauth.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"preferences.test.d.ts","sourceRoot":"","sources":["../../../src/integrations/providers/preferences.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { mkdirSync, mkdtempSync, statSync, writeFileSync } from 'node:fs';
|
|
2
|
-
import { tmpdir } from 'node:os';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
|
-
import { describe, expect, it } from 'vitest';
|
|
5
|
-
import { PreferencesError, preferencesPath, readPreferences, writePreferences, } from './preferences.js';
|
|
6
|
-
/**
|
|
7
|
-
* POSIX file modes do not exist on Windows: `chmod` is a no-op there and
|
|
8
|
-
* `fs.stat().mode` reports a fixed value, so these cases assert a
|
|
9
|
-
* permission the platform cannot enforce. Skipping keeps the suite
|
|
10
|
-
* meaningful on Windows instead of permanently red — the behavior itself
|
|
11
|
-
* is still covered on Linux and macOS, where it actually matters.
|
|
12
|
-
*/
|
|
13
|
-
const IS_WINDOWS = process.platform === 'win32';
|
|
14
|
-
function tmpHome() {
|
|
15
|
-
return mkdtempSync(join(tmpdir(), 'namzu-prefs-v2-'));
|
|
16
|
-
}
|
|
17
|
-
describe('readPreferences', () => {
|
|
18
|
-
it('reports missing when the file is absent', () => {
|
|
19
|
-
expect(readPreferences(tmpHome())).toEqual({ status: 'missing' });
|
|
20
|
-
});
|
|
21
|
-
it('detects a v1 file as needs-repick (legacy schema)', () => {
|
|
22
|
-
const home = tmpHome();
|
|
23
|
-
mkdirSync(join(home, '.namzu'));
|
|
24
|
-
writeFileSync(join(home, '.namzu', 'preferences.json'), JSON.stringify({ version: 1, default: 'claude', active: ['claude'] }));
|
|
25
|
-
const r = readPreferences(home);
|
|
26
|
-
expect(r.status).toBe('needs-repick');
|
|
27
|
-
if (r.status === 'needs-repick') {
|
|
28
|
-
expect(r.reason).toMatch(/older schema/);
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
it('round-trips a v2 selection', () => {
|
|
32
|
-
const home = tmpHome();
|
|
33
|
-
writePreferences({ version: 2, provider: 'anthropic', model: 'claude-opus-4-7' }, home);
|
|
34
|
-
const r = readPreferences(home);
|
|
35
|
-
expect(r.status).toBe('ok');
|
|
36
|
-
if (r.status === 'ok') {
|
|
37
|
-
expect(r.prefs.provider).toBe('anthropic');
|
|
38
|
-
expect(r.prefs.model).toBe('claude-opus-4-7');
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
it('throws on unparseable JSON', () => {
|
|
42
|
-
const home = tmpHome();
|
|
43
|
-
mkdirSync(join(home, '.namzu'));
|
|
44
|
-
writeFileSync(join(home, '.namzu', 'preferences.json'), '{ not json');
|
|
45
|
-
expect(() => readPreferences(home)).toThrow(PreferencesError);
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
describe('writePreferences', () => {
|
|
49
|
-
it.skipIf(IS_WINDOWS)('enforces mode 0600 on file and 0700 on dir', () => {
|
|
50
|
-
const home = tmpHome();
|
|
51
|
-
writePreferences({ version: 2, provider: 'openai' }, home);
|
|
52
|
-
const fileMode = statSync(preferencesPath(home)).mode & 0o777;
|
|
53
|
-
const dirMode = statSync(join(home, '.namzu')).mode & 0o777;
|
|
54
|
-
expect(fileMode).toBe(0o600);
|
|
55
|
-
expect(dirMode).toBe(0o700);
|
|
56
|
-
});
|
|
57
|
-
it('rejects an empty provider', () => {
|
|
58
|
-
expect(() => writePreferences({ version: 2, provider: '' }, tmpHome())).toThrow(PreferencesError);
|
|
59
|
-
});
|
|
60
|
-
it('preserves subagents.active when present', () => {
|
|
61
|
-
const home = tmpHome();
|
|
62
|
-
writePreferences({
|
|
63
|
-
version: 2,
|
|
64
|
-
provider: 'anthropic',
|
|
65
|
-
subagents: { active: ['claude', 'codex'] },
|
|
66
|
-
}, home);
|
|
67
|
-
const r = readPreferences(home);
|
|
68
|
-
if (r.status === 'ok') {
|
|
69
|
-
expect(r.prefs.subagents?.active).toEqual(['claude', 'codex']);
|
|
70
|
-
}
|
|
71
|
-
else {
|
|
72
|
-
throw new Error('expected ok status');
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
//# sourceMappingURL=preferences.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"preferences.test.js","sourceRoot":"","sources":["../../../src/integrations/providers/preferences.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A closed workspace takes no new conversation from the CLI either.
|
|
3
|
-
*
|
|
4
|
-
* The kernel gained a workspace-closed gate and it was applied to the SDK's own
|
|
5
|
-
* ingress paths. `startConversation` calls `createSession` on the store
|
|
6
|
-
* DIRECTLY, and a store deliberately holds no view of workspace status, so the
|
|
7
|
-
* invariant did not reach here.
|
|
8
|
-
*
|
|
9
|
-
* Whether that mattered turned on one question a grep cannot answer: does the
|
|
10
|
-
* CLI ever reach a project it did not just create? It does. `openSessions`
|
|
11
|
-
* reads the project id back out of `.namzu/cli.json` and creates a new project
|
|
12
|
-
* only when the pointer is missing or stale — so every run after the first
|
|
13
|
-
* attaches to a project that already existed and may since have been closed.
|
|
14
|
-
*
|
|
15
|
-
* That is why this test archives a project created by an EARLIER `openSessions`
|
|
16
|
-
* and then reopens the same directory. A test that archived a project it made
|
|
17
|
-
* itself, in one call, would pass against the first-run case the gate can never
|
|
18
|
-
* fire on.
|
|
19
|
-
*/
|
|
20
|
-
export {};
|
|
21
|
-
//# sourceMappingURL=archived-workspace.test.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"archived-workspace.test.d.ts","sourceRoot":"","sources":["../../../../src/integrations/sessions/__tests__/archived-workspace.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG"}
|