@co0ontty/wand 4.46.0 → 4.47.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/dist/build-info.json +3 -3
- package/dist/distribution-manager.d.ts +17 -0
- package/dist/distribution-manager.js +80 -17
- package/dist/server-update-routes.d.ts +5 -0
- package/dist/server-update-routes.js +15 -0
- package/dist/server.js +8 -2
- package/dist/structured-claude-adapter.d.ts +3 -1
- package/dist/structured-claude-adapter.js +48 -93
- package/dist/structured-codex-adapter.d.ts +5 -0
- package/dist/structured-codex-adapter.js +40 -78
- package/dist/structured-exec-host.d.ts +76 -0
- package/dist/structured-exec-host.js +117 -0
- package/dist/structured-exec-pump.d.ts +37 -0
- package/dist/structured-exec-pump.js +127 -0
- package/dist/structured-grok-adapter.d.ts +3 -1
- package/dist/structured-grok-adapter.js +28 -67
- package/dist/structured-opencode-adapter.d.ts +3 -1
- package/dist/structured-opencode-adapter.js +37 -79
- package/dist/structured-pi-adapter.d.ts +3 -1
- package/dist/structured-pi-adapter.js +27 -39
- package/dist/structured-qoder-adapter.d.ts +3 -1
- package/dist/structured-qoder-adapter.js +49 -87
- package/dist/structured-session-manager.d.ts +9 -1
- package/dist/structured-session-manager.js +399 -12
- package/dist/terminal-daemon-client.d.ts +15 -1
- package/dist/terminal-daemon-client.js +261 -0
- package/dist/terminal-daemon-protocol.d.ts +10 -3
- package/dist/terminal-daemon-protocol.js +1 -1
- package/dist/terminal-daemon-server.js +178 -0
- package/dist/web-ui/content/scripts.js +57 -57
- package/dist/web-ui/content/styles.css +1 -1
- package/dist/web-ui/embedded-assets.d.ts +1 -1
- package/dist/web-ui/embedded-assets.js +3 -3
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
+
import { startStructuredCli } from "./structured-exec-pump.js";
|
|
2
3
|
import { thinkingEffortToPiLevel } from "./structured-provider-common.js";
|
|
3
4
|
function record(value) {
|
|
4
5
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
@@ -86,50 +87,37 @@ export function applyPiEvent(state, event) {
|
|
|
86
87
|
}
|
|
87
88
|
export class PiRunner {
|
|
88
89
|
spawnProcess;
|
|
89
|
-
|
|
90
|
+
execHost;
|
|
91
|
+
constructor(spawnProcess = spawn, execHost) {
|
|
90
92
|
this.spawnProcess = spawnProcess;
|
|
93
|
+
this.execHost = execHost;
|
|
91
94
|
}
|
|
92
95
|
start(context, observer) {
|
|
93
96
|
const args = buildPiArgs(context.session, context.prompt);
|
|
94
|
-
const spawnedAt = new Date().toISOString();
|
|
95
|
-
const child = this.spawnProcess("pi", args, { cwd: context.session.cwd, env: context.env, stdio: ["ignore", "pipe", "pipe"] });
|
|
96
97
|
const state = { blocks: [], result: "", sessionId: context.session.claudeSessionId, model: context.session.selectedModel ?? undefined };
|
|
97
|
-
let
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
child.on("error", (error) => { if (!settled) {
|
|
121
|
-
settled = true;
|
|
122
|
-
resolve(result(null, null, error));
|
|
123
|
-
} });
|
|
124
|
-
child.on("close", (code, signal) => { if (!settled) {
|
|
125
|
-
settled = true;
|
|
126
|
-
processLine(lineBuffer);
|
|
127
|
-
resolve(result(code, signal));
|
|
128
|
-
} });
|
|
98
|
+
let primaryError = null;
|
|
99
|
+
return startStructuredCli({
|
|
100
|
+
sessionId: context.session.id,
|
|
101
|
+
file: "pi",
|
|
102
|
+
args,
|
|
103
|
+
cwd: context.session.cwd,
|
|
104
|
+
env: context.env,
|
|
105
|
+
observer,
|
|
106
|
+
execHost: this.execHost,
|
|
107
|
+
spawnProcess: this.spawnProcess,
|
|
108
|
+
createState: () => state,
|
|
109
|
+
processLine: (line) => {
|
|
110
|
+
if (!observer.isActive() || !line.trim())
|
|
111
|
+
return;
|
|
112
|
+
try {
|
|
113
|
+
const event = JSON.parse(line);
|
|
114
|
+
observer.onEvent?.(event);
|
|
115
|
+
primaryError = applyPiEvent(state, event) ?? primaryError;
|
|
116
|
+
observer.onUpdate(state);
|
|
117
|
+
}
|
|
118
|
+
catch { /* Pi stdout is NDJSON; ignore non-protocol noise. */ }
|
|
119
|
+
},
|
|
120
|
+
finalize: (ctx, exitCode, signal, spawnError) => ({ state, exitCode, signal, stderr: ctx.stderr, primaryError, ...(spawnError ? { spawnError } : {}) }),
|
|
129
121
|
});
|
|
130
|
-
return { args, spawnedAt, pid: child.pid ?? null, completion, interrupt: () => { try {
|
|
131
|
-
child.kill("SIGTERM");
|
|
132
|
-
}
|
|
133
|
-
catch { /* best effort */ } } };
|
|
134
122
|
}
|
|
135
123
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
+
import type { StructuredExecHost } from "./structured-exec-host.js";
|
|
2
3
|
import type { StructuredRunnerAdapter, StructuredRunnerContext, StructuredRunnerExecution, StructuredRunnerObserver } from "./structured-runner.js";
|
|
3
4
|
import type { SessionSnapshot } from "./types.js";
|
|
4
5
|
export declare function buildQoderArgs(session: SessionSnapshot, prompt: string): string[];
|
|
5
6
|
/** Owns the official Qoder CLI print process and its stream-json protocol. */
|
|
6
7
|
export declare class QoderRunner implements StructuredRunnerAdapter {
|
|
7
8
|
private readonly spawnProcess;
|
|
8
|
-
|
|
9
|
+
private readonly execHost?;
|
|
10
|
+
constructor(spawnProcess?: typeof spawn, execHost?: StructuredExecHost | undefined);
|
|
9
11
|
start(context: StructuredRunnerContext, observer: StructuredRunnerObserver): StructuredRunnerExecution;
|
|
10
12
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { ClaudeCliProtocolReducer } from "./structured-claude-protocol.js";
|
|
3
|
+
import { startStructuredCli } from "./structured-exec-pump.js";
|
|
3
4
|
export function buildQoderArgs(session, prompt) {
|
|
4
5
|
const args = ["-p", prompt, "--output-format", "stream-json"];
|
|
5
6
|
const model = session.selectedModel?.trim();
|
|
@@ -20,105 +21,66 @@ export function buildQoderArgs(session, prompt) {
|
|
|
20
21
|
/** Owns the official Qoder CLI print process and its stream-json protocol. */
|
|
21
22
|
export class QoderRunner {
|
|
22
23
|
spawnProcess;
|
|
23
|
-
|
|
24
|
+
execHost;
|
|
25
|
+
constructor(spawnProcess = spawn, execHost) {
|
|
24
26
|
this.spawnProcess = spawnProcess;
|
|
27
|
+
this.execHost = execHost;
|
|
25
28
|
}
|
|
26
29
|
start(context, observer) {
|
|
27
30
|
const args = buildQoderArgs(context.session, context.prompt);
|
|
28
|
-
const spawnedAt = new Date().toISOString();
|
|
29
|
-
const child = this.spawnProcess("qodercli", args, {
|
|
30
|
-
cwd: context.session.cwd,
|
|
31
|
-
env: context.env,
|
|
32
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
33
|
-
});
|
|
34
31
|
const reducer = new ClaudeCliProtocolReducer(context.session);
|
|
35
|
-
let lineBuffer = "";
|
|
36
|
-
let stderr = "";
|
|
37
32
|
let stdoutTail = "";
|
|
38
33
|
let primaryError = null;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (!observer.isActive())
|
|
51
|
-
return;
|
|
52
|
-
const trimmed = line.trim();
|
|
53
|
-
if (!trimmed)
|
|
54
|
-
return;
|
|
55
|
-
let event;
|
|
56
|
-
try {
|
|
57
|
-
event = JSON.parse(trimmed);
|
|
58
|
-
}
|
|
59
|
-
catch {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
if (event && typeof event === "object" && !Array.isArray(event)) {
|
|
63
|
-
const record = event;
|
|
64
|
-
observer.onEvent?.(record);
|
|
65
|
-
if (record.type === "result" && record.subtype !== "success") {
|
|
66
|
-
const errors = Array.isArray(record.errors)
|
|
67
|
-
? record.errors.filter((item) => typeof item === "string")
|
|
68
|
-
: [];
|
|
69
|
-
primaryError = errors.join("\n") || "Qoder CLI execution failed";
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
if (reducer.apply(event, context.session.mode === "managed"))
|
|
73
|
-
observer.onUpdate(reducer.state);
|
|
74
|
-
};
|
|
75
|
-
const completion = new Promise((resolve) => {
|
|
76
|
-
child.stdout?.on("data", (chunk) => {
|
|
34
|
+
return startStructuredCli({
|
|
35
|
+
sessionId: context.session.id,
|
|
36
|
+
file: "qodercli",
|
|
37
|
+
args,
|
|
38
|
+
cwd: context.session.cwd,
|
|
39
|
+
env: context.env,
|
|
40
|
+
observer,
|
|
41
|
+
execHost: this.execHost,
|
|
42
|
+
spawnProcess: this.spawnProcess,
|
|
43
|
+
createState: () => reducer.state,
|
|
44
|
+
processLine: (line) => {
|
|
77
45
|
if (!observer.isActive())
|
|
78
46
|
return;
|
|
79
|
-
const
|
|
80
|
-
|
|
47
|
+
const trimmed = line.trim();
|
|
48
|
+
if (!trimmed)
|
|
49
|
+
return;
|
|
50
|
+
let event;
|
|
51
|
+
try {
|
|
52
|
+
event = JSON.parse(trimmed);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (event && typeof event === "object" && !Array.isArray(event)) {
|
|
58
|
+
const record = event;
|
|
59
|
+
observer.onEvent?.(record);
|
|
60
|
+
if (record.type === "result" && record.subtype !== "success") {
|
|
61
|
+
const errors = Array.isArray(record.errors)
|
|
62
|
+
? record.errors.filter((item) => typeof item === "string")
|
|
63
|
+
: [];
|
|
64
|
+
primaryError = errors.join("\n") || "Qoder CLI execution failed";
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (reducer.apply(event, context.session.mode === "managed"))
|
|
68
|
+
observer.onUpdate(reducer.state);
|
|
69
|
+
},
|
|
70
|
+
onStdoutText: (text) => {
|
|
81
71
|
const trimmed = text.trim();
|
|
82
72
|
if (trimmed)
|
|
83
73
|
stdoutTail = trimmed.slice(-1024);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
observer.onStderr?.(text);
|
|
95
|
-
stderr += text;
|
|
96
|
-
});
|
|
97
|
-
child.on("error", (error) => {
|
|
98
|
-
if (settled)
|
|
99
|
-
return;
|
|
100
|
-
settled = true;
|
|
101
|
-
resolve(result(null, null, error));
|
|
102
|
-
});
|
|
103
|
-
child.on("close", (exitCode, signal) => {
|
|
104
|
-
if (settled)
|
|
105
|
-
return;
|
|
106
|
-
settled = true;
|
|
107
|
-
if (lineBuffer.trim())
|
|
108
|
-
processLine(lineBuffer);
|
|
109
|
-
lineBuffer = "";
|
|
110
|
-
resolve(result(exitCode, signal));
|
|
111
|
-
});
|
|
74
|
+
},
|
|
75
|
+
finalize: (ctx, exitCode, signal, spawnError) => ({
|
|
76
|
+
state: reducer.state,
|
|
77
|
+
exitCode,
|
|
78
|
+
signal,
|
|
79
|
+
stderr: ctx.stderr,
|
|
80
|
+
stdoutTail,
|
|
81
|
+
primaryError,
|
|
82
|
+
...(spawnError ? { spawnError } : {}),
|
|
83
|
+
}),
|
|
112
84
|
});
|
|
113
|
-
return {
|
|
114
|
-
args,
|
|
115
|
-
spawnedAt,
|
|
116
|
-
pid: child.pid ?? null,
|
|
117
|
-
completion,
|
|
118
|
-
interrupt: () => { try {
|
|
119
|
-
child.kill("SIGTERM");
|
|
120
|
-
}
|
|
121
|
-
catch { /* best effort */ } },
|
|
122
|
-
};
|
|
123
85
|
}
|
|
124
86
|
}
|
|
@@ -3,6 +3,7 @@ import { type WorktreeSetupSpec } from "./git-worktree.js";
|
|
|
3
3
|
import { SessionLogger } from "./session-logger.js";
|
|
4
4
|
import { WandStorage } from "./storage.js";
|
|
5
5
|
import { ExecutionMode, ProcessEvent, SessionProvider, SessionRunner, SessionSnapshot, SessionSource, WandConfig } from "./types.js";
|
|
6
|
+
import { type StructuredExecHost } from "./structured-exec-host.js";
|
|
6
7
|
import type { StructuredRunnerAdapter } from "./structured-runner.js";
|
|
7
8
|
export interface StructuredSessionManagerRunners {
|
|
8
9
|
claudeCli?: StructuredRunnerAdapter;
|
|
@@ -52,6 +53,7 @@ export declare class StructuredSessionManager {
|
|
|
52
53
|
private readonly config;
|
|
53
54
|
private readonly logger;
|
|
54
55
|
private readonly sdkQueryFactory;
|
|
56
|
+
private readonly execHost?;
|
|
55
57
|
private readonly sessions;
|
|
56
58
|
private readonly pendingRunnerExecutions;
|
|
57
59
|
private readonly pendingSdkAbort;
|
|
@@ -93,12 +95,18 @@ export declare class StructuredSessionManager {
|
|
|
93
95
|
private readonly grokRunner;
|
|
94
96
|
private readonly qoderRunner;
|
|
95
97
|
private readonly piRunner;
|
|
98
|
+
/** Structured CLI runs that were mid-flight when the previous web process died. */
|
|
99
|
+
private pendingRecoveryIds;
|
|
96
100
|
private disposed;
|
|
97
|
-
constructor(storage: WandStorage, config: WandConfig, logger?: SessionLogger | null, sdkQueryFactory?: typeof sdkQuery, runners?: StructuredSessionManagerRunners);
|
|
101
|
+
constructor(storage: WandStorage, config: WandConfig, logger?: SessionLogger | null, sdkQueryFactory?: typeof sdkQuery, runners?: StructuredSessionManagerRunners, execHost?: StructuredExecHost | undefined);
|
|
98
102
|
private archiveExpiredSessions;
|
|
99
103
|
setEventEmitter(emitEvent: (event: ProcessEvent) => void): void;
|
|
100
104
|
/** Stop every runner and flush terminal state before storage is closed. */
|
|
101
105
|
dispose(): void;
|
|
106
|
+
/** Called once after startup wiring; safe to skip when no host or candidates. */
|
|
107
|
+
recoverDetachedRuns(): Promise<void>;
|
|
108
|
+
private resumeDetachedRun;
|
|
109
|
+
private finalizeRecoveredRun;
|
|
102
110
|
private trackStreamEmitTimer;
|
|
103
111
|
private clearStreamEmitTimer;
|
|
104
112
|
/** Mark streaming payload dirty and enforce both leading and trailing checkpoints. */
|