lunel-cli 0.1.96 → 0.1.98
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/ai/codex.js +19 -1
- package/dist/index.js +55 -2
- package/dist/transport/protocol.d.ts +1 -1
- package/dist/transport/protocol.js +4 -1
- package/dist/transport/v2.d.ts +1 -0
- package/dist/transport/v2.js +1 -1
- package/package.json +1 -1
package/dist/ai/codex.js
CHANGED
|
@@ -7,6 +7,21 @@ import { spawn } from "child_process";
|
|
|
7
7
|
import { createInterface } from "readline";
|
|
8
8
|
const THREAD_LIST_SOURCE_KINDS = ["cli", "vscode", "appServer", "exec", "unknown"];
|
|
9
9
|
const DEBUG_MODE = process.env.LUNEL_DEBUG === "1" || process.env.LUNEL_DEBUG_AI === "1";
|
|
10
|
+
function joinStreamingText(previousText, nextChunk) {
|
|
11
|
+
if (!previousText) {
|
|
12
|
+
return nextChunk;
|
|
13
|
+
}
|
|
14
|
+
if (/[\s]$/.test(previousText) || /^[\s]/.test(nextChunk)) {
|
|
15
|
+
return `${previousText}${nextChunk}`;
|
|
16
|
+
}
|
|
17
|
+
if (/^[.,!?;:)\]%}"'`]/.test(nextChunk)) {
|
|
18
|
+
return `${previousText}${nextChunk}`;
|
|
19
|
+
}
|
|
20
|
+
if (/[(\[{/"'`]$/.test(previousText)) {
|
|
21
|
+
return `${previousText}${nextChunk}`;
|
|
22
|
+
}
|
|
23
|
+
return `${previousText} ${nextChunk}`;
|
|
24
|
+
}
|
|
10
25
|
export class CodexProvider {
|
|
11
26
|
proc = null;
|
|
12
27
|
shuttingDown = false;
|
|
@@ -557,7 +572,10 @@ export class CodexProvider {
|
|
|
557
572
|
const nextChunk = this.extractTextPayload(params);
|
|
558
573
|
if (!nextChunk)
|
|
559
574
|
return;
|
|
560
|
-
const
|
|
575
|
+
const previousText = this.partTextById.get(partKey) ?? "";
|
|
576
|
+
const nextText = preferWholeText
|
|
577
|
+
? nextChunk
|
|
578
|
+
: joinStreamingText(previousText, nextChunk);
|
|
561
579
|
this.partTextById.set(partKey, nextText);
|
|
562
580
|
session.updatedAt = Date.now();
|
|
563
581
|
this.upsertLocalMessagePart(session, messageId, {
|
package/dist/index.js
CHANGED
|
@@ -2754,6 +2754,7 @@ async function processMessage(message) {
|
|
|
2754
2754
|
};
|
|
2755
2755
|
}
|
|
2756
2756
|
}
|
|
2757
|
+
let currentReattachGeneration = null;
|
|
2757
2758
|
function normalizeGatewayUrl(input) {
|
|
2758
2759
|
const raw = input.trim();
|
|
2759
2760
|
if (!raw) {
|
|
@@ -2853,6 +2854,47 @@ async function getAssignedProxyUrl(password) {
|
|
|
2853
2854
|
}
|
|
2854
2855
|
return normalizeGatewayUrl(payload.proxyUrl);
|
|
2855
2856
|
}
|
|
2857
|
+
async function claimReattach(password) {
|
|
2858
|
+
const response = await fetch(new URL("/v2/reattach/claim", MANAGER_URL), {
|
|
2859
|
+
method: "POST",
|
|
2860
|
+
headers: { "Content-Type": "application/json" },
|
|
2861
|
+
body: JSON.stringify({
|
|
2862
|
+
password,
|
|
2863
|
+
role: "cli",
|
|
2864
|
+
}),
|
|
2865
|
+
});
|
|
2866
|
+
if (!response.ok) {
|
|
2867
|
+
let message = `Failed to claim reattach from manager: ${response.status}`;
|
|
2868
|
+
try {
|
|
2869
|
+
const payload = await response.json();
|
|
2870
|
+
if (payload.error) {
|
|
2871
|
+
message = payload.error;
|
|
2872
|
+
}
|
|
2873
|
+
else if (payload.reason) {
|
|
2874
|
+
message = payload.reason;
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
catch {
|
|
2878
|
+
// ignore parse failures and use the fallback message
|
|
2879
|
+
}
|
|
2880
|
+
throw new Error(message);
|
|
2881
|
+
}
|
|
2882
|
+
const payload = await response.json();
|
|
2883
|
+
if (typeof payload.proxyUrl !== "string" || !payload.proxyUrl) {
|
|
2884
|
+
throw new Error("Manager returned invalid reattach proxy assignment");
|
|
2885
|
+
}
|
|
2886
|
+
if (typeof payload.generation !== "number" || !Number.isFinite(payload.generation) || payload.generation < 1) {
|
|
2887
|
+
throw new Error("Manager returned invalid reattach generation");
|
|
2888
|
+
}
|
|
2889
|
+
if (typeof payload.expiresAt !== "number" || !Number.isFinite(payload.expiresAt)) {
|
|
2890
|
+
throw new Error("Manager returned invalid reattach expiry");
|
|
2891
|
+
}
|
|
2892
|
+
return {
|
|
2893
|
+
proxyUrl: normalizeGatewayUrl(payload.proxyUrl),
|
|
2894
|
+
generation: payload.generation,
|
|
2895
|
+
expiresAt: payload.expiresAt,
|
|
2896
|
+
};
|
|
2897
|
+
}
|
|
2856
2898
|
async function revokePassword(password, reason = "revoked by cli --new") {
|
|
2857
2899
|
const response = await fetch(new URL("/v2/revoke", MANAGER_URL), {
|
|
2858
2900
|
method: "POST",
|
|
@@ -2924,6 +2966,7 @@ async function connectWebSocketV2() {
|
|
|
2924
2966
|
gatewayUrl,
|
|
2925
2967
|
password: currentSessionPassword,
|
|
2926
2968
|
sessionSecret: currentSessionPassword,
|
|
2969
|
+
generation: currentReattachGeneration,
|
|
2927
2970
|
role: "cli",
|
|
2928
2971
|
debugLog: DEBUG_MODE ? debugLog : undefined,
|
|
2929
2972
|
handlers: {
|
|
@@ -2998,7 +3041,9 @@ async function handleConnectionDrop(reason) {
|
|
|
2998
3041
|
const base = Math.min(250 * 2 ** (attempt - 1), 30_000);
|
|
2999
3042
|
const delayMs = Math.round(base * (0.8 + Math.random() * 0.4));
|
|
3000
3043
|
try {
|
|
3001
|
-
|
|
3044
|
+
const reattach = await claimReattach(currentSessionPassword);
|
|
3045
|
+
currentPrimaryGateway = reattach.proxyUrl;
|
|
3046
|
+
currentReattachGeneration = reattach.generation;
|
|
3002
3047
|
await connectWebSocketV2();
|
|
3003
3048
|
debugLog(`[reconnect] connected via ${activeGatewayUrl}`);
|
|
3004
3049
|
return;
|
|
@@ -3069,7 +3114,15 @@ async function main() {
|
|
|
3069
3114
|
}
|
|
3070
3115
|
currentSessionCode = sessionCodeToUse;
|
|
3071
3116
|
currentSessionPassword = sessionPasswordToUse;
|
|
3072
|
-
|
|
3117
|
+
if (usedSavedSession) {
|
|
3118
|
+
const reattach = await claimReattach(sessionPasswordToUse);
|
|
3119
|
+
currentPrimaryGateway = reattach.proxyUrl;
|
|
3120
|
+
currentReattachGeneration = reattach.generation;
|
|
3121
|
+
}
|
|
3122
|
+
else {
|
|
3123
|
+
currentPrimaryGateway = await getAssignedProxyUrl(sessionPasswordToUse);
|
|
3124
|
+
currentReattachGeneration = null;
|
|
3125
|
+
}
|
|
3073
3126
|
activeGatewayUrl = currentPrimaryGateway;
|
|
3074
3127
|
await connectWebSocketV2();
|
|
3075
3128
|
}
|
|
@@ -74,4 +74,4 @@ export declare function decodeV2BinaryFrame(data: Uint8Array): {
|
|
|
74
74
|
type: number;
|
|
75
75
|
payload: Uint8Array;
|
|
76
76
|
} | null;
|
|
77
|
-
export declare function buildSessionV2WsUrl(gatewayUrl: string, role: "cli" | "app", password: string): string;
|
|
77
|
+
export declare function buildSessionV2WsUrl(gatewayUrl: string, role: "cli" | "app", password: string, generation?: number | null): string;
|
|
@@ -66,11 +66,14 @@ export function decodeV2BinaryFrame(data) {
|
|
|
66
66
|
payload: data.subarray(3),
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
|
-
export function buildSessionV2WsUrl(gatewayUrl, role, password) {
|
|
69
|
+
export function buildSessionV2WsUrl(gatewayUrl, role, password, generation) {
|
|
70
70
|
const wsBase = gatewayUrl.replace(/^https:/, "wss:");
|
|
71
71
|
if (!wsBase.startsWith("wss://")) {
|
|
72
72
|
throw new Error("Gateway URL must use https://");
|
|
73
73
|
}
|
|
74
74
|
const query = new URLSearchParams({ password });
|
|
75
|
+
if (typeof generation === "number" && Number.isFinite(generation) && generation > 0) {
|
|
76
|
+
query.set("generation", String(generation));
|
|
77
|
+
}
|
|
75
78
|
return `${wsBase}/v2/ws/${role}?${query.toString()}`;
|
|
76
79
|
}
|
package/dist/transport/v2.d.ts
CHANGED
package/dist/transport/v2.js
CHANGED
|
@@ -44,7 +44,7 @@ export class V2SessionTransport {
|
|
|
44
44
|
this.secureReadyResolve = resolve;
|
|
45
45
|
this.secureReadyReject = reject;
|
|
46
46
|
});
|
|
47
|
-
const wsUrl = buildSessionV2WsUrl(this.options.gatewayUrl, this.options.role, this.options.password);
|
|
47
|
+
const wsUrl = buildSessionV2WsUrl(this.options.gatewayUrl, this.options.role, this.options.password, this.options.generation);
|
|
48
48
|
await new Promise((resolve, reject) => {
|
|
49
49
|
const ws = new WebSocket(wsUrl);
|
|
50
50
|
let opened = false;
|