qwen-agent-server 0.11.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/README.md +211 -0
- package/dist/backends.js +444 -0
- package/dist/backends.js.map +1 -0
- package/dist/embed.js +92 -0
- package/dist/embed.js.map +1 -0
- package/dist/extensions.js +497 -0
- package/dist/extensions.js.map +1 -0
- package/dist/log.js +21 -0
- package/dist/log.js.map +1 -0
- package/dist/openai-compat.js +147 -0
- package/dist/openai-compat.js.map +1 -0
- package/dist/permissions.js +71 -0
- package/dist/permissions.js.map +1 -0
- package/dist/pool.js +155 -0
- package/dist/pool.js.map +1 -0
- package/dist/rerank.js +93 -0
- package/dist/rerank.js.map +1 -0
- package/dist/server.js +1050 -0
- package/dist/server.js.map +1 -0
- package/dist/session.js +649 -0
- package/dist/session.js.map +1 -0
- package/dist/shutdown.js +68 -0
- package/dist/shutdown.js.map +1 -0
- package/dist/threads.js +218 -0
- package/dist/threads.js.map +1 -0
- package/dist/tokenize.js +90 -0
- package/dist/tokenize.js.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/version.js +13 -0
- package/dist/version.js.map +1 -0
- package/dist/vision.js +293 -0
- package/dist/vision.js.map +1 -0
- package/package.json +42 -0
package/dist/embed.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
//
|
|
3
|
+
// embed.ts — direct-HTTP dispatch for `qwen_embed`.
|
|
4
|
+
//
|
|
5
|
+
// llama-server exposes /v1/embeddings (OpenAI-compat) when started with
|
|
6
|
+
// `--embedding` and an embedding-capable model (e.g. bge-m3,
|
|
7
|
+
// qwen3-embedding-0.6b). The SDK is text-chat only and doesn't surface
|
|
8
|
+
// this endpoint; we POST directly via the shared OpenAI-compat helper.
|
|
9
|
+
import { createLogger } from "./log.js";
|
|
10
|
+
import { dispatchOpenAIPost } from "./openai-compat.js";
|
|
11
|
+
const log = createLogger("qwen-embed");
|
|
12
|
+
const DEFAULT_TIMEOUT_MS = 60_000;
|
|
13
|
+
/**
|
|
14
|
+
* POST to a backend's /v1/embeddings with one-or-many text inputs.
|
|
15
|
+
* Never throws; failures encoded in result.error.
|
|
16
|
+
*/
|
|
17
|
+
export async function dispatchEmbed(backend, texts, opts = {}) {
|
|
18
|
+
const timeout_ms = opts.timeout_ms ?? DEFAULT_TIMEOUT_MS;
|
|
19
|
+
const body = {
|
|
20
|
+
model: backend.model,
|
|
21
|
+
input: texts.length === 1 ? texts[0] : texts,
|
|
22
|
+
};
|
|
23
|
+
if (opts.encoding_format !== undefined) {
|
|
24
|
+
body.encoding_format = opts.encoding_format;
|
|
25
|
+
}
|
|
26
|
+
const outcome = await dispatchOpenAIPost(backend, "/v1/embeddings", body, {
|
|
27
|
+
timeout_ms,
|
|
28
|
+
});
|
|
29
|
+
if (!outcome.ok) {
|
|
30
|
+
if (outcome.status !== undefined) {
|
|
31
|
+
log.warn({
|
|
32
|
+
backend_id: backend.id,
|
|
33
|
+
status: outcome.status,
|
|
34
|
+
body_excerpt: outcome.body_text?.slice(0, 200),
|
|
35
|
+
}, "embed dispatch HTTP failure");
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
ok: false,
|
|
39
|
+
elapsed_ms: outcome.elapsed_ms,
|
|
40
|
+
backend_id: backend.id,
|
|
41
|
+
error: outcome.error,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
let parsed;
|
|
45
|
+
try {
|
|
46
|
+
parsed = JSON.parse(outcome.body_text);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
return {
|
|
50
|
+
ok: false,
|
|
51
|
+
elapsed_ms: outcome.elapsed_ms,
|
|
52
|
+
backend_id: backend.id,
|
|
53
|
+
error: {
|
|
54
|
+
code: "backend_error",
|
|
55
|
+
message: `non-JSON response: ${err.message}`,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
if (!Array.isArray(parsed.data) || parsed.data.length === 0) {
|
|
60
|
+
return {
|
|
61
|
+
ok: false,
|
|
62
|
+
elapsed_ms: outcome.elapsed_ms,
|
|
63
|
+
backend_id: backend.id,
|
|
64
|
+
error: { code: "no_data", message: "backend returned empty data array" },
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
// Reassemble in input order (data[].index can be sparse on some servers).
|
|
68
|
+
const ordered = [...parsed.data].sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
|
|
69
|
+
const embeddings = ordered
|
|
70
|
+
.map((d) => d.embedding)
|
|
71
|
+
.filter((e) => Array.isArray(e));
|
|
72
|
+
if (embeddings.length !== texts.length) {
|
|
73
|
+
return {
|
|
74
|
+
ok: false,
|
|
75
|
+
elapsed_ms: outcome.elapsed_ms,
|
|
76
|
+
backend_id: backend.id,
|
|
77
|
+
error: {
|
|
78
|
+
code: "no_data",
|
|
79
|
+
message: `expected ${texts.length} embeddings, got ${embeddings.length}`,
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
ok: true,
|
|
85
|
+
embeddings,
|
|
86
|
+
...(parsed.usage !== undefined ? { usage: parsed.usage } : {}),
|
|
87
|
+
...(parsed.model !== undefined ? { model: parsed.model } : {}),
|
|
88
|
+
elapsed_ms: outcome.elapsed_ms,
|
|
89
|
+
backend_id: backend.id,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=embed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embed.js","sourceRoot":"","sources":["../src/embed.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
//
|
|
3
|
+
// Per-spawn extension loadout helpers — RDR-002.
|
|
4
|
+
//
|
|
5
|
+
// This module exposes the supervisor-side bridge between Claude's
|
|
6
|
+
// orchestrator and the Qwen Code CLI's extensions surface:
|
|
7
|
+
//
|
|
8
|
+
// resolveQwenRealBin(env, whichFn?) — resolve the real qwen binary
|
|
9
|
+
// path the wrapper script will exec. Called once at supervisor
|
|
10
|
+
// startup; result is cached on the handlers/pool context and
|
|
11
|
+
// forwarded to every session via QueryOptions.env.QWEN_REAL_BIN.
|
|
12
|
+
//
|
|
13
|
+
// resolveWrapperPath() — absolute path to the bash wrapper shipped
|
|
14
|
+
// in this package at scripts/qwen-extensions-wrapper.sh. The
|
|
15
|
+
// wrapper is a fixed file; per-session variation is via env vars
|
|
16
|
+
// (QWEN_REAL_BIN, QWEN_AGENT_EXTENSIONS).
|
|
17
|
+
//
|
|
18
|
+
// parseInstalledExtensions(stdout) — pure parser for `qwen
|
|
19
|
+
// extensions list` output. Returns the list of installed names
|
|
20
|
+
// (lowercased) or [] on empty / unparseable input. Never throws.
|
|
21
|
+
//
|
|
22
|
+
// createInstalledExtensionsCache(qwenRealBin, execFn?) — async
|
|
23
|
+
// factory returning a cache object with get/reload/size methods.
|
|
24
|
+
// Initial population shells out to `<qwenRealBin> extensions list`;
|
|
25
|
+
// execFn is injected for testability.
|
|
26
|
+
//
|
|
27
|
+
// Subsequent phases will add the resolveExtensions(opts, sessionDefault,
|
|
28
|
+
// installedCache) algorithm and the qwen_spawn handler integration.
|
|
29
|
+
import { execFile, execFileSync } from "node:child_process";
|
|
30
|
+
import { statSync } from "node:fs";
|
|
31
|
+
import { dirname, resolve } from "node:path";
|
|
32
|
+
import { fileURLToPath } from "node:url";
|
|
33
|
+
import { createLogger } from "./log.js";
|
|
34
|
+
import { readConfigDefaultExtensions } from "./backends.js";
|
|
35
|
+
const log = createLogger("qwen-extensions");
|
|
36
|
+
/**
|
|
37
|
+
* Default `which` implementation used when a caller doesn't inject one.
|
|
38
|
+
* Returns the resolved absolute path or null if the command is not on
|
|
39
|
+
* PATH. Never throws.
|
|
40
|
+
*/
|
|
41
|
+
function defaultWhich(cmd) {
|
|
42
|
+
try {
|
|
43
|
+
const out = execFileSync("/usr/bin/env", ["which", cmd], {
|
|
44
|
+
encoding: "utf8",
|
|
45
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
46
|
+
}).trim();
|
|
47
|
+
return out === "" ? null : out;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Resolve the real qwen binary path the wrapper script will `exec`.
|
|
55
|
+
*
|
|
56
|
+
* Policy (RDR-002 §The wrapper-script bridge → QWEN_REAL_BIN bullet):
|
|
57
|
+
*
|
|
58
|
+
* 1. If `env.QWEN_REAL_BIN` is set and non-empty, honour it verbatim.
|
|
59
|
+
* Verify the path exists and has any executable bit set; throw
|
|
60
|
+
* with a descriptive message on miss. The supervisor exits
|
|
61
|
+
* non-zero at startup rather than failing at first spawn.
|
|
62
|
+
* 2. Else, run `which qwen`. If empty/null, throw — the supervisor
|
|
63
|
+
* cannot start without a resolvable qwen binary.
|
|
64
|
+
*
|
|
65
|
+
* The `whichFn` parameter is injected for testability; production code
|
|
66
|
+
* leaves it undefined and `defaultWhich` is used.
|
|
67
|
+
*/
|
|
68
|
+
export function resolveQwenRealBin(env, whichFn) {
|
|
69
|
+
const override = env["QWEN_REAL_BIN"];
|
|
70
|
+
if (override !== undefined && override !== "") {
|
|
71
|
+
let mode;
|
|
72
|
+
try {
|
|
73
|
+
const stat = statSync(override);
|
|
74
|
+
if (!stat.isFile()) {
|
|
75
|
+
throw new Error(`QWEN_REAL_BIN=${override} is not a regular file`);
|
|
76
|
+
}
|
|
77
|
+
mode = stat.mode;
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
// Re-throw our own descriptive errors; wrap fs errors with the path.
|
|
81
|
+
if (err instanceof Error && err.message.startsWith("QWEN_REAL_BIN=")) {
|
|
82
|
+
throw err;
|
|
83
|
+
}
|
|
84
|
+
throw new Error(`QWEN_REAL_BIN=${override} does not exist or is not accessible`);
|
|
85
|
+
}
|
|
86
|
+
if ((mode & 0o111) === 0) {
|
|
87
|
+
throw new Error(`QWEN_REAL_BIN=${override} exists but is not executable (mode bits 0o111 unset)`);
|
|
88
|
+
}
|
|
89
|
+
return override;
|
|
90
|
+
}
|
|
91
|
+
const which = whichFn ?? defaultWhich;
|
|
92
|
+
const found = which("qwen");
|
|
93
|
+
if (found === null || found === "") {
|
|
94
|
+
throw new Error("QWEN_REAL_BIN unset and 'qwen' not on PATH — install Qwen Code or set QWEN_REAL_BIN");
|
|
95
|
+
}
|
|
96
|
+
return found;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Absolute path to the wrapper script shipped at
|
|
100
|
+
* `mcp-bridges/qwen-agent-server/scripts/qwen-extensions-wrapper.sh`.
|
|
101
|
+
*
|
|
102
|
+
* Resolution is anchored on `import.meta.url` so the same code works
|
|
103
|
+
* whether the module loads from `src/` (during tests) or from `dist/`
|
|
104
|
+
* (after `tsc` build) — both sit one level below the package root.
|
|
105
|
+
*/
|
|
106
|
+
export function resolveWrapperPath() {
|
|
107
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
108
|
+
return resolve(here, "..", "scripts", "qwen-extensions-wrapper.sh");
|
|
109
|
+
}
|
|
110
|
+
// ─────────────────────────────────────────────────────────────────
|
|
111
|
+
// Installed-extensions cache
|
|
112
|
+
/**
|
|
113
|
+
* Strip ANSI SGR escape sequences (chalk emits these around status
|
|
114
|
+
* glyphs) so the parser can match plain-text content.
|
|
115
|
+
*/
|
|
116
|
+
const ANSI_RE = /\x1b\[[0-9;]*m/g;
|
|
117
|
+
/**
|
|
118
|
+
* First-line header of an extension block emitted by
|
|
119
|
+
* `extensionToOutputString` (cli.js:456690):
|
|
120
|
+
*
|
|
121
|
+
* <glyph> <name> (<version>)
|
|
122
|
+
*
|
|
123
|
+
* where `<glyph>` is `✓` (U+2713) or `✗` (U+2717) and `<name>` is the
|
|
124
|
+
* `config.name` field of the extension manifest. The glyph is REQUIRED:
|
|
125
|
+
* `extensionToOutputString` only emits a leading-space-only line in
|
|
126
|
+
* `inline2 = true` mode, which `handleList` (cli.js:456770) does not
|
|
127
|
+
* pass. Requiring the glyph narrows the regex so unrelated lines that
|
|
128
|
+
* happen to end with `(something)` cannot accidentally register as
|
|
129
|
+
* extension names if a future block-separator change causes the
|
|
130
|
+
* `\n{2,}` split to miss boundaries.
|
|
131
|
+
*
|
|
132
|
+
* The version sub-pattern `[^()]+` deliberately rejects nested parens,
|
|
133
|
+
* which keeps the second-line ` Source: ... (Type: ...)` from
|
|
134
|
+
* accidentally matching when block boundaries don't separate cleanly.
|
|
135
|
+
*/
|
|
136
|
+
const HEADER_RE = /^\s*[✓✗]\s+(.+?)\s+\([^()]+\)\s*$/;
|
|
137
|
+
/**
|
|
138
|
+
* Parse `qwen extensions list` stdout and return the lowercased
|
|
139
|
+
* `config.name` of each installed extension.
|
|
140
|
+
*
|
|
141
|
+
* Fail-soft per RDR-002 audit-note #4: empty input, the
|
|
142
|
+
* "No extensions installed." sentinel, and unrecognized output all
|
|
143
|
+
* yield `[]` rather than throwing — an upstream output-format change
|
|
144
|
+
* degrades gracefully (cache populates empty; future spawns reject
|
|
145
|
+
* unknown names) instead of bricking the supervisor.
|
|
146
|
+
*/
|
|
147
|
+
export function parseInstalledExtensions(stdout) {
|
|
148
|
+
return parseInstalledExtensionsRich(stdout).map((e) => e.name);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Parse `qwen extensions list` stdout into structured per-extension
|
|
152
|
+
* records. Mirrors `extensionToOutputString` in cli.js:456690 — each
|
|
153
|
+
* block is joined by `\n\n` and starts with `<glyph> <name> (<version>)`.
|
|
154
|
+
*
|
|
155
|
+
* Fail-soft: on empty / sentinel / unparseable input, returns `[]`.
|
|
156
|
+
* Individual fields that don't match expected line patterns are simply
|
|
157
|
+
* omitted from the record; we never throw.
|
|
158
|
+
*/
|
|
159
|
+
export function parseInstalledExtensionsRich(stdout) {
|
|
160
|
+
if (typeof stdout !== "string")
|
|
161
|
+
return [];
|
|
162
|
+
const cleaned = stdout.replace(ANSI_RE, "");
|
|
163
|
+
if (cleaned.trim() === "")
|
|
164
|
+
return [];
|
|
165
|
+
if (/no extensions installed/i.test(cleaned))
|
|
166
|
+
return [];
|
|
167
|
+
const blocks = cleaned.split(/\n{2,}/);
|
|
168
|
+
const out = [];
|
|
169
|
+
for (const block of blocks) {
|
|
170
|
+
const lines = block.split("\n");
|
|
171
|
+
const firstLine = lines[0]?.trim() ?? "";
|
|
172
|
+
if (firstLine === "")
|
|
173
|
+
continue;
|
|
174
|
+
// Header match: glyph + name + (version)
|
|
175
|
+
const headerMatch = /^\s*([✓✗])\s+(.+?)\s+\(([^()]+)\)\s*$/.exec(firstLine);
|
|
176
|
+
if (!headerMatch)
|
|
177
|
+
continue;
|
|
178
|
+
const glyph = headerMatch[1];
|
|
179
|
+
const name = headerMatch[2]?.trim();
|
|
180
|
+
const version = headerMatch[3]?.trim();
|
|
181
|
+
if (!name)
|
|
182
|
+
continue;
|
|
183
|
+
const info = { name: name.toLowerCase() };
|
|
184
|
+
if (version)
|
|
185
|
+
info.version = version;
|
|
186
|
+
info.enabled_workspace = glyph === "✓";
|
|
187
|
+
// Field lines and list-section accumulation. Format reference:
|
|
188
|
+
// ` Path: <path>`
|
|
189
|
+
// ` Source: <source> (Type: <type>)` [optional]
|
|
190
|
+
// ` Enabled (User): <bool>`
|
|
191
|
+
// ` Enabled (Workspace): <bool>`
|
|
192
|
+
// ` Context files:` then ` <file>` lines
|
|
193
|
+
// ` Commands:` then ` /<cmd>` lines
|
|
194
|
+
// ` Skills:` then ` <skill>` lines
|
|
195
|
+
// ` Agents:` then ` <agent>` lines
|
|
196
|
+
// ` MCP servers:` then ` <name>` lines
|
|
197
|
+
let currentList = null;
|
|
198
|
+
for (let i = 1; i < lines.length; i++) {
|
|
199
|
+
const line = lines[i];
|
|
200
|
+
if (line === undefined)
|
|
201
|
+
continue;
|
|
202
|
+
const trimmed = line.trim();
|
|
203
|
+
if (trimmed === "")
|
|
204
|
+
continue;
|
|
205
|
+
// List-item lines start with two spaces of indent; field lines start with one.
|
|
206
|
+
const isListItem = /^ {2,}\S/.test(line) && !/^\s*\w[\w\s]*?:/.test(trimmed);
|
|
207
|
+
if (isListItem && currentList !== null) {
|
|
208
|
+
// Strip the leading slash for commands ("/foo" → "foo") to match
|
|
209
|
+
// how the supervisor's resolveExtensions expects them.
|
|
210
|
+
const item = trimmed.replace(/^\//, "");
|
|
211
|
+
if (item)
|
|
212
|
+
currentList.push(item);
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
currentList = null;
|
|
216
|
+
const fieldMatch = /^\s*([\w\s()]+?):\s*(.*)$/.exec(line);
|
|
217
|
+
if (!fieldMatch)
|
|
218
|
+
continue;
|
|
219
|
+
const key = (fieldMatch[1] ?? "").trim().toLowerCase();
|
|
220
|
+
const val = (fieldMatch[2] ?? "").trim();
|
|
221
|
+
if (key === "path") {
|
|
222
|
+
if (val)
|
|
223
|
+
info.path = val;
|
|
224
|
+
}
|
|
225
|
+
else if (key === "source") {
|
|
226
|
+
// Strip trailing "(Type: ...)" suffix — source is just the identifier.
|
|
227
|
+
if (val)
|
|
228
|
+
info.source = val.replace(/\s*\(Type:\s*[^)]*\)\s*$/, "");
|
|
229
|
+
}
|
|
230
|
+
else if (key === "enabled (user)") {
|
|
231
|
+
info.enabled_user = /^true$/i.test(val);
|
|
232
|
+
}
|
|
233
|
+
else if (key === "enabled (workspace)") {
|
|
234
|
+
info.enabled_workspace = /^true$/i.test(val);
|
|
235
|
+
}
|
|
236
|
+
else if (key === "context files") {
|
|
237
|
+
info.context_files = [];
|
|
238
|
+
currentList = info.context_files;
|
|
239
|
+
}
|
|
240
|
+
else if (key === "commands") {
|
|
241
|
+
info.commands = [];
|
|
242
|
+
currentList = info.commands;
|
|
243
|
+
}
|
|
244
|
+
else if (key === "skills") {
|
|
245
|
+
info.skills = [];
|
|
246
|
+
currentList = info.skills;
|
|
247
|
+
}
|
|
248
|
+
else if (key === "agents") {
|
|
249
|
+
info.agents = [];
|
|
250
|
+
currentList = info.agents;
|
|
251
|
+
}
|
|
252
|
+
else if (key === "mcp servers") {
|
|
253
|
+
info.mcp_servers = [];
|
|
254
|
+
currentList = info.mcp_servers;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
// Drop empty list arrays so JSON stays compact.
|
|
258
|
+
for (const k of ["context_files", "commands", "skills", "agents", "mcp_servers"]) {
|
|
259
|
+
if (info[k] && info[k]?.length === 0)
|
|
260
|
+
delete info[k];
|
|
261
|
+
}
|
|
262
|
+
out.push(info);
|
|
263
|
+
}
|
|
264
|
+
return out;
|
|
265
|
+
}
|
|
266
|
+
export const defaultExecExtensionsList = (qwenRealBin) => new Promise((res, rej) => {
|
|
267
|
+
execFile(qwenRealBin, ["extensions", "list"], { encoding: "utf8" }, (err, stdout) => {
|
|
268
|
+
if (err) {
|
|
269
|
+
rej(err);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
res(stdout);
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
/**
|
|
276
|
+
* Shell out to `<qwenRealBin> extensions list`, parse the rich form, and
|
|
277
|
+
* return the structured per-extension records. Throws on exec failure
|
|
278
|
+
* (qwen binary missing, etc.). Returns `[]` if output is empty or
|
|
279
|
+
* unparseable — same fail-soft contract as the bare-name parser.
|
|
280
|
+
*
|
|
281
|
+
* Used by the `qwen_extensions` MCP tool to give callers the full
|
|
282
|
+
* installed-extensions inventory (versions, paths, source, declared
|
|
283
|
+
* commands/skills/agents/MCP servers) without going through the
|
|
284
|
+
* cache (which only retains names).
|
|
285
|
+
*/
|
|
286
|
+
export async function listInstalledExtensions(qwenRealBin, execFn = defaultExecExtensionsList) {
|
|
287
|
+
const stdout = await execFn(qwenRealBin);
|
|
288
|
+
return parseInstalledExtensionsRich(stdout);
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Construct an `InstalledExtensionsCache` and prime it once.
|
|
292
|
+
*
|
|
293
|
+
* - Exec errors propagate (fail-fast at startup) — the supervisor
|
|
294
|
+
* should not start if the qwen binary cannot be invoked.
|
|
295
|
+
* - Output that is non-empty but unparseable is treated as an empty
|
|
296
|
+
* set; a structured-log warning records the first 200 chars of the
|
|
297
|
+
* output so an operator can diagnose without a crash.
|
|
298
|
+
*/
|
|
299
|
+
export async function createInstalledExtensionsCache(qwenRealBin, execFn) {
|
|
300
|
+
const exec = execFn ?? defaultExecExtensionsList;
|
|
301
|
+
let names = new Set();
|
|
302
|
+
async function loadOnce() {
|
|
303
|
+
const stdout = await exec(qwenRealBin);
|
|
304
|
+
const parsed = parseInstalledExtensions(stdout);
|
|
305
|
+
if (parsed.length === 0 &&
|
|
306
|
+
stdout.trim() !== "" &&
|
|
307
|
+
!/no extensions installed/i.test(stdout)) {
|
|
308
|
+
log.warn({ stdout_preview: stdout.slice(0, 200) }, "qwen extensions list output did not match expected format; cache populated empty");
|
|
309
|
+
}
|
|
310
|
+
return new Set(parsed);
|
|
311
|
+
}
|
|
312
|
+
names = await loadOnce();
|
|
313
|
+
return {
|
|
314
|
+
get: () => names,
|
|
315
|
+
reload: async () => {
|
|
316
|
+
names = await loadOnce();
|
|
317
|
+
return names;
|
|
318
|
+
},
|
|
319
|
+
size: () => names.size,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Thrown by `resolveExtensions` when the resolved set contains a name
|
|
324
|
+
* the supervisor's installed-extensions cache does not know, or when
|
|
325
|
+
* the caller asked for enable/disable without a session-default base
|
|
326
|
+
* to mutate. Caught by the qwen_spawn handler and translated into a
|
|
327
|
+
* `{ error: { code: 'spawn_error', message } }` envelope.
|
|
328
|
+
*/
|
|
329
|
+
export class ExtensionResolutionError extends Error {
|
|
330
|
+
unknown;
|
|
331
|
+
constructor(message, unknown = []) {
|
|
332
|
+
super(message);
|
|
333
|
+
this.name = "ExtensionResolutionError";
|
|
334
|
+
this.unknown = unknown;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Read the supervisor's session-default extension set.
|
|
339
|
+
*
|
|
340
|
+
* Resolution priority (highest first):
|
|
341
|
+
* 1. `QWEN_DEFAULT_EXTENSIONS` env var (back-compat / one-shot override)
|
|
342
|
+
* 2. `default_extensions` field in `~/.qwen-coprocessor-stack/config.json`
|
|
343
|
+
* 3. "leave-defaults" sentinel — wrapper drops --extensions; CLI defaults
|
|
344
|
+
* (all enabled per extension-enablement.json) apply
|
|
345
|
+
*
|
|
346
|
+
* The config-file source is mtime-cached at the `readConfig()` layer in
|
|
347
|
+
* backends.ts, so re-invocation on every spawn is cheap.
|
|
348
|
+
*/
|
|
349
|
+
export function getSessionDefaultExtensions(env) {
|
|
350
|
+
// 1. env override
|
|
351
|
+
const raw = env["QWEN_DEFAULT_EXTENSIONS"];
|
|
352
|
+
if (raw !== undefined && raw !== "") {
|
|
353
|
+
return dedupeLower(raw.split(",").map((s) => s.trim()).filter((s) => s !== ""));
|
|
354
|
+
}
|
|
355
|
+
// 2. config file
|
|
356
|
+
const fromFile = readConfigDefaultExtensions();
|
|
357
|
+
if (fromFile && fromFile.length > 0) {
|
|
358
|
+
return dedupeLower(fromFile);
|
|
359
|
+
}
|
|
360
|
+
// 3. unset → CLI defaults apply
|
|
361
|
+
return "leave-defaults";
|
|
362
|
+
}
|
|
363
|
+
function dedupeLower(input) {
|
|
364
|
+
const seen = new Set();
|
|
365
|
+
const out = [];
|
|
366
|
+
for (const name of input) {
|
|
367
|
+
const lower = name.toLowerCase();
|
|
368
|
+
if (lower === "" || seen.has(lower))
|
|
369
|
+
continue;
|
|
370
|
+
seen.add(lower);
|
|
371
|
+
out.push(lower);
|
|
372
|
+
}
|
|
373
|
+
return out;
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Framework-required extension set — RDR-002 §Framework-required
|
|
377
|
+
* extensions. Empty today: the supervisor's contract with the inner
|
|
378
|
+
* Qwen (write-authority gating, ask_user_question exclusion,
|
|
379
|
+
* system-prompt preamble, multi-turn streamInput) is enforced via
|
|
380
|
+
* QueryOptions and does not require any extension to be loaded. Adding
|
|
381
|
+
* even one would change the supervisor's contract and must be
|
|
382
|
+
* RDR-tracked.
|
|
383
|
+
*
|
|
384
|
+
* If this set ever becomes non-empty: names here are supervisor-
|
|
385
|
+
* controlled and must be validated against the installed-extensions
|
|
386
|
+
* cache once at supervisor startup; the per-spawn `resolveExtensions`
|
|
387
|
+
* step-7 union does NOT re-validate framework-required names (step 7
|
|
388
|
+
* runs after step 6 in the RDR algorithm).
|
|
389
|
+
*/
|
|
390
|
+
const FRAMEWORK_REQUIRED_EXTENSIONS = [];
|
|
391
|
+
/**
|
|
392
|
+
* Step 7 — union with framework-required. Adds any framework-required
|
|
393
|
+
* names not already in the resolved set, lowercased and dedup-safe.
|
|
394
|
+
* Idempotent / no-op today because the framework-required set is empty.
|
|
395
|
+
*
|
|
396
|
+
* Exported for direct testability of the non-empty path: an inline
|
|
397
|
+
* call with a non-empty `frameworkRequired` argument exercises the
|
|
398
|
+
* union logic that would otherwise be unreachable from
|
|
399
|
+
* `resolveExtensions` while `FRAMEWORK_REQUIRED_EXTENSIONS` is empty.
|
|
400
|
+
*/
|
|
401
|
+
export function unionFrameworkRequired(base, frameworkRequired = FRAMEWORK_REQUIRED_EXTENSIONS) {
|
|
402
|
+
if (frameworkRequired.length === 0)
|
|
403
|
+
return base;
|
|
404
|
+
const seen = new Set(base);
|
|
405
|
+
const out = [...base];
|
|
406
|
+
for (const name of frameworkRequired) {
|
|
407
|
+
const lower = name.toLowerCase();
|
|
408
|
+
if (!seen.has(lower)) {
|
|
409
|
+
out.push(lower);
|
|
410
|
+
seen.add(lower);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return out;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Resolve the active extension set for a single qwen_spawn call,
|
|
417
|
+
* implementing steps 1–9 of RDR-002 §Resolution-algorithm.
|
|
418
|
+
*
|
|
419
|
+
* Steps (verbatim from the RDR):
|
|
420
|
+
* 1. Determine session-default — caller passes it in.
|
|
421
|
+
* 2. Compute base — `only` wins exact-set semantics; otherwise
|
|
422
|
+
* session-default is the base.
|
|
423
|
+
* 3. Apply `enable` additively.
|
|
424
|
+
* 4. Apply `disable` subtractively (disable wins on overlap).
|
|
425
|
+
* 5. enable / disable independent.
|
|
426
|
+
* 6. Validate against installedCache; throw ExtensionResolutionError
|
|
427
|
+
* with the unknown names if any.
|
|
428
|
+
* 7. Union with framework-required (today: empty).
|
|
429
|
+
* 8. Render — non-empty → comma-list; explicit empty → "none".
|
|
430
|
+
* 9. "leave-defaults" → null envValue.
|
|
431
|
+
*/
|
|
432
|
+
export function resolveExtensions(opts, sessionDefault, installedCache) {
|
|
433
|
+
const onlyProvided = opts?.only !== undefined;
|
|
434
|
+
const enableProvided = opts?.enable !== undefined && opts.enable.length > 0;
|
|
435
|
+
const disableProvided = opts?.disable !== undefined && opts.disable.length > 0;
|
|
436
|
+
// Step 2: compute base.
|
|
437
|
+
// 2a: only wins (enable/disable IGNORED).
|
|
438
|
+
// 2b: else base is session-default with enable/disable applied.
|
|
439
|
+
let base;
|
|
440
|
+
if (onlyProvided) {
|
|
441
|
+
base = dedupeLower(opts.only);
|
|
442
|
+
}
|
|
443
|
+
else if (sessionDefault === "leave-defaults") {
|
|
444
|
+
if (enableProvided || disableProvided) {
|
|
445
|
+
// Cannot compute a deterministic resolved set without enumerating
|
|
446
|
+
// the implicit CLI-defaults set; reject so the caller gets a
|
|
447
|
+
// visible error rather than silent surprise.
|
|
448
|
+
throw new ExtensionResolutionError("cannot apply opts.extensions.enable/disable when QWEN_DEFAULT_EXTENSIONS is unset; " +
|
|
449
|
+
"set a session default or use opts.extensions.only to specify the exact set");
|
|
450
|
+
}
|
|
451
|
+
// Step 9: no mutations and no base — leave-defaults short-circuits
|
|
452
|
+
// before validation/union (no resolved set to validate or union into).
|
|
453
|
+
return { envValue: null, resolved: "leave-defaults" };
|
|
454
|
+
}
|
|
455
|
+
else {
|
|
456
|
+
// Session-default is a concrete list. Apply enable additively, then
|
|
457
|
+
// disable subtractively (steps 3–5).
|
|
458
|
+
base = dedupeLower(sessionDefault);
|
|
459
|
+
if (enableProvided) {
|
|
460
|
+
const additions = dedupeLower(opts.enable);
|
|
461
|
+
const seen = new Set(base);
|
|
462
|
+
for (const name of additions) {
|
|
463
|
+
if (!seen.has(name)) {
|
|
464
|
+
base.push(name);
|
|
465
|
+
seen.add(name);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
if (disableProvided) {
|
|
470
|
+
const removals = new Set(dedupeLower(opts.disable));
|
|
471
|
+
base = base.filter((name) => !removals.has(name));
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
// Step 6: validate caller-supplied names against the installed cache.
|
|
475
|
+
validateInstalled(base, installedCache);
|
|
476
|
+
// Step 7: union with framework-required. Names here are supervisor-
|
|
477
|
+
// controlled and pre-validated at startup; not re-validated per spawn.
|
|
478
|
+
base = unionFrameworkRequired(base);
|
|
479
|
+
// Step 8: render. An empty base reached by subtraction or an explicit
|
|
480
|
+
// only=[] renders as "none". The only path that produces leave-defaults
|
|
481
|
+
// is the no-op branch above.
|
|
482
|
+
if (base.length === 0) {
|
|
483
|
+
return { envValue: "none", resolved: "none" };
|
|
484
|
+
}
|
|
485
|
+
return { envValue: base.join(","), resolved: base };
|
|
486
|
+
}
|
|
487
|
+
function validateInstalled(names, installed) {
|
|
488
|
+
const unknown = [];
|
|
489
|
+
for (const name of names) {
|
|
490
|
+
if (!installed.has(name))
|
|
491
|
+
unknown.push(name);
|
|
492
|
+
}
|
|
493
|
+
if (unknown.length > 0) {
|
|
494
|
+
throw new ExtensionResolutionError(`unknown extension(s): ${unknown.join(", ")}`, unknown);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
//# sourceMappingURL=extensions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extensions.js","sourceRoot":"","sources":["../src/extensions.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,EAAE;AACF,iDAAiD;AACjD,EAAE;AACF,kEAAkE;AAClE,2DAA2D;AAC3D,EAAE;AACF,sEAAsE;AACtE,mEAAmE;AACnE,iEAAiE;AACjE,qEAAqE;AACrE,EAAE;AACF,sEAAsE;AACtE,iEAAiE;AACjE,qEAAqE;AACrE,8CAA8C;AAC9C,EAAE;AACF,8DAA8D;AAC9D,mEAAmE;AACnE,qEAAqE;AACrE,EAAE;AACF,kEAAkE;AAClE,qEAAqE;AACrE,wEAAwE;AACxE,0CAA0C;AAC1C,EAAE;AACF,yEAAyE;AACzE,oEAAoE;AAEpE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAExC,OAAO,EAAE,2BAA2B,EAAE,MAAM,eAAe,CAAC;AAE5D,MAAM,GAAG,GAAG,YAAY,CAAC,iBAAiB,CAAC,CAAC;AAE5C;;;;GAIG;AACH,SAAS,YAAY,CAAC,GAAW;IAC/B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,cAAc,EAAE,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE;YACvD,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;SACpC,CAAC,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,kBAAkB,CAChC,GAAsB,EACtB,OAAwC;IAExC,MAAM,QAAQ,GAAG,GAAG,CAAC,eAAe,CAAC,CAAC;IACtC,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,EAAE,EAAE,CAAC;QAC9C,IAAI,IAAY,CAAC;QACjB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAChC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;gBACnB,MAAM,IAAI,KAAK,CACb,iBAAiB,QAAQ,wBAAwB,CAClD,CAAC;YACJ,CAAC;YACD,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACnB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,qEAAqE;YACrE,IAAI,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;gBACrE,MAAM,GAAG,CAAC;YACZ,CAAC;YACD,MAAM,IAAI,KAAK,CACb,iBAAiB,QAAQ,sCAAsC,CAChE,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CACb,iBAAiB,QAAQ,uDAAuD,CACjF,CAAC;QACJ,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,IAAI,YAAY,CAAC;IACtC,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;IAC5B,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CACb,qFAAqF,CACtF,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACrD,OAAO,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,4BAA4B,CAAC,CAAC;AACtE,CAAC;AAED,oEAAoE;AACpE,6BAA6B;AAE7B;;;GAGG;AACH,MAAM,OAAO,GAAG,iBAAiB,CAAC;AAElC;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,SAAS,GAAG,mCAAmC,CAAC;AAEtD;;;;;;;;;GASG;AACH,MAAM,UAAU,wBAAwB,CAAC,MAAc;IACrD,OAAO,4BAA4B,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AACjE,CAAC;AAwBD;;;;;;;;GAQG;AACH,MAAM,UAAU,4BAA4B,CAAC,MAAc;IACzD,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IAC1C,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC5C,IAAI,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,EAAE,CAAC;IACrC,IAAI,0BAA0B,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,CAAC;IAExD,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACvC,MAAM,GAAG,GAAoB,EAAE,CAAC;IAChC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAChC,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QACzC,IAAI,SAAS,KAAK,EAAE;YAAE,SAAS;QAE/B,yCAAyC;QACzC,MAAM,WAAW,GAAG,uCAAuC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC5E,IAAI,CAAC,WAAW;YAAE,SAAS;QAC3B,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;QAC7B,MAAM,IAAI,GAAG,WAAW,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;QACpC,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;QACvC,IAAI,CAAC,IAAI;YAAE,SAAS;QAEpB,MAAM,IAAI,GAAkB,EAAE,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;QACzD,IAAI,OAAO;YAAE,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACpC,IAAI,CAAC,iBAAiB,GAAG,KAAK,KAAK,GAAG,CAAC;QAEvC,+DAA+D;QAC/D,oBAAoB;QACpB,kDAAkD;QAClD,8BAA8B;QAC9B,mCAAmC;QACnC,4CAA4C;QAC5C,uCAAuC;QACvC,sCAAsC;QACtC,sCAAsC;QACtC,0CAA0C;QAC1C,IAAI,WAAW,GAAoB,IAAI,CAAC;QACxC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACtB,IAAI,IAAI,KAAK,SAAS;gBAAE,SAAS;YACjC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YAC5B,IAAI,OAAO,KAAK,EAAE;gBAAE,SAAS;YAE7B,+EAA+E;YAC/E,MAAM,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC7E,IAAI,UAAU,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;gBACvC,iEAAiE;gBACjE,uDAAuD;gBACvD,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;gBACxC,IAAI,IAAI;oBAAE,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACjC,SAAS;YACX,CAAC;YAED,WAAW,GAAG,IAAI,CAAC;YAEnB,MAAM,UAAU,GAAG,2BAA2B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1D,IAAI,CAAC,UAAU;gBAAE,SAAS;YAC1B,MAAM,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YACvD,MAAM,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;YAEzC,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;gBACnB,IAAI,GAAG;oBAAE,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC;YAC3B,CAAC;iBAAM,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;gBAC5B,uEAAuE;gBACvE,IAAI,GAAG;oBAAE,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,0BAA0B,EAAE,EAAE,CAAC,CAAC;YACrE,CAAC;iBAAM,IAAI,GAAG,KAAK,gBAAgB,EAAE,CAAC;gBACpC,IAAI,CAAC,YAAY,GAAG,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC1C,CAAC;iBAAM,IAAI,GAAG,KAAK,qBAAqB,EAAE,CAAC;gBACzC,IAAI,CAAC,iBAAiB,GAAG,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC/C,CAAC;iBAAM,IAAI,GAAG,KAAK,eAAe,EAAE,CAAC;gBACnC,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;gBACxB,WAAW,GAAG,IAAI,CAAC,aAAa,CAAC;YACnC,CAAC;iBAAM,IAAI,GAAG,KAAK,UAAU,EAAE,CAAC;gBAC9B,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;gBACnB,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC;YAC9B,CAAC;iBAAM,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;gBAC5B,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;gBACjB,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC;YAC5B,CAAC;iBAAM,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;gBAC5B,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;gBACjB,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC;YAC5B,CAAC;iBAAM,IAAI,GAAG,KAAK,aAAa,EAAE,CAAC;gBACjC,IAAI,CAAC,WAAW,GAAG,EAAE,CAAC;gBACtB,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;YACjC,CAAC;QACH,CAAC;QAED,gDAAgD;QAChD,KAAK,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa,CAAU,EAAE,CAAC;YAC1F,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC;QACvD,CAAC;QAED,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AASD,MAAM,CAAC,MAAM,yBAAyB,GAAyB,CAAC,WAAW,EAAE,EAAE,CAC7E,IAAI,OAAO,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;IACvB,QAAQ,CACN,WAAW,EACX,CAAC,YAAY,EAAE,MAAM,CAAC,EACtB,EAAE,QAAQ,EAAE,MAAM,EAAE,EACpB,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE;QACd,IAAI,GAAG,EAAE,CAAC;YACR,GAAG,CAAC,GAAG,CAAC,CAAC;YACT,OAAO;QACT,CAAC;QACD,GAAG,CAAC,MAAM,CAAC,CAAC;IACd,CAAC,CACF,CAAC;AACJ,CAAC,CAAC,CAAC;AAEL;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,WAAmB,EACnB,SAA+B,yBAAyB;IAExD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;IACzC,OAAO,4BAA4B,CAAC,MAAM,CAAC,CAAC;AAC9C,CAAC;AAsBD;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAClD,WAAmB,EACnB,MAA6B;IAE7B,MAAM,IAAI,GAAG,MAAM,IAAI,yBAAyB,CAAC;IACjD,IAAI,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAE9B,KAAK,UAAU,QAAQ;QACrB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC;QACvC,MAAM,MAAM,GAAG,wBAAwB,CAAC,MAAM,CAAC,CAAC;QAChD,IACE,MAAM,CAAC,MAAM,KAAK,CAAC;YACnB,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE;YACpB,CAAC,0BAA0B,CAAC,IAAI,CAAC,MAAM,CAAC,EACxC,CAAC;YACD,GAAG,CAAC,IAAI,CACN,EAAE,cAAc,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EACxC,kFAAkF,CACnF,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;IACzB,CAAC;IAED,KAAK,GAAG,MAAM,QAAQ,EAAE,CAAC;IAEzB,OAAO;QACL,GAAG,EAAE,GAAG,EAAE,CAAC,KAAK;QAChB,MAAM,EAAE,KAAK,IAAI,EAAE;YACjB,KAAK,GAAG,MAAM,QAAQ,EAAE,CAAC;YACzB,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI;KACvB,CAAC;AACJ,CAAC;AAgCD;;;;;;GAMG;AACH,MAAM,OAAO,wBAAyB,SAAQ,KAAK;IACxC,OAAO,CAAW;IAC3B,YAAY,OAAe,EAAE,UAAoB,EAAE;QACjD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;QACvC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;CACF;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,2BAA2B,CACzC,GAAsB;IAEtB,kBAAkB;IAClB,MAAM,GAAG,GAAG,GAAG,CAAC,yBAAyB,CAAC,CAAC;IAC3C,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,EAAE,EAAE,CAAC;QACpC,OAAO,WAAW,CAChB,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAC5D,CAAC;IACJ,CAAC;IAED,iBAAiB;IACjB,MAAM,QAAQ,GAAG,2BAA2B,EAAE,CAAC;IAC/C,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpC,OAAO,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC;IAED,gCAAgC;IAChC,OAAO,gBAAgB,CAAC;AAC1B,CAAC;AAED,SAAS,WAAW,CAAC,KAAe;IAClC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QACjC,IAAI,KAAK,KAAK,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,SAAS;QAC9C,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAChB,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,6BAA6B,GAAsB,EAAE,CAAC;AAE5D;;;;;;;;;GASG;AACH,MAAM,UAAU,sBAAsB,CACpC,IAAc,EACd,oBAAuC,6BAA6B;IAEpE,IAAI,iBAAiB,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAChD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC;IAC3B,MAAM,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;IACtB,KAAK,MAAM,IAAI,IAAI,iBAAiB,EAAE,CAAC;QACrC,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QACjC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACrB,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAChB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,iBAAiB,CAC/B,IAA+B,EAC/B,cAA2C,EAC3C,cAA2B;IAE3B,MAAM,YAAY,GAAG,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC;IAC9C,MAAM,cAAc,GAAG,IAAI,EAAE,MAAM,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IAC5E,MAAM,eAAe,GAAG,IAAI,EAAE,OAAO,KAAK,SAAS,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;IAE/E,wBAAwB;IACxB,0CAA0C;IAC1C,gEAAgE;IAChE,IAAI,IAAc,CAAC;IAEnB,IAAI,YAAY,EAAE,CAAC;QACjB,IAAI,GAAG,WAAW,CAAC,IAAK,CAAC,IAAK,CAAC,CAAC;IAClC,CAAC;SAAM,IAAI,cAAc,KAAK,gBAAgB,EAAE,CAAC;QAC/C,IAAI,cAAc,IAAI,eAAe,EAAE,CAAC;YACtC,kEAAkE;YAClE,6DAA6D;YAC7D,6CAA6C;YAC7C,MAAM,IAAI,wBAAwB,CAChC,qFAAqF;gBACnF,4EAA4E,CAC/E,CAAC;QACJ,CAAC;QACD,mEAAmE;QACnE,uEAAuE;QACvE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IACxD,CAAC;SAAM,CAAC;QACN,oEAAoE;QACpE,qCAAqC;QACrC,IAAI,GAAG,WAAW,CAAC,cAAc,CAAC,CAAC;QACnC,IAAI,cAAc,EAAE,CAAC;YACnB,MAAM,SAAS,GAAG,WAAW,CAAC,IAAK,CAAC,MAAO,CAAC,CAAC;YAC7C,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC;YAC3B,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;gBAC7B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;oBACpB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBAChB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBACjB,CAAC;YACH,CAAC;QACH,CAAC;QACD,IAAI,eAAe,EAAE,CAAC;YACpB,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,IAAK,CAAC,OAAQ,CAAC,CAAC,CAAC;YACtD,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;IAED,sEAAsE;IACtE,iBAAiB,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;IAExC,oEAAoE;IACpE,uEAAuE;IACvE,IAAI,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAC;IAEpC,sEAAsE;IACtE,wEAAwE;IACxE,6BAA6B;IAC7B,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IAChD,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AACtD,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAe,EAAE,SAAsB;IAChE,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/C,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,wBAAwB,CAChC,yBAAyB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAC7C,OAAO,CACR,CAAC;IACJ,CAAC;AACH,CAAC"}
|
package/dist/log.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
//
|
|
3
|
+
// Shared pino logger factory.
|
|
4
|
+
//
|
|
5
|
+
// All loggers in this package MUST write to stderr (fd 2), never stdout.
|
|
6
|
+
// This package runs as an MCP stdio server: stdout is reserved exclusively
|
|
7
|
+
// for JSON-RPC protocol frames. Any non-JSONRPC bytes on stdout corrupt the
|
|
8
|
+
// channel for strict MCP clients (e.g. the official Python SDK, which
|
|
9
|
+
// pydantic-validates every received line as a JSONRPCMessage).
|
|
10
|
+
//
|
|
11
|
+
// Discovered via nexus spike_d bench, 2026-05-15 — Claude Code's MCP plugin
|
|
12
|
+
// happened to be lenient with non-JSONRPC stdout, masking the bug until a
|
|
13
|
+
// strict client connected.
|
|
14
|
+
import pino from "pino";
|
|
15
|
+
// Single shared destination — pino docs recommend reusing one destination
|
|
16
|
+
// stream across loggers rather than constructing one per logger.
|
|
17
|
+
const STDERR = pino.destination(2);
|
|
18
|
+
export function createLogger(name) {
|
|
19
|
+
return pino({ name }, STDERR);
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=log.js.map
|
package/dist/log.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.js","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,EAAE;AACF,8BAA8B;AAC9B,EAAE;AACF,yEAAyE;AACzE,2EAA2E;AAC3E,4EAA4E;AAC5E,sEAAsE;AACtE,+DAA+D;AAC/D,EAAE;AACF,4EAA4E;AAC5E,0EAA0E;AAC1E,2BAA2B;AAE3B,OAAO,IAAqB,MAAM,MAAM,CAAC;AAEzC,0EAA0E;AAC1E,iEAAiE;AACjE,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;AAEnC,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,MAAM,CAAC,CAAC;AAChC,CAAC"}
|