@linzumi/cli 0.0.12-beta → 0.0.13-beta
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 +200 -95
- package/package.json +2 -2
- package/src/agentBootstrap.ts +806 -0
- package/src/channelSession.ts +616 -31
- package/src/channelSessionSupport.ts +54 -1
- package/src/forwardTunnel.ts +32 -7
- package/src/index.ts +293 -56
- package/src/kandanQueue.ts +11 -0
- package/src/localForwarding.ts +31 -8
- package/src/protocol.ts +15 -0
- package/src/runner.ts +21 -3
package/src/kandanQueue.ts
CHANGED
|
@@ -8,12 +8,22 @@
|
|
|
8
8
|
Spec: plans/2026-04-24-local-codex-runner-deep-quality-spec.md
|
|
9
9
|
Relationship: Carries selected queued message seqs through interrupt fusion
|
|
10
10
|
so the UI can show which pending replies were accepted by the interrupt.
|
|
11
|
+
|
|
12
|
+
- Date: 2026-05-02
|
|
13
|
+
Spec: plans/2026-05-02-agent-first-zero-to-codex-launch-plan.md
|
|
14
|
+
Relationship: Carries Kandan attachment metadata through npm CLI runner queue
|
|
15
|
+
fusion so attachment-backed replies stay intact before Codex sees them.
|
|
11
16
|
*/
|
|
17
|
+
import { type KandanChatAttachment } from "./channelSessionSupport";
|
|
18
|
+
|
|
19
|
+
export type QueuedKandanAttachment = KandanChatAttachment;
|
|
20
|
+
|
|
12
21
|
export type QueuedKandanMessage = {
|
|
13
22
|
readonly seq: number;
|
|
14
23
|
readonly actorSlug: string | undefined;
|
|
15
24
|
readonly actorUserId: number | undefined;
|
|
16
25
|
readonly body: string;
|
|
26
|
+
readonly attachments: readonly QueuedKandanAttachment[];
|
|
17
27
|
};
|
|
18
28
|
|
|
19
29
|
export type QueueInterruptResult =
|
|
@@ -98,5 +108,6 @@ function fuseQueuedMessages(
|
|
|
98
108
|
actorSlug: "kandan",
|
|
99
109
|
actorUserId: undefined,
|
|
100
110
|
body: selected.map(codexInputForQueuedKandanMessage).join("\n\n---\n\n"),
|
|
111
|
+
attachments: selected.flatMap(message => message.attachments),
|
|
101
112
|
};
|
|
102
113
|
}
|
package/src/localForwarding.ts
CHANGED
|
@@ -211,14 +211,15 @@ function openLocalWebSocket(
|
|
|
211
211
|
scheme: "ws" | "wss",
|
|
212
212
|
): void {
|
|
213
213
|
let opened = false;
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
214
|
+
const url = localForwardUrl(
|
|
215
|
+
scheme === "ws" ? "http" : "https",
|
|
216
|
+
control.port,
|
|
217
|
+
control.path,
|
|
218
|
+
control.queryString,
|
|
219
|
+
).replace(/^http/, scheme);
|
|
220
|
+
const protocols = webSocketProtocols(control.headers);
|
|
221
|
+
const websocket =
|
|
222
|
+
protocols === undefined ? new WebSocket(url) : new WebSocket(url, protocols);
|
|
222
223
|
sockets.set(control.socketId, websocket);
|
|
223
224
|
websocket.addEventListener("open", () => {
|
|
224
225
|
opened = true;
|
|
@@ -260,6 +261,28 @@ function openLocalWebSocket(
|
|
|
260
261
|
});
|
|
261
262
|
}
|
|
262
263
|
|
|
264
|
+
function webSocketProtocols(headers: JsonValue | undefined): string[] | undefined {
|
|
265
|
+
if (!Array.isArray(headers)) {
|
|
266
|
+
return undefined;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const protocols = headers.flatMap(header => {
|
|
270
|
+
if (
|
|
271
|
+
!isWireHeader(header) ||
|
|
272
|
+
header.name.toLowerCase() !== "sec-websocket-protocol"
|
|
273
|
+
) {
|
|
274
|
+
return [];
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return header.value
|
|
278
|
+
.split(",")
|
|
279
|
+
.map(protocol => protocol.trim())
|
|
280
|
+
.filter(protocol => protocol !== "");
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
return protocols.length === 0 ? undefined : protocols;
|
|
284
|
+
}
|
|
285
|
+
|
|
263
286
|
function localForwardUrl(
|
|
264
287
|
scheme: "http" | "https",
|
|
265
288
|
port: number,
|
package/src/protocol.ts
CHANGED
|
@@ -37,6 +37,11 @@
|
|
|
37
37
|
Spec: plans/2026-04-26-local-runner-subdomain-forwarding-epr.md
|
|
38
38
|
Relationship: Defines the typed WebSocket forwarding controls used by
|
|
39
39
|
isolated preview subdomains.
|
|
40
|
+
|
|
41
|
+
- Date: 2026-05-02
|
|
42
|
+
Spec: plans/2026-05-02-agent-first-zero-to-codex-launch-plan.md
|
|
43
|
+
Relationship: Keeps the npm CLI runner protocol in parity with the legacy
|
|
44
|
+
runner for runtime session setting updates.
|
|
40
45
|
*/
|
|
41
46
|
export type JsonValue =
|
|
42
47
|
| null
|
|
@@ -182,6 +187,16 @@ export type KandanControl =
|
|
|
182
187
|
readonly requestId: string;
|
|
183
188
|
readonly decision: "approve" | "deny";
|
|
184
189
|
}
|
|
190
|
+
| {
|
|
191
|
+
readonly type: "update_session_settings";
|
|
192
|
+
readonly instanceId?: string;
|
|
193
|
+
readonly threadId: string;
|
|
194
|
+
readonly model?: string | null;
|
|
195
|
+
readonly reasoningEffort?: string | null;
|
|
196
|
+
readonly approvalPolicy?: string | null;
|
|
197
|
+
readonly sandbox?: string | null;
|
|
198
|
+
readonly fast?: boolean;
|
|
199
|
+
}
|
|
185
200
|
| {
|
|
186
201
|
readonly type: "resolve_port_forward_request";
|
|
187
202
|
readonly instanceId: string;
|
package/src/runner.ts
CHANGED
|
@@ -211,6 +211,21 @@ async function openLocalCodexRunner(
|
|
|
211
211
|
channel: options.channelSession?.channelSlug ?? null,
|
|
212
212
|
capabilities: capabilitiesPayload(),
|
|
213
213
|
});
|
|
214
|
+
|
|
215
|
+
const pendingControls: KandanControl[] = [];
|
|
216
|
+
const controlDispatcher: {
|
|
217
|
+
value: ((control: KandanControl) => void) | undefined;
|
|
218
|
+
} = { value: undefined };
|
|
219
|
+
kandan.onControl((control) => {
|
|
220
|
+
const dispatcher = controlDispatcher.value;
|
|
221
|
+
if (dispatcher === undefined) {
|
|
222
|
+
pendingControls.push(control);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
dispatcher(control);
|
|
227
|
+
});
|
|
228
|
+
|
|
214
229
|
await kandan.join(topic, joinPayload(), { rejoinPayload: joinPayload });
|
|
215
230
|
|
|
216
231
|
const started =
|
|
@@ -431,7 +446,7 @@ async function openLocalCodexRunner(
|
|
|
431
446
|
channelSession?.handleCodexNotification(notification.method, params);
|
|
432
447
|
});
|
|
433
448
|
|
|
434
|
-
|
|
449
|
+
const handleControl = (control: KandanControl) => {
|
|
435
450
|
log("kandan.control", { control });
|
|
436
451
|
if (!controlTargetsInstance(control, instanceId)) {
|
|
437
452
|
log("kandan.control_ignored", {
|
|
@@ -584,7 +599,10 @@ async function openLocalCodexRunner(
|
|
|
584
599
|
message: error instanceof Error ? error.message : String(error),
|
|
585
600
|
});
|
|
586
601
|
});
|
|
587
|
-
}
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
controlDispatcher.value = handleControl;
|
|
605
|
+
pendingControls.splice(0).forEach(handleControl);
|
|
588
606
|
|
|
589
607
|
return { instanceId, codexUrl, close };
|
|
590
608
|
}
|
|
@@ -670,7 +688,7 @@ function normalizedWorkDescription(value: string | undefined): string | undefine
|
|
|
670
688
|
|
|
671
689
|
function makeRunnerLogger(options: RunnerOptions): RunnerLogger {
|
|
672
690
|
return createRunnerLogger(
|
|
673
|
-
options.logFile ?? join(options.cwd, ".
|
|
691
|
+
options.logFile ?? join(options.cwd, ".linzumi-runner.log"),
|
|
674
692
|
options.launchTui ? undefined : reportRunnerConsoleEvent,
|
|
675
693
|
);
|
|
676
694
|
}
|