feique 1.3.3 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +3 -2
- package/README.md +3 -2
- package/dist/backend/claude.d.ts +2 -0
- package/dist/backend/claude.js +57 -54
- package/dist/backend/claude.js.map +1 -1
- package/dist/backend/codex.d.ts +2 -0
- package/dist/backend/codex.js +27 -0
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/factory.d.ts +43 -0
- package/dist/backend/factory.js +109 -29
- package/dist/backend/factory.js.map +1 -1
- package/dist/backend/probe.d.ts +27 -0
- package/dist/backend/probe.js +85 -0
- package/dist/backend/probe.js.map +1 -0
- package/dist/backend/qwen.d.ts +59 -0
- package/dist/backend/qwen.js +372 -0
- package/dist/backend/qwen.js.map +1 -0
- package/dist/backend/registry.d.ts +58 -0
- package/dist/backend/registry.js +23 -0
- package/dist/backend/registry.js.map +1 -0
- package/dist/backend/types.d.ts +6 -1
- package/dist/bridge/admin-config.d.ts +47 -0
- package/dist/bridge/admin-config.js +141 -0
- package/dist/bridge/admin-config.js.map +1 -0
- package/dist/bridge/collab-commands.d.ts +42 -0
- package/dist/bridge/collab-commands.js +254 -0
- package/dist/bridge/collab-commands.js.map +1 -0
- package/dist/bridge/commands.d.ts +1 -1
- package/dist/bridge/commands.js +10 -6
- package/dist/bridge/commands.js.map +1 -1
- package/dist/bridge/feishu-commands.d.ts +27 -0
- package/dist/bridge/feishu-commands.js +462 -0
- package/dist/bridge/feishu-commands.js.map +1 -0
- package/dist/bridge/intent-classifier.d.ts +7 -2
- package/dist/bridge/intent-classifier.js +1 -1
- package/dist/bridge/intent-classifier.js.map +1 -1
- package/dist/bridge/lifecycle.d.ts +46 -0
- package/dist/bridge/lifecycle.js +228 -0
- package/dist/bridge/lifecycle.js.map +1 -0
- package/dist/bridge/memory-commands.d.ts +26 -0
- package/dist/bridge/memory-commands.js +330 -0
- package/dist/bridge/memory-commands.js.map +1 -0
- package/dist/bridge/reply-builders.d.ts +30 -0
- package/dist/bridge/reply-builders.js +72 -0
- package/dist/bridge/reply-builders.js.map +1 -0
- package/dist/bridge/run-pipeline.d.ts +86 -0
- package/dist/bridge/run-pipeline.js +453 -0
- package/dist/bridge/run-pipeline.js.map +1 -0
- package/dist/bridge/run-scheduler.d.ts +47 -0
- package/dist/bridge/run-scheduler.js +121 -0
- package/dist/bridge/run-scheduler.js.map +1 -0
- package/dist/bridge/service-utils.d.ts +47 -0
- package/dist/bridge/service-utils.js +309 -0
- package/dist/bridge/service-utils.js.map +1 -0
- package/dist/bridge/service.d.ts +114 -66
- package/dist/bridge/service.js +230 -2199
- package/dist/bridge/service.js.map +1 -1
- package/dist/config/load.js +1 -1
- package/dist/config/load.js.map +1 -1
- package/dist/config/paths.js +1 -20
- package/dist/config/paths.js.map +1 -1
- package/dist/config/schema.d.ts +50 -16
- package/dist/config/schema.js +41 -2
- package/dist/config/schema.js.map +1 -1
- package/dist/feishu/long-connection.js +1 -0
- package/dist/feishu/long-connection.js.map +1 -1
- package/dist/feishu/webhook.js +1 -0
- package/dist/feishu/webhook.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
const PROBE_TIMEOUT_MS = 3000;
|
|
3
|
+
const CACHE_TTL_MS = 60_000;
|
|
4
|
+
const cache = new Map();
|
|
5
|
+
function cacheKey(backend, bin, shell, preExec) {
|
|
6
|
+
return `${backend}\0${bin}\0${shell ?? ''}\0${preExec ?? ''}`;
|
|
7
|
+
}
|
|
8
|
+
/** Visible for tests. */
|
|
9
|
+
export function clearProbeCache() {
|
|
10
|
+
cache.clear();
|
|
11
|
+
}
|
|
12
|
+
function buildProbeCommand(spec) {
|
|
13
|
+
if (!spec.preExec) {
|
|
14
|
+
return { command: spec.bin, args: ['--version'] };
|
|
15
|
+
}
|
|
16
|
+
const shell = spec.shell ?? process.env.SHELL ?? '/bin/zsh';
|
|
17
|
+
// Single-quote bin because preExec is already a shell command line.
|
|
18
|
+
const chained = `${spec.preExec} && ${JSON.stringify(spec.bin)} --version`;
|
|
19
|
+
return { command: shell, args: ['-ic', chained] };
|
|
20
|
+
}
|
|
21
|
+
async function runProbe(spec) {
|
|
22
|
+
return new Promise((resolve) => {
|
|
23
|
+
const { command, args } = buildProbeCommand(spec);
|
|
24
|
+
let settled = false;
|
|
25
|
+
let stderr = '';
|
|
26
|
+
const child = spawn(command, args, {
|
|
27
|
+
stdio: ['ignore', 'ignore', 'pipe'],
|
|
28
|
+
});
|
|
29
|
+
const timer = setTimeout(() => {
|
|
30
|
+
if (settled)
|
|
31
|
+
return;
|
|
32
|
+
settled = true;
|
|
33
|
+
try {
|
|
34
|
+
child.kill('SIGKILL');
|
|
35
|
+
}
|
|
36
|
+
catch { /* ignore */ }
|
|
37
|
+
resolve({ ok: false, reason: `timeout after ${PROBE_TIMEOUT_MS}ms running ${spec.bin} --version` });
|
|
38
|
+
}, PROBE_TIMEOUT_MS);
|
|
39
|
+
timer.unref();
|
|
40
|
+
child.stderr?.on('data', (chunk) => {
|
|
41
|
+
stderr += chunk.toString();
|
|
42
|
+
if (stderr.length > 2000)
|
|
43
|
+
stderr = stderr.slice(0, 2000);
|
|
44
|
+
});
|
|
45
|
+
child.on('error', (error) => {
|
|
46
|
+
if (settled)
|
|
47
|
+
return;
|
|
48
|
+
settled = true;
|
|
49
|
+
clearTimeout(timer);
|
|
50
|
+
const code = error.code;
|
|
51
|
+
if (code === 'ENOENT') {
|
|
52
|
+
resolve({ ok: false, reason: `binary not found: ${spec.bin}` });
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
resolve({ ok: false, reason: `spawn error (${code ?? 'unknown'}): ${error.message}` });
|
|
56
|
+
});
|
|
57
|
+
child.on('exit', (exitCode) => {
|
|
58
|
+
if (settled)
|
|
59
|
+
return;
|
|
60
|
+
settled = true;
|
|
61
|
+
clearTimeout(timer);
|
|
62
|
+
if (exitCode === 0) {
|
|
63
|
+
resolve({ ok: true });
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const tail = stderr.trim().split('\n').slice(-1)[0] ?? '';
|
|
67
|
+
resolve({
|
|
68
|
+
ok: false,
|
|
69
|
+
reason: `${spec.bin} --version exited with code ${exitCode}${tail ? `: ${tail}` : ''}`,
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
export async function probeBackend(backend, spec) {
|
|
75
|
+
const key = cacheKey(backend, spec.bin, spec.shell, spec.preExec);
|
|
76
|
+
const now = Date.now();
|
|
77
|
+
const cached = cache.get(key);
|
|
78
|
+
if (cached && cached.expiresAt > now) {
|
|
79
|
+
return cached.result;
|
|
80
|
+
}
|
|
81
|
+
const result = await runProbe(spec);
|
|
82
|
+
cache.set(key, { expiresAt: now + CACHE_TTL_MS, result });
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=probe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"probe.js","sourceRoot":"","sources":["../../src/backend/probe.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { Backend, BackendEvent, BackendRunOptions, BackendRunResult, IndexedSession } from './types.js';
|
|
2
|
+
import type { BackendDefinition } from './registry.js';
|
|
3
|
+
/**
|
|
4
|
+
* Qwen Code CLI backend. Near-identical event + arg surface to
|
|
5
|
+
* ClaudeBackend — both CLIs speak `--output-format stream-json` with
|
|
6
|
+
* `system/init → assistant → result` event frames. The main differences:
|
|
7
|
+
*
|
|
8
|
+
* 1. `--approval-mode plan|default|auto-edit|yolo` instead of
|
|
9
|
+
* `--permission-mode ...`
|
|
10
|
+
* 2. Session storage layout: qwen writes JSONL chats under
|
|
11
|
+
* `~/.qwen/projects/<encoded-cwd>/chats/<uuid>.jsonl`. The first
|
|
12
|
+
* line of each chat is a `type: system` frame with `cwd`, `version`,
|
|
13
|
+
* and `timestamp`, which we parse to drive session adoption.
|
|
14
|
+
* 3. No `--max-budget-usd` equivalent.
|
|
15
|
+
*
|
|
16
|
+
* Qwen Code version validated against: 0.14.1.
|
|
17
|
+
*/
|
|
18
|
+
export type QwenApprovalMode = 'plan' | 'default' | 'auto-edit' | 'yolo';
|
|
19
|
+
export interface QwenBackendConfig {
|
|
20
|
+
bin: string;
|
|
21
|
+
shell?: string;
|
|
22
|
+
preExec?: string;
|
|
23
|
+
defaultApprovalMode: QwenApprovalMode;
|
|
24
|
+
defaultModel?: string;
|
|
25
|
+
allowedTools?: string[];
|
|
26
|
+
systemPromptAppend?: string;
|
|
27
|
+
runTimeoutMs: number;
|
|
28
|
+
}
|
|
29
|
+
export interface QwenProjectConfig {
|
|
30
|
+
approvalMode?: QwenApprovalMode;
|
|
31
|
+
model?: string;
|
|
32
|
+
allowedTools?: string[];
|
|
33
|
+
systemPromptAppend?: string;
|
|
34
|
+
}
|
|
35
|
+
export declare class QwenBackend implements Backend {
|
|
36
|
+
private readonly config;
|
|
37
|
+
private readonly qwenHomeDir;
|
|
38
|
+
readonly name: "qwen";
|
|
39
|
+
constructor(config: QwenBackendConfig, qwenHomeDir?: string);
|
|
40
|
+
run(options: BackendRunOptions & {
|
|
41
|
+
projectConfig?: QwenProjectConfig;
|
|
42
|
+
}): Promise<BackendRunResult>;
|
|
43
|
+
summarizeEvent(event: BackendEvent): string | null;
|
|
44
|
+
listProjectSessions(projectRoot: string, limit?: number): Promise<IndexedSession[]>;
|
|
45
|
+
findLatestSession(projectRoot: string): Promise<IndexedSession | null>;
|
|
46
|
+
findSessionById(projectRoot: string, sessionId: string): Promise<IndexedSession | null>;
|
|
47
|
+
private buildArgs;
|
|
48
|
+
private buildSpawnSpec;
|
|
49
|
+
/**
|
|
50
|
+
* Walk `~/.qwen/projects/<slug>/chats/*.jsonl` and parse the first
|
|
51
|
+
* line of each file to extract `sessionId` + `cwd` + `timestamp`.
|
|
52
|
+
*
|
|
53
|
+
* Qwen encodes the project directory as the slug (e.g. `/Users/dh` →
|
|
54
|
+
* `-Users-dh`). We don't depend on that mapping — we walk every slug
|
|
55
|
+
* and match by parsed `cwd`.
|
|
56
|
+
*/
|
|
57
|
+
private scanSessions;
|
|
58
|
+
}
|
|
59
|
+
export declare const qwenBackendDefinition: BackendDefinition;
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import fs from 'node:fs/promises';
|
|
4
|
+
import { spawn } from 'node:child_process';
|
|
5
|
+
import { registerBackend } from './registry.js';
|
|
6
|
+
export class QwenBackend {
|
|
7
|
+
config;
|
|
8
|
+
qwenHomeDir;
|
|
9
|
+
name = 'qwen';
|
|
10
|
+
constructor(config, qwenHomeDir = resolveQwenHomeDir()) {
|
|
11
|
+
this.config = config;
|
|
12
|
+
this.qwenHomeDir = qwenHomeDir;
|
|
13
|
+
}
|
|
14
|
+
async run(options) {
|
|
15
|
+
const args = this.buildArgs(options);
|
|
16
|
+
const spawnSpec = this.buildSpawnSpec(args);
|
|
17
|
+
options.logger.info({ command: spawnSpec.command, args: spawnSpec.args, workdir: options.workdir }, 'Starting Qwen turn');
|
|
18
|
+
return await new Promise((resolve, reject) => {
|
|
19
|
+
const processRef = spawn(spawnSpec.command, spawnSpec.args, {
|
|
20
|
+
cwd: options.workdir,
|
|
21
|
+
env: {
|
|
22
|
+
...process.env,
|
|
23
|
+
NO_COLOR: '1',
|
|
24
|
+
},
|
|
25
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
26
|
+
});
|
|
27
|
+
let stderr = '';
|
|
28
|
+
let stdoutBuffer = '';
|
|
29
|
+
let sessionId = options.sessionId;
|
|
30
|
+
let finalMessage = '';
|
|
31
|
+
let inputTokens;
|
|
32
|
+
let outputTokens;
|
|
33
|
+
let settled = false;
|
|
34
|
+
let timeoutHandle;
|
|
35
|
+
let abortCleanup;
|
|
36
|
+
const finishReject = (error) => {
|
|
37
|
+
if (settled)
|
|
38
|
+
return;
|
|
39
|
+
settled = true;
|
|
40
|
+
cleanup();
|
|
41
|
+
reject(error);
|
|
42
|
+
};
|
|
43
|
+
const finishResolve = (result) => {
|
|
44
|
+
if (settled)
|
|
45
|
+
return;
|
|
46
|
+
settled = true;
|
|
47
|
+
cleanup();
|
|
48
|
+
resolve(result);
|
|
49
|
+
};
|
|
50
|
+
const cleanup = () => {
|
|
51
|
+
if (timeoutHandle)
|
|
52
|
+
clearTimeout(timeoutHandle);
|
|
53
|
+
abortCleanup?.();
|
|
54
|
+
abortCleanup = undefined;
|
|
55
|
+
};
|
|
56
|
+
const abortRun = (reason) => {
|
|
57
|
+
if (processRef.killed)
|
|
58
|
+
return;
|
|
59
|
+
const message = typeof reason === 'string' ? reason : reason instanceof Error ? reason.message : 'Qwen run aborted';
|
|
60
|
+
const error = new Error(message);
|
|
61
|
+
error.name = 'AbortError';
|
|
62
|
+
processRef.kill('SIGTERM');
|
|
63
|
+
setTimeout(() => {
|
|
64
|
+
if (!processRef.killed)
|
|
65
|
+
processRef.kill('SIGKILL');
|
|
66
|
+
}, 3000).unref();
|
|
67
|
+
finishReject(error);
|
|
68
|
+
};
|
|
69
|
+
if (options.timeoutMs && options.timeoutMs > 0) {
|
|
70
|
+
timeoutHandle = setTimeout(() => {
|
|
71
|
+
abortRun(`Qwen timed out after ${options.timeoutMs}ms`);
|
|
72
|
+
}, options.timeoutMs);
|
|
73
|
+
timeoutHandle.unref();
|
|
74
|
+
}
|
|
75
|
+
if (options.signal) {
|
|
76
|
+
const onAbort = () => abortRun(options.signal?.reason ?? 'Qwen run aborted');
|
|
77
|
+
if (options.signal.aborted) {
|
|
78
|
+
onAbort();
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
options.signal.addEventListener('abort', onAbort, { once: true });
|
|
82
|
+
abortCleanup = () => options.signal?.removeEventListener('abort', onAbort);
|
|
83
|
+
}
|
|
84
|
+
if (typeof processRef.pid === 'number') {
|
|
85
|
+
void options.onSpawn?.(processRef.pid);
|
|
86
|
+
}
|
|
87
|
+
processRef.stdout.on('data', async (chunk) => {
|
|
88
|
+
stdoutBuffer += chunk.toString('utf8');
|
|
89
|
+
const lines = stdoutBuffer.split(/\r?\n/);
|
|
90
|
+
stdoutBuffer = lines.pop() ?? '';
|
|
91
|
+
for (const line of lines) {
|
|
92
|
+
const trimmed = line.trim();
|
|
93
|
+
if (!trimmed.startsWith('{'))
|
|
94
|
+
continue;
|
|
95
|
+
try {
|
|
96
|
+
const event = JSON.parse(trimmed);
|
|
97
|
+
// Session id is emitted on both init and result events.
|
|
98
|
+
if (event.session_id) {
|
|
99
|
+
sessionId = event.session_id;
|
|
100
|
+
}
|
|
101
|
+
// Extract final text + usage from result event.
|
|
102
|
+
if (event.type === 'result') {
|
|
103
|
+
if (typeof event.result === 'string') {
|
|
104
|
+
finalMessage = event.result;
|
|
105
|
+
}
|
|
106
|
+
if (event.usage) {
|
|
107
|
+
if (typeof event.usage.input_tokens === 'number')
|
|
108
|
+
inputTokens = event.usage.input_tokens;
|
|
109
|
+
if (typeof event.usage.output_tokens === 'number')
|
|
110
|
+
outputTokens = event.usage.output_tokens;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Fall back to assistant text content if result event never arrives.
|
|
114
|
+
if (event.type === 'assistant' && event.message?.content) {
|
|
115
|
+
const texts = event.message.content
|
|
116
|
+
.filter(c => c.type === 'text' && typeof c.text === 'string')
|
|
117
|
+
.map(c => c.text)
|
|
118
|
+
.join('\n');
|
|
119
|
+
if (texts) {
|
|
120
|
+
finalMessage = texts;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const backendEvent = {
|
|
124
|
+
type: event.type,
|
|
125
|
+
session_id: event.session_id ?? sessionId,
|
|
126
|
+
message: typeof event.result === 'string' ? event.result : undefined,
|
|
127
|
+
};
|
|
128
|
+
await options.onEvent?.(backendEvent);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
options.logger.debug({ line }, 'Ignoring unparsable Qwen line');
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
processRef.stderr.on('data', (chunk) => {
|
|
136
|
+
stderr += chunk.toString('utf8');
|
|
137
|
+
});
|
|
138
|
+
processRef.on('error', (error) => {
|
|
139
|
+
finishReject(error instanceof Error ? error : new Error(String(error)));
|
|
140
|
+
});
|
|
141
|
+
processRef.on('close', (exitCode) => {
|
|
142
|
+
if (settled)
|
|
143
|
+
return;
|
|
144
|
+
if ((exitCode ?? 1) !== 0 && !finalMessage) {
|
|
145
|
+
finishReject(new Error(`Qwen exited with code ${exitCode ?? 1}: ${stderr.trim() || 'no stderr output'}`));
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
finishResolve({
|
|
149
|
+
sessionId,
|
|
150
|
+
finalMessage: finalMessage.trim(),
|
|
151
|
+
stderr: stderr.trim(),
|
|
152
|
+
exitCode: exitCode ?? 0,
|
|
153
|
+
inputTokens,
|
|
154
|
+
outputTokens,
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
summarizeEvent(event) {
|
|
160
|
+
if (event.type === 'error' && typeof event.message === 'string') {
|
|
161
|
+
return `Qwen 错误:${event.message}`;
|
|
162
|
+
}
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
async listProjectSessions(projectRoot, limit = 10) {
|
|
166
|
+
const sessions = await this.scanSessions();
|
|
167
|
+
const matches = [];
|
|
168
|
+
for (const session of sessions) {
|
|
169
|
+
const match = scoreSessionMatch(projectRoot, session.cwd);
|
|
170
|
+
if (!match)
|
|
171
|
+
continue;
|
|
172
|
+
matches.push({
|
|
173
|
+
...session,
|
|
174
|
+
matchKind: match.kind,
|
|
175
|
+
matchScore: match.score,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
return matches
|
|
179
|
+
.sort((a, b) => {
|
|
180
|
+
const scoreDelta = (b.matchScore ?? 0) - (a.matchScore ?? 0);
|
|
181
|
+
if (scoreDelta !== 0)
|
|
182
|
+
return scoreDelta;
|
|
183
|
+
return b.updatedAt.localeCompare(a.updatedAt);
|
|
184
|
+
})
|
|
185
|
+
.slice(0, limit);
|
|
186
|
+
}
|
|
187
|
+
async findLatestSession(projectRoot) {
|
|
188
|
+
const [session] = await this.listProjectSessions(projectRoot, 1);
|
|
189
|
+
return session ?? null;
|
|
190
|
+
}
|
|
191
|
+
async findSessionById(projectRoot, sessionId) {
|
|
192
|
+
const sessions = await this.scanSessions();
|
|
193
|
+
const candidate = sessions.find(s => s.sessionId === sessionId);
|
|
194
|
+
if (!candidate)
|
|
195
|
+
return null;
|
|
196
|
+
const match = scoreSessionMatch(projectRoot, candidate.cwd);
|
|
197
|
+
if (!match)
|
|
198
|
+
return null;
|
|
199
|
+
return { ...candidate, matchKind: match.kind, matchScore: match.score };
|
|
200
|
+
}
|
|
201
|
+
buildArgs(options) {
|
|
202
|
+
const args = ['-p'];
|
|
203
|
+
args.push('--output-format', 'stream-json');
|
|
204
|
+
const approvalMode = options.projectConfig?.approvalMode ?? this.config.defaultApprovalMode;
|
|
205
|
+
args.push('--approval-mode', approvalMode);
|
|
206
|
+
const model = options.projectConfig?.model ?? this.config.defaultModel;
|
|
207
|
+
if (model) {
|
|
208
|
+
args.push('--model', model);
|
|
209
|
+
}
|
|
210
|
+
const allowedTools = options.projectConfig?.allowedTools ?? this.config.allowedTools;
|
|
211
|
+
if (allowedTools && allowedTools.length > 0) {
|
|
212
|
+
args.push('--allowed-tools', ...allowedTools);
|
|
213
|
+
}
|
|
214
|
+
const systemPromptAppend = options.projectConfig?.systemPromptAppend ?? this.config.systemPromptAppend;
|
|
215
|
+
if (systemPromptAppend) {
|
|
216
|
+
args.push('--append-system-prompt', systemPromptAppend);
|
|
217
|
+
}
|
|
218
|
+
if (options.sessionId) {
|
|
219
|
+
args.push('--resume', options.sessionId);
|
|
220
|
+
}
|
|
221
|
+
args.push(options.prompt);
|
|
222
|
+
return args;
|
|
223
|
+
}
|
|
224
|
+
buildSpawnSpec(qwenArgs) {
|
|
225
|
+
if (!this.config.preExec) {
|
|
226
|
+
return { command: this.config.bin, args: qwenArgs };
|
|
227
|
+
}
|
|
228
|
+
const shell = this.config.shell ?? process.env.SHELL ?? '/bin/zsh';
|
|
229
|
+
const chainedCommand = `${this.config.preExec} && ${quoteShellCommand([this.config.bin, ...qwenArgs])}`;
|
|
230
|
+
return { command: shell, args: ['-ic', chainedCommand] };
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Walk `~/.qwen/projects/<slug>/chats/*.jsonl` and parse the first
|
|
234
|
+
* line of each file to extract `sessionId` + `cwd` + `timestamp`.
|
|
235
|
+
*
|
|
236
|
+
* Qwen encodes the project directory as the slug (e.g. `/Users/dh` →
|
|
237
|
+
* `-Users-dh`). We don't depend on that mapping — we walk every slug
|
|
238
|
+
* and match by parsed `cwd`.
|
|
239
|
+
*/
|
|
240
|
+
async scanSessions() {
|
|
241
|
+
const projectsDir = path.join(this.qwenHomeDir, 'projects');
|
|
242
|
+
const sessions = new Map();
|
|
243
|
+
let slugEntries;
|
|
244
|
+
try {
|
|
245
|
+
slugEntries = await fs.readdir(projectsDir);
|
|
246
|
+
}
|
|
247
|
+
catch {
|
|
248
|
+
return [];
|
|
249
|
+
}
|
|
250
|
+
for (const slug of slugEntries) {
|
|
251
|
+
const chatsDir = path.join(projectsDir, slug, 'chats');
|
|
252
|
+
const chatFiles = await fs.readdir(chatsDir).catch(() => []);
|
|
253
|
+
for (const chatFile of chatFiles) {
|
|
254
|
+
if (!chatFile.endsWith('.jsonl'))
|
|
255
|
+
continue;
|
|
256
|
+
const filePath = path.join(chatsDir, chatFile);
|
|
257
|
+
const stat = await fs.stat(filePath).catch(() => null);
|
|
258
|
+
if (!stat?.isFile())
|
|
259
|
+
continue;
|
|
260
|
+
try {
|
|
261
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
262
|
+
const firstLine = content.split(/\r?\n/, 1)[0]?.trim();
|
|
263
|
+
if (!firstLine)
|
|
264
|
+
continue;
|
|
265
|
+
const parsed = JSON.parse(firstLine);
|
|
266
|
+
const sessionId = parsed.sessionId;
|
|
267
|
+
const cwd = parsed.cwd;
|
|
268
|
+
if (!sessionId || !cwd)
|
|
269
|
+
continue;
|
|
270
|
+
const createdAt = parsed.timestamp;
|
|
271
|
+
const updatedAt = new Date(stat.mtimeMs).toISOString();
|
|
272
|
+
const existing = sessions.get(sessionId);
|
|
273
|
+
if (!existing || existing.updatedAt < updatedAt) {
|
|
274
|
+
const record = {
|
|
275
|
+
sessionId,
|
|
276
|
+
cwd,
|
|
277
|
+
updatedAt,
|
|
278
|
+
filePath,
|
|
279
|
+
source: 'sessions',
|
|
280
|
+
backend: 'qwen',
|
|
281
|
+
};
|
|
282
|
+
if (createdAt) {
|
|
283
|
+
record.createdAt = createdAt;
|
|
284
|
+
}
|
|
285
|
+
sessions.set(sessionId, record);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return [...sessions.values()].sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
function resolveQwenHomeDir() {
|
|
297
|
+
const configured = process.env.QWEN_HOME?.trim();
|
|
298
|
+
if (!configured)
|
|
299
|
+
return path.join(os.homedir(), '.qwen');
|
|
300
|
+
if (configured === '~')
|
|
301
|
+
return os.homedir();
|
|
302
|
+
if (configured.startsWith('~/'))
|
|
303
|
+
return path.join(os.homedir(), configured.slice(2));
|
|
304
|
+
return path.resolve(configured);
|
|
305
|
+
}
|
|
306
|
+
function quoteShellArg(value) {
|
|
307
|
+
if (value.length === 0)
|
|
308
|
+
return "''";
|
|
309
|
+
return `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
310
|
+
}
|
|
311
|
+
function quoteShellCommand(parts) {
|
|
312
|
+
return parts.map(quoteShellArg).join(' ');
|
|
313
|
+
}
|
|
314
|
+
const FUZZY_SUFFIX_TOKENS = new Set(['bridge', 'repo', 'project', 'workspace']);
|
|
315
|
+
function scoreSessionMatch(projectRoot, sessionCwd) {
|
|
316
|
+
const normalizedProjectRoot = path.resolve(projectRoot).replace(/\/+$/, '').toLowerCase();
|
|
317
|
+
const normalizedSessionRoot = path.resolve(sessionCwd).replace(/\/+$/, '').toLowerCase();
|
|
318
|
+
if (normalizedProjectRoot === normalizedSessionRoot) {
|
|
319
|
+
return { kind: 'exact-root', score: 100 };
|
|
320
|
+
}
|
|
321
|
+
const projectBase = path.basename(normalizedProjectRoot);
|
|
322
|
+
const sessionBase = path.basename(normalizedSessionRoot);
|
|
323
|
+
if (projectBase === sessionBase) {
|
|
324
|
+
return { kind: 'basename', score: 80 };
|
|
325
|
+
}
|
|
326
|
+
const normalizedProjectName = normalizeProjectName(projectBase);
|
|
327
|
+
const normalizedSessionName = normalizeProjectName(sessionBase);
|
|
328
|
+
if (normalizedProjectName && normalizedProjectName === normalizedSessionName) {
|
|
329
|
+
return { kind: 'normalized-name', score: 60 };
|
|
330
|
+
}
|
|
331
|
+
if (normalizedProjectName.length >= 5 && normalizedSessionName.includes(normalizedProjectName)) {
|
|
332
|
+
return { kind: 'basename-contains', score: 40 };
|
|
333
|
+
}
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
function normalizeProjectName(input) {
|
|
337
|
+
const tokens = input
|
|
338
|
+
.toLowerCase()
|
|
339
|
+
.replace(/\.git$/, '')
|
|
340
|
+
.split(/[^a-z0-9]+/)
|
|
341
|
+
.filter(Boolean);
|
|
342
|
+
const filtered = tokens.filter(token => !FUZZY_SUFFIX_TOKENS.has(token));
|
|
343
|
+
return (filtered.length > 0 ? filtered : tokens).join('-');
|
|
344
|
+
}
|
|
345
|
+
// ---------------------------------------------------------------------------
|
|
346
|
+
// Registry definition
|
|
347
|
+
// ---------------------------------------------------------------------------
|
|
348
|
+
export const qwenBackendDefinition = {
|
|
349
|
+
name: 'qwen',
|
|
350
|
+
create(config) {
|
|
351
|
+
return new QwenBackend({
|
|
352
|
+
bin: config.qwen?.bin ?? 'qwen',
|
|
353
|
+
shell: config.qwen?.shell ?? config.codex.shell,
|
|
354
|
+
preExec: config.qwen?.pre_exec ?? config.codex.pre_exec,
|
|
355
|
+
defaultApprovalMode: config.qwen?.default_approval_mode ?? 'default',
|
|
356
|
+
defaultModel: config.qwen?.default_model,
|
|
357
|
+
allowedTools: config.qwen?.allowed_tools,
|
|
358
|
+
systemPromptAppend: config.qwen?.system_prompt_append,
|
|
359
|
+
runTimeoutMs: config.qwen?.run_timeout_ms ?? config.codex.run_timeout_ms,
|
|
360
|
+
});
|
|
361
|
+
},
|
|
362
|
+
probeSpec(config) {
|
|
363
|
+
return {
|
|
364
|
+
bin: config.qwen?.bin ?? 'qwen',
|
|
365
|
+
shell: config.qwen?.shell ?? config.codex.shell,
|
|
366
|
+
preExec: config.qwen?.pre_exec ?? config.codex.pre_exec,
|
|
367
|
+
};
|
|
368
|
+
},
|
|
369
|
+
defaultFallback: ['claude', 'codex'],
|
|
370
|
+
};
|
|
371
|
+
registerBackend(qwenBackendDefinition);
|
|
372
|
+
//# sourceMappingURL=qwen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qwen.js","sourceRoot":"","sources":["../../src/backend/qwen.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { BridgeConfig } from '../config/schema.js';
|
|
2
|
+
import type { Backend, BackendName } from './types.js';
|
|
3
|
+
import type { ProbeSpec } from './probe.js';
|
|
4
|
+
import type { CodexSessionIndex } from '../codex/session-index.js';
|
|
5
|
+
/**
|
|
6
|
+
* Extensible backend registry.
|
|
7
|
+
*
|
|
8
|
+
* Every backend (codex, claude, qwen, ...) contributes a BackendDefinition
|
|
9
|
+
* that knows how to construct itself from BridgeConfig and how to expose
|
|
10
|
+
* its probe spec for startup failover. Adding a new backend is purely an
|
|
11
|
+
* additive change: drop a new file under src/backend/, export its
|
|
12
|
+
* definition, call registerBackend() at module load, and wire it into
|
|
13
|
+
* src/backend/factory.ts's side-effect import list.
|
|
14
|
+
*
|
|
15
|
+
* BackendName is intentionally widened to `string` (see types.ts) —
|
|
16
|
+
* compile-time literal unions were incompatible with a registry because
|
|
17
|
+
* every new backend would require touching the type. Runtime validation
|
|
18
|
+
* happens in requireBackendDefinition().
|
|
19
|
+
*/
|
|
20
|
+
export interface BackendDependencies {
|
|
21
|
+
/**
|
|
22
|
+
* Shared CodexSessionIndex, passed in by FeiqueService. Any backend
|
|
23
|
+
* that wants access can read it from here; codex relies on it for
|
|
24
|
+
* resume semantics.
|
|
25
|
+
*/
|
|
26
|
+
codexSessionIndex?: CodexSessionIndex;
|
|
27
|
+
}
|
|
28
|
+
export interface BackendDefinition {
|
|
29
|
+
/** Unique registry key, e.g. 'codex', 'claude', 'qwen'. */
|
|
30
|
+
readonly name: string;
|
|
31
|
+
/**
|
|
32
|
+
* Build a live Backend instance from config. Called on every run so
|
|
33
|
+
* hot-reloaded config is picked up immediately.
|
|
34
|
+
*/
|
|
35
|
+
create(config: BridgeConfig, deps: BackendDependencies): Backend;
|
|
36
|
+
/**
|
|
37
|
+
* Extract the probe spec (bin + shell + pre_exec) from config.
|
|
38
|
+
* Used by the failover resolver before each run.
|
|
39
|
+
*/
|
|
40
|
+
probeSpec(config: BridgeConfig): ProbeSpec;
|
|
41
|
+
/**
|
|
42
|
+
* Optional default fallback chain. Used when neither the project nor
|
|
43
|
+
* the global config supplies an explicit fallback list. If omitted,
|
|
44
|
+
* the resolver falls back to "every other registered backend" in
|
|
45
|
+
* registration order.
|
|
46
|
+
*
|
|
47
|
+
* Example: codex's default fallback is ['claude'] so that when codex
|
|
48
|
+
* is broken on a box, users get claude automatically without needing
|
|
49
|
+
* to set anything.
|
|
50
|
+
*/
|
|
51
|
+
readonly defaultFallback?: readonly string[];
|
|
52
|
+
}
|
|
53
|
+
export declare function registerBackend(definition: BackendDefinition): void;
|
|
54
|
+
export declare function getBackendDefinition(name: string): BackendDefinition | undefined;
|
|
55
|
+
export declare function requireBackendDefinition(name: string): BackendDefinition;
|
|
56
|
+
export declare function listBackendNames(): BackendName[];
|
|
57
|
+
/** Visible for tests only. */
|
|
58
|
+
export declare function clearBackendRegistry(): void;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const registry = new Map();
|
|
2
|
+
export function registerBackend(definition) {
|
|
3
|
+
registry.set(definition.name, definition);
|
|
4
|
+
}
|
|
5
|
+
export function getBackendDefinition(name) {
|
|
6
|
+
return registry.get(name);
|
|
7
|
+
}
|
|
8
|
+
export function requireBackendDefinition(name) {
|
|
9
|
+
const def = registry.get(name);
|
|
10
|
+
if (!def) {
|
|
11
|
+
const known = [...registry.keys()].join(', ') || '(none)';
|
|
12
|
+
throw new Error(`Unknown backend: ${name}. Registered backends: ${known}`);
|
|
13
|
+
}
|
|
14
|
+
return def;
|
|
15
|
+
}
|
|
16
|
+
export function listBackendNames() {
|
|
17
|
+
return [...registry.keys()];
|
|
18
|
+
}
|
|
19
|
+
/** Visible for tests only. */
|
|
20
|
+
export function clearBackendRegistry() {
|
|
21
|
+
registry.clear();
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/backend/registry.ts"],"names":[],"mappings":"AA2DA,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA6B,CAAC;AAEtD,MAAM,UAAU,eAAe,CAAC,UAA6B;IAC3D,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,OAAO,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,IAAY;IACnD,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,KAAK,GAAG,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC;QAC1D,MAAM,IAAI,KAAK,CAAC,oBAAoB,IAAI,0BAA0B,KAAK,EAAE,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,OAAO,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;AAC9B,CAAC;AAED,8BAA8B;AAC9B,MAAM,UAAU,oBAAoB;IAClC,QAAQ,CAAC,KAAK,EAAE,CAAC;AACnB,CAAC"}
|
package/dist/backend/types.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { Logger } from '../logging.js';
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Backend identifier. Intentionally typed as `string` (not a literal union)
|
|
4
|
+
* because backends are registered at runtime via src/backend/registry.ts.
|
|
5
|
+
* Use requireBackendDefinition() to validate at the boundary.
|
|
6
|
+
*/
|
|
7
|
+
export type BackendName = string;
|
|
3
8
|
export interface BackendEvent {
|
|
4
9
|
type?: string;
|
|
5
10
|
session_id?: string;
|