lunel-cli 0.1.80 → 0.1.82
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.d.ts +4 -1
- package/dist/ai/codex.js +21 -1
- package/dist/ai/index.d.ts +3 -1
- package/dist/ai/interface.d.ts +3 -1
- package/dist/ai/opencode.d.ts +3 -1
- package/dist/ai/opencode.js +1 -3
- package/dist/index.js +112 -10
- package/package.json +1 -1
package/dist/ai/codex.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export declare class CodexProvider implements AIProvider {
|
|
|
6
6
|
private nextId;
|
|
7
7
|
private pending;
|
|
8
8
|
private sessions;
|
|
9
|
+
private deletedThreadIds;
|
|
9
10
|
private resumedThreadIds;
|
|
10
11
|
private pendingPermissionRequestIds;
|
|
11
12
|
private assistantMessageIdByTurnId;
|
|
@@ -22,7 +23,9 @@ export declare class CodexProvider implements AIProvider {
|
|
|
22
23
|
getSession(id: string): Promise<{
|
|
23
24
|
session: SessionInfo;
|
|
24
25
|
}>;
|
|
25
|
-
deleteSession(id: string): Promise<
|
|
26
|
+
deleteSession(id: string): Promise<{
|
|
27
|
+
deleted: boolean;
|
|
28
|
+
}>;
|
|
26
29
|
getMessages(sessionId: string): Promise<{
|
|
27
30
|
messages: MessageInfo[];
|
|
28
31
|
}>;
|
package/dist/ai/codex.js
CHANGED
|
@@ -13,6 +13,7 @@ export class CodexProvider {
|
|
|
13
13
|
nextId = 1;
|
|
14
14
|
pending = new Map();
|
|
15
15
|
sessions = new Map();
|
|
16
|
+
deletedThreadIds = new Set();
|
|
16
17
|
resumedThreadIds = new Set();
|
|
17
18
|
pendingPermissionRequestIds = new Map();
|
|
18
19
|
assistantMessageIdByTurnId = new Map();
|
|
@@ -81,6 +82,8 @@ export class CodexProvider {
|
|
|
81
82
|
}
|
|
82
83
|
this.reconcileSessionsWithServer(activeThreads, archivedThreads);
|
|
83
84
|
const sessions = Array.from(this.sessions.values())
|
|
85
|
+
.filter((session) => !session.archived)
|
|
86
|
+
.filter((session) => !this.deletedThreadIds.has(session.id))
|
|
84
87
|
.filter((session) => this.belongsToCurrentRoot(session))
|
|
85
88
|
.sort((a, b) => a.updatedAt - b.updatedAt)
|
|
86
89
|
.map((session) => this.toSessionInfo(session));
|
|
@@ -99,8 +102,21 @@ export class CodexProvider {
|
|
|
99
102
|
return { session: this.toSessionInfo(next) };
|
|
100
103
|
}
|
|
101
104
|
async deleteSession(id) {
|
|
105
|
+
const session = this.sessions.get(id);
|
|
106
|
+
this.deletedThreadIds.add(id);
|
|
102
107
|
this.sessions.delete(id);
|
|
103
|
-
|
|
108
|
+
this.resumedThreadIds.delete(id);
|
|
109
|
+
try {
|
|
110
|
+
const params = { threadId: id };
|
|
111
|
+
if (session?.cwd) {
|
|
112
|
+
params.cwd = session.cwd;
|
|
113
|
+
}
|
|
114
|
+
await this.call("thread/archive", params);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
// Match Remodex behavior: delete is optimistic locally, archive is best effort.
|
|
118
|
+
}
|
|
119
|
+
return { deleted: true };
|
|
104
120
|
}
|
|
105
121
|
async getMessages(sessionId) {
|
|
106
122
|
const session = this.ensureLocalSession(sessionId);
|
|
@@ -783,10 +799,14 @@ export class CodexProvider {
|
|
|
783
799
|
const localSessions = this.sessions;
|
|
784
800
|
const merged = new Map();
|
|
785
801
|
for (const thread of activeThreads) {
|
|
802
|
+
if (this.deletedThreadIds.has(thread.id))
|
|
803
|
+
continue;
|
|
786
804
|
const session = this.mergeSession(localSessions.get(thread.id), { ...thread, archived: false });
|
|
787
805
|
merged.set(thread.id, session);
|
|
788
806
|
}
|
|
789
807
|
for (const thread of archivedThreads) {
|
|
808
|
+
if (this.deletedThreadIds.has(thread.id))
|
|
809
|
+
continue;
|
|
790
810
|
if (merged.has(thread.id))
|
|
791
811
|
continue;
|
|
792
812
|
const session = this.mergeSession(localSessions.get(thread.id), { ...thread, archived: true });
|
package/dist/ai/index.d.ts
CHANGED
|
@@ -20,7 +20,9 @@ export declare class AiManager {
|
|
|
20
20
|
getSession(backend: AiBackend, id: string): Promise<{
|
|
21
21
|
session: import("./interface.js").SessionInfo;
|
|
22
22
|
}>;
|
|
23
|
-
deleteSession(backend: AiBackend, id: string): Promise<
|
|
23
|
+
deleteSession(backend: AiBackend, id: string): Promise<{
|
|
24
|
+
deleted: boolean;
|
|
25
|
+
}>;
|
|
24
26
|
getMessages(backend: AiBackend, sessionId: string): Promise<{
|
|
25
27
|
messages: import("./interface.js").MessageInfo[];
|
|
26
28
|
}>;
|
package/dist/ai/interface.d.ts
CHANGED
|
@@ -53,7 +53,9 @@ export interface AIProvider {
|
|
|
53
53
|
getSession(id: string): Promise<{
|
|
54
54
|
session: SessionInfo;
|
|
55
55
|
}>;
|
|
56
|
-
deleteSession(id: string): Promise<
|
|
56
|
+
deleteSession(id: string): Promise<{
|
|
57
|
+
deleted: boolean;
|
|
58
|
+
}>;
|
|
57
59
|
getMessages(sessionId: string): Promise<{
|
|
58
60
|
messages: MessageInfo[];
|
|
59
61
|
}>;
|
package/dist/ai/opencode.d.ts
CHANGED
|
@@ -21,7 +21,9 @@ export declare class OpenCodeProvider implements AIProvider {
|
|
|
21
21
|
getSession(id: string): Promise<{
|
|
22
22
|
session: SessionInfo;
|
|
23
23
|
}>;
|
|
24
|
-
deleteSession(id: string): Promise<
|
|
24
|
+
deleteSession(id: string): Promise<{
|
|
25
|
+
deleted: boolean;
|
|
26
|
+
}>;
|
|
25
27
|
getMessages(sessionId: string): Promise<{
|
|
26
28
|
messages: MessageInfo[];
|
|
27
29
|
}>;
|
package/dist/ai/opencode.js
CHANGED
|
@@ -108,9 +108,7 @@ export class OpenCodeProvider {
|
|
|
108
108
|
}
|
|
109
109
|
async deleteSession(id) {
|
|
110
110
|
const response = await this.client.session.delete({ path: { id } });
|
|
111
|
-
|
|
112
|
-
throw new Error(JSON.stringify(response.error));
|
|
113
|
-
return {};
|
|
111
|
+
return { deleted: Boolean(requireData(response, "session.delete")) };
|
|
114
112
|
}
|
|
115
113
|
// -------------------------------------------------------------------------
|
|
116
114
|
// Messages
|
package/dist/index.js
CHANGED
|
@@ -216,6 +216,7 @@ function hasFlag(args, flag) {
|
|
|
216
216
|
}
|
|
217
217
|
const EXTRA_PORTS = parseExtraPortsFromArgs(CLI_ARGS);
|
|
218
218
|
const USE_APPLE_REVIEW_CODE = hasFlag(CLI_ARGS, "--abcd-code");
|
|
219
|
+
const FORCE_NEW_CODE = hasFlag(CLI_ARGS, "--new-code");
|
|
219
220
|
const SCAN_PORTS = Array.from(new Set([...DEV_PORTS, ...EXTRA_PORTS])).sort((a, b) => a - b);
|
|
220
221
|
function samePortSet(a, b) {
|
|
221
222
|
if (a.length !== b.length)
|
|
@@ -303,18 +304,31 @@ async function readCliConfig() {
|
|
|
303
304
|
return {
|
|
304
305
|
version: 1,
|
|
305
306
|
deviceId: typeof parsed.deviceId === "string" && parsed.deviceId ? parsed.deviceId : generatePersistentSecret(32),
|
|
307
|
+
sessions: Array.isArray(parsed.sessions)
|
|
308
|
+
? parsed.sessions.filter((entry) => (!!entry
|
|
309
|
+
&& typeof entry.rootDir === "string"
|
|
310
|
+
&& typeof entry.sessionPassword === "string"
|
|
311
|
+
&& typeof entry.savedAt === "number")).map((entry) => ({
|
|
312
|
+
rootDir: entry.rootDir,
|
|
313
|
+
sessionCode: typeof entry.sessionCode === "string" ? entry.sessionCode : null,
|
|
314
|
+
sessionPassword: entry.sessionPassword,
|
|
315
|
+
savedAt: entry.savedAt,
|
|
316
|
+
}))
|
|
317
|
+
: [],
|
|
306
318
|
};
|
|
307
319
|
}
|
|
308
320
|
catch {
|
|
309
321
|
return {
|
|
310
322
|
version: 1,
|
|
311
323
|
deviceId: generatePersistentSecret(32),
|
|
324
|
+
sessions: [],
|
|
312
325
|
};
|
|
313
326
|
}
|
|
314
327
|
}
|
|
315
328
|
async function writeCliConfig(config) {
|
|
316
329
|
await fs.mkdir(path.dirname(CLI_CONFIG_PATH), { recursive: true });
|
|
317
330
|
await fs.writeFile(CLI_CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
|
|
331
|
+
cliConfigPromise = Promise.resolve(config);
|
|
318
332
|
}
|
|
319
333
|
let cliConfigPromise = null;
|
|
320
334
|
async function getCliConfig() {
|
|
@@ -323,6 +337,34 @@ async function getCliConfig() {
|
|
|
323
337
|
}
|
|
324
338
|
return await cliConfigPromise;
|
|
325
339
|
}
|
|
340
|
+
function getSavedSessionForRoot(config, rootDir) {
|
|
341
|
+
const sessions = Array.isArray(config.sessions) ? config.sessions : [];
|
|
342
|
+
return sessions.find((entry) => entry.rootDir === rootDir) || null;
|
|
343
|
+
}
|
|
344
|
+
async function saveSessionForRoot(sessionCode, sessionPassword) {
|
|
345
|
+
const config = await getCliConfig();
|
|
346
|
+
const sessions = Array.isArray(config.sessions) ? [...config.sessions] : [];
|
|
347
|
+
const nextEntry = {
|
|
348
|
+
rootDir: ROOT_DIR,
|
|
349
|
+
sessionCode,
|
|
350
|
+
sessionPassword,
|
|
351
|
+
savedAt: Date.now(),
|
|
352
|
+
};
|
|
353
|
+
const deduped = sessions.filter((entry) => entry.rootDir !== ROOT_DIR);
|
|
354
|
+
deduped.unshift(nextEntry);
|
|
355
|
+
await writeCliConfig({
|
|
356
|
+
...config,
|
|
357
|
+
sessions: deduped.slice(0, 100),
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
async function clearSavedSessionForRoot() {
|
|
361
|
+
const config = await getCliConfig();
|
|
362
|
+
const sessions = Array.isArray(config.sessions) ? config.sessions : [];
|
|
363
|
+
await writeCliConfig({
|
|
364
|
+
...config,
|
|
365
|
+
sessions: sessions.filter((entry) => entry.rootDir !== ROOT_DIR),
|
|
366
|
+
});
|
|
367
|
+
}
|
|
326
368
|
// ============================================================================
|
|
327
369
|
// File System Handlers
|
|
328
370
|
// ============================================================================
|
|
@@ -2596,7 +2638,20 @@ async function getAssignedProxyUrl(password) {
|
|
|
2596
2638
|
url.searchParams.set("password", password);
|
|
2597
2639
|
const response = await fetch(url);
|
|
2598
2640
|
if (!response.ok) {
|
|
2599
|
-
|
|
2641
|
+
let message = `Failed to get proxy from manager: ${response.status}`;
|
|
2642
|
+
try {
|
|
2643
|
+
const payload = await response.json();
|
|
2644
|
+
if (payload.error) {
|
|
2645
|
+
message = payload.error;
|
|
2646
|
+
}
|
|
2647
|
+
else if (payload.reason) {
|
|
2648
|
+
message = payload.reason;
|
|
2649
|
+
}
|
|
2650
|
+
}
|
|
2651
|
+
catch {
|
|
2652
|
+
// ignore parse failures and use the fallback message
|
|
2653
|
+
}
|
|
2654
|
+
throw new Error(message);
|
|
2600
2655
|
}
|
|
2601
2656
|
const payload = await response.json();
|
|
2602
2657
|
if (typeof payload.proxyUrl !== "string" || !payload.proxyUrl) {
|
|
@@ -2604,6 +2659,30 @@ async function getAssignedProxyUrl(password) {
|
|
|
2604
2659
|
}
|
|
2605
2660
|
return normalizeGatewayUrl(payload.proxyUrl);
|
|
2606
2661
|
}
|
|
2662
|
+
async function revokePassword(password, reason = "revoked by cli --new-code") {
|
|
2663
|
+
const response = await fetch(new URL("/v2/revoke", MANAGER_URL), {
|
|
2664
|
+
method: "POST",
|
|
2665
|
+
headers: { "Content-Type": "application/json" },
|
|
2666
|
+
body: JSON.stringify({ password, reason }),
|
|
2667
|
+
});
|
|
2668
|
+
if (response.ok || response.status === 404) {
|
|
2669
|
+
return;
|
|
2670
|
+
}
|
|
2671
|
+
let message = `Failed to revoke previous session: ${response.status}`;
|
|
2672
|
+
try {
|
|
2673
|
+
const payload = await response.json();
|
|
2674
|
+
if (payload.error) {
|
|
2675
|
+
message = payload.error;
|
|
2676
|
+
}
|
|
2677
|
+
else if (payload.reason) {
|
|
2678
|
+
message = payload.reason;
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
catch {
|
|
2682
|
+
// ignore parse failures and use the fallback message
|
|
2683
|
+
}
|
|
2684
|
+
throw new Error(message);
|
|
2685
|
+
}
|
|
2607
2686
|
function displayQR(code) {
|
|
2608
2687
|
console.log("\n");
|
|
2609
2688
|
qrcode.generate(code, { small: true }, (qr) => {
|
|
@@ -2875,8 +2954,10 @@ async function main() {
|
|
|
2875
2954
|
if (EXTRA_PORTS.length > 0) {
|
|
2876
2955
|
console.log(`Extra ports enabled: ${EXTRA_PORTS.join(", ")}`);
|
|
2877
2956
|
}
|
|
2957
|
+
let usedSavedSession = false;
|
|
2878
2958
|
try {
|
|
2879
|
-
await getCliConfig();
|
|
2959
|
+
const cliConfig = await getCliConfig();
|
|
2960
|
+
const savedSession = getSavedSessionForRoot(cliConfig, ROOT_DIR);
|
|
2880
2961
|
console.log("Checking PTY runtime...");
|
|
2881
2962
|
await ensurePtyBinaryReady();
|
|
2882
2963
|
console.log("PTY runtime ready.\n");
|
|
@@ -2895,27 +2976,48 @@ async function main() {
|
|
|
2895
2976
|
checkDataChannelBackpressure();
|
|
2896
2977
|
}
|
|
2897
2978
|
});
|
|
2898
|
-
let
|
|
2979
|
+
let sessionCodeToUse = null;
|
|
2980
|
+
let sessionPasswordToUse;
|
|
2899
2981
|
if (USE_APPLE_REVIEW_CODE) {
|
|
2900
|
-
codeToAssemble = APPLE_REVIEW_CODE;
|
|
2901
|
-
currentSessionCode = codeToAssemble;
|
|
2902
2982
|
console.log(`Using fixed review code: ${APPLE_REVIEW_CODE}`);
|
|
2983
|
+
const assembled = await assembleWithCode(APPLE_REVIEW_CODE);
|
|
2984
|
+
sessionCodeToUse = assembled.code;
|
|
2985
|
+
sessionPasswordToUse = assembled.password;
|
|
2986
|
+
await saveSessionForRoot(sessionCodeToUse, sessionPasswordToUse);
|
|
2987
|
+
}
|
|
2988
|
+
else if (!FORCE_NEW_CODE && savedSession) {
|
|
2989
|
+
console.log(`Using saved session for ${ROOT_DIR}`);
|
|
2990
|
+
sessionCodeToUse = savedSession.sessionCode;
|
|
2991
|
+
sessionPasswordToUse = savedSession.sessionPassword;
|
|
2992
|
+
usedSavedSession = true;
|
|
2903
2993
|
}
|
|
2904
2994
|
else {
|
|
2995
|
+
if (FORCE_NEW_CODE && savedSession?.sessionPassword) {
|
|
2996
|
+
await revokePassword(savedSession.sessionPassword);
|
|
2997
|
+
await clearSavedSessionForRoot();
|
|
2998
|
+
}
|
|
2905
2999
|
const qr = await createQrCode();
|
|
2906
|
-
codeToAssemble = qr.code;
|
|
2907
3000
|
currentSessionCode = qr.code;
|
|
2908
3001
|
displayQR(qr.code);
|
|
3002
|
+
const assembled = await assembleWithCode(qr.code);
|
|
3003
|
+
sessionCodeToUse = assembled.code;
|
|
3004
|
+
sessionPasswordToUse = assembled.password;
|
|
3005
|
+
await saveSessionForRoot(sessionCodeToUse, sessionPasswordToUse);
|
|
2909
3006
|
}
|
|
2910
|
-
const assembled = await assembleWithCode(codeToAssemble);
|
|
2911
3007
|
resetReplayBuffer();
|
|
2912
|
-
currentSessionCode =
|
|
2913
|
-
currentSessionPassword =
|
|
2914
|
-
currentPrimaryGateway = await getAssignedProxyUrl(
|
|
3008
|
+
currentSessionCode = sessionCodeToUse;
|
|
3009
|
+
currentSessionPassword = sessionPasswordToUse;
|
|
3010
|
+
currentPrimaryGateway = await getAssignedProxyUrl(sessionPasswordToUse);
|
|
2915
3011
|
activeGatewayUrl = currentPrimaryGateway;
|
|
2916
3012
|
await connectWebSocket();
|
|
2917
3013
|
}
|
|
2918
3014
|
catch (error) {
|
|
3015
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3016
|
+
if (!USE_APPLE_REVIEW_CODE &&
|
|
3017
|
+
usedSavedSession &&
|
|
3018
|
+
/invalid|revoked|not found|expired|password invalid|password revoked/i.test(message)) {
|
|
3019
|
+
await clearSavedSessionForRoot().catch(() => { });
|
|
3020
|
+
}
|
|
2919
3021
|
if (error instanceof Error) {
|
|
2920
3022
|
console.error(`Error: ${error.message}`);
|
|
2921
3023
|
if (error.stack)
|