@cookielab.io/klovi 3.4.0 → 3.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/dist/cli.js +493 -457
- package/dist/server.js +493 -457
- package/dist/web/{chunk-1kc2497n.js → chunk-87y4za2n.js} +177 -174
- package/dist/web/chunk-gky4vze0.css +1 -0
- package/dist/web/index.html +1 -1
- package/package.json +4 -4
- package/dist/web/chunk-psyp4t4q.css +0 -1
package/dist/cli.js
CHANGED
|
@@ -38,6 +38,69 @@ function epochSecondsToIso(epochSeconds) {
|
|
|
38
38
|
}
|
|
39
39
|
var MS_PER_SECOND = 1000;
|
|
40
40
|
|
|
41
|
+
// ../../packages/plugin-core/src/jsonl-stream.ts
|
|
42
|
+
import { FileSystem } from "@effect/platform";
|
|
43
|
+
import { Effect, Ref, Stream } from "effect";
|
|
44
|
+
function parseAndVisit(line, lineIndex, visitor, onMalformed) {
|
|
45
|
+
if (!line.trim()) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const lineNumber = lineIndex + 1;
|
|
49
|
+
try {
|
|
50
|
+
const parsed = JSON.parse(line);
|
|
51
|
+
return visitor({ parsed, line, lineIndex, lineNumber });
|
|
52
|
+
} catch (error) {
|
|
53
|
+
onMalformed?.(line, lineNumber, error);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function processLine(line, state) {
|
|
58
|
+
return Effect.gen(function* () {
|
|
59
|
+
if (yield* Ref.get(state.bailedRef)) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const lineIndex = yield* Ref.getAndUpdate(state.indexRef, (n) => n + 1);
|
|
63
|
+
const result = parseAndVisit(line, lineIndex, state.visitor, state.onMalformed);
|
|
64
|
+
if (result === false) {
|
|
65
|
+
yield* Ref.set(state.bailedRef, true);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
function streamJsonlHead(filePath, visitor, options = {}) {
|
|
70
|
+
return Effect.gen(function* () {
|
|
71
|
+
const fs = yield* FileSystem.FileSystem;
|
|
72
|
+
const indexRef = yield* Ref.make(0);
|
|
73
|
+
const bailedRef = yield* Ref.make(false);
|
|
74
|
+
const chunkSize = options.chunkSize ?? DEFAULT_HEAD_CHUNK_SIZE;
|
|
75
|
+
const baseStream = fs.stream(filePath, { chunkSize });
|
|
76
|
+
const linesStream = baseStream.pipe(Stream.decodeText("utf-8"), Stream.splitLines);
|
|
77
|
+
const cappedStream = options.maxLines === undefined ? linesStream : linesStream.pipe(Stream.take(options.maxLines));
|
|
78
|
+
const state = {
|
|
79
|
+
visitor,
|
|
80
|
+
indexRef,
|
|
81
|
+
bailedRef,
|
|
82
|
+
onMalformed: options.onMalformed
|
|
83
|
+
};
|
|
84
|
+
yield* cappedStream.pipe(Stream.takeUntilEffect(() => Ref.get(bailedRef)), Stream.runForEach((line) => processLine(line, state)));
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
function streamJsonl(filePath, visitor, options = {}) {
|
|
88
|
+
return Effect.gen(function* () {
|
|
89
|
+
const fs = yield* FileSystem.FileSystem;
|
|
90
|
+
const indexRef = yield* Ref.make(0);
|
|
91
|
+
const chunkSize = options.chunkSize ?? DEFAULT_FULL_CHUNK_SIZE;
|
|
92
|
+
yield* fs.stream(filePath, { chunkSize }).pipe(Stream.decodeText("utf-8"), Stream.splitLines, Stream.runForEach((line) => Effect.gen(function* () {
|
|
93
|
+
const lineIndex = yield* Ref.getAndUpdate(indexRef, (n) => n + 1);
|
|
94
|
+
parseAndVisit(line, lineIndex, visitor, options.onMalformed);
|
|
95
|
+
})));
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
var KILOBYTE_BYTES = 1024, DEFAULT_HEAD_CHUNK_KILOBYTES = 8, DEFAULT_HEAD_CHUNK_SIZE, DEFAULT_FULL_CHUNK_KILOBYTES = 64, DEFAULT_FULL_CHUNK_SIZE;
|
|
99
|
+
var init_jsonl_stream = __esm(() => {
|
|
100
|
+
DEFAULT_HEAD_CHUNK_SIZE = DEFAULT_HEAD_CHUNK_KILOBYTES * KILOBYTE_BYTES;
|
|
101
|
+
DEFAULT_FULL_CHUNK_SIZE = DEFAULT_FULL_CHUNK_KILOBYTES * KILOBYTE_BYTES;
|
|
102
|
+
});
|
|
103
|
+
|
|
41
104
|
// ../../packages/plugin-core/src/plugin-config.ts
|
|
42
105
|
import { Context } from "effect";
|
|
43
106
|
var PluginConfig;
|
|
@@ -56,8 +119,8 @@ var init_plugin_errors = __esm(() => {
|
|
|
56
119
|
|
|
57
120
|
// ../../packages/plugin-core/src/resolve-worktree.ts
|
|
58
121
|
import { join } from "node:path";
|
|
59
|
-
import { FileSystem } from "@effect/platform";
|
|
60
|
-
import { Effect } from "effect";
|
|
122
|
+
import { FileSystem as FileSystem2 } from "@effect/platform";
|
|
123
|
+
import { Effect as Effect2 } from "effect";
|
|
61
124
|
function stripT3CodeSuffix(path) {
|
|
62
125
|
if (!T3CODE_SUFFIX_REGEX.test(path)) {
|
|
63
126
|
return null;
|
|
@@ -68,14 +131,14 @@ function stripT3CodeSuffix(path) {
|
|
|
68
131
|
return { path: parentPath, projectName };
|
|
69
132
|
}
|
|
70
133
|
function resolveGitWorktree(worktreePath) {
|
|
71
|
-
return
|
|
72
|
-
const fs = yield*
|
|
134
|
+
return Effect2.gen(function* () {
|
|
135
|
+
const fs = yield* FileSystem2.FileSystem;
|
|
73
136
|
const dotGitPath = join(worktreePath, ".git");
|
|
74
|
-
const info = yield* fs.stat(dotGitPath).pipe(
|
|
137
|
+
const info = yield* fs.stat(dotGitPath).pipe(Effect2.catchAll(() => Effect2.succeed(null)));
|
|
75
138
|
if (!info || info.type !== "File") {
|
|
76
139
|
return worktreePath;
|
|
77
140
|
}
|
|
78
|
-
const content = yield* fs.readFileString(dotGitPath).pipe(
|
|
141
|
+
const content = yield* fs.readFileString(dotGitPath).pipe(Effect2.catchAll(() => Effect2.succeed("")));
|
|
79
142
|
if (!content) {
|
|
80
143
|
return worktreePath;
|
|
81
144
|
}
|
|
@@ -132,7 +195,7 @@ function buildNameMap(projects, t3codeProjects) {
|
|
|
132
195
|
return nameToResolvedPaths;
|
|
133
196
|
}
|
|
134
197
|
function resolveEntry(entry, nameMap) {
|
|
135
|
-
return
|
|
198
|
+
return Effect2.gen(function* () {
|
|
136
199
|
const candidates = nameMap.get(entry.projectName);
|
|
137
200
|
if (!candidates || candidates.length === 0) {
|
|
138
201
|
return;
|
|
@@ -151,7 +214,7 @@ function resolveEntry(entry, nameMap) {
|
|
|
151
214
|
});
|
|
152
215
|
}
|
|
153
216
|
function resolveT3CodePaths(projects) {
|
|
154
|
-
return
|
|
217
|
+
return Effect2.gen(function* () {
|
|
155
218
|
const t3codeEntries = collectT3CodeEntries(projects);
|
|
156
219
|
if (t3codeEntries.length === 0) {
|
|
157
220
|
return;
|
|
@@ -186,7 +249,7 @@ function parseSessionId(sessionId) {
|
|
|
186
249
|
var SESSION_ID_SEPARATOR = "::";
|
|
187
250
|
|
|
188
251
|
// ../../packages/plugin-core/src/plugin-registry.ts
|
|
189
|
-
import { Effect as
|
|
252
|
+
import { Effect as Effect3, Layer } from "effect";
|
|
190
253
|
function encodeResolvedPath(resolvedPath) {
|
|
191
254
|
if (resolvedPath.startsWith("/")) {
|
|
192
255
|
return resolvedPath.replace(/\//gu, "-");
|
|
@@ -225,22 +288,21 @@ class PluginRegistry {
|
|
|
225
288
|
return [...this.plugins.values()].map((entry) => entry.plugin);
|
|
226
289
|
}
|
|
227
290
|
discoverPluginStates(includeSessions) {
|
|
228
|
-
return
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
const discoveredIndex = includeSessions && entry.plugin.discoverIndex ? yield* entry.plugin.discoverIndex.pipe(
|
|
232
|
-
const projects = discoveredIndex?.projects ?? (yield* entry.plugin.discoverProjects.pipe(
|
|
233
|
-
|
|
291
|
+
return Effect3.gen(this, function* () {
|
|
292
|
+
const entries = [...this.plugins.values()];
|
|
293
|
+
return yield* Effect3.forEach(entries, (entry) => Effect3.gen(function* () {
|
|
294
|
+
const discoveredIndex = includeSessions && entry.plugin.discoverIndex ? yield* entry.plugin.discoverIndex.pipe(Effect3.provide(entry.configLayer), Effect3.catchAll(() => Effect3.succeed(undefined))) : undefined;
|
|
295
|
+
const projects = discoveredIndex?.projects ?? (yield* entry.plugin.discoverProjects.pipe(Effect3.provide(entry.configLayer), Effect3.catchAll(() => Effect3.succeed([]))));
|
|
296
|
+
return {
|
|
234
297
|
entry,
|
|
235
298
|
projects,
|
|
236
299
|
...discoveredIndex ? { sessionsByNativeId: discoveredIndex.sessionsByNativeId } : {}
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
return states;
|
|
300
|
+
};
|
|
301
|
+
}), { concurrency: "unbounded" });
|
|
240
302
|
});
|
|
241
303
|
}
|
|
242
304
|
mergeProjects(allProjects) {
|
|
243
|
-
return
|
|
305
|
+
return Effect3.gen(function* () {
|
|
244
306
|
yield* resolveT3CodePaths(allProjects);
|
|
245
307
|
const projectsByPath = new Map;
|
|
246
308
|
for (const project of allProjects) {
|
|
@@ -280,18 +342,18 @@ class PluginRegistry {
|
|
|
280
342
|
loadSourceSessions(state, source) {
|
|
281
343
|
const discoveredSessions = state.sessionsByNativeId?.get(source.nativeId);
|
|
282
344
|
if (discoveredSessions) {
|
|
283
|
-
return
|
|
345
|
+
return Effect3.succeed(this.encodeSessions(source.pluginId, discoveredSessions));
|
|
284
346
|
}
|
|
285
|
-
return state.entry.plugin.listSessions(source.nativeId).pipe(
|
|
347
|
+
return state.entry.plugin.listSessions(source.nativeId).pipe(Effect3.provide(state.entry.configLayer), Effect3.catchAll(() => Effect3.succeed([])), Effect3.map((sessions) => this.encodeSessions(source.pluginId, sessions)));
|
|
286
348
|
}
|
|
287
349
|
discoverAllProjects() {
|
|
288
|
-
return
|
|
350
|
+
return Effect3.gen(this, function* () {
|
|
289
351
|
const states = yield* this.discoverPluginStates(false);
|
|
290
352
|
return yield* this.mergeProjects(states.flatMap((state) => state.projects));
|
|
291
353
|
});
|
|
292
354
|
}
|
|
293
355
|
discoverAllProjectsWithSessions() {
|
|
294
|
-
return
|
|
356
|
+
return Effect3.gen(this, function* () {
|
|
295
357
|
const states = yield* this.discoverPluginStates(true);
|
|
296
358
|
const mergedProjects = yield* this.mergeProjects(states.flatMap((state) => state.projects));
|
|
297
359
|
const statesByPluginId = new Map(states.map((state) => [state.entry.plugin.id, state]));
|
|
@@ -315,14 +377,14 @@ class PluginRegistry {
|
|
|
315
377
|
});
|
|
316
378
|
}
|
|
317
379
|
listAllSessions(project) {
|
|
318
|
-
return
|
|
380
|
+
return Effect3.gen(this, function* () {
|
|
319
381
|
const allSessions = [];
|
|
320
382
|
for (const source of project.sources) {
|
|
321
383
|
const entry = this.plugins.get(source.pluginId);
|
|
322
384
|
if (!entry) {
|
|
323
385
|
continue;
|
|
324
386
|
}
|
|
325
|
-
allSessions.push(...yield* entry.plugin.listSessions(source.nativeId).pipe(
|
|
387
|
+
allSessions.push(...yield* entry.plugin.listSessions(source.nativeId).pipe(Effect3.provide(entry.configLayer), Effect3.catchAll(() => Effect3.succeed([])), Effect3.map((sessions) => this.encodeSessions(source.pluginId, sessions))));
|
|
326
388
|
}
|
|
327
389
|
sortByIsoDesc(allSessions, (session) => session.timestamp);
|
|
328
390
|
return allSessions;
|
|
@@ -353,6 +415,7 @@ var init_sqlite_service = __esm(() => {
|
|
|
353
415
|
|
|
354
416
|
// ../../packages/plugin-core/src/index.ts
|
|
355
417
|
var init_src = __esm(() => {
|
|
418
|
+
init_jsonl_stream();
|
|
356
419
|
init_plugin_config();
|
|
357
420
|
init_plugin_errors();
|
|
358
421
|
init_plugin_registry();
|
|
@@ -362,18 +425,18 @@ var init_src = __esm(() => {
|
|
|
362
425
|
|
|
363
426
|
// ../../packages/plugin-opencode/src/db.ts
|
|
364
427
|
import { join as join2 } from "node:path";
|
|
365
|
-
import { FileSystem as
|
|
366
|
-
import { Effect as
|
|
428
|
+
import { FileSystem as FileSystem3 } from "@effect/platform";
|
|
429
|
+
import { Effect as Effect4 } from "effect";
|
|
367
430
|
function getOpenCodeDbPath() {
|
|
368
|
-
return
|
|
431
|
+
return Effect4.gen(function* () {
|
|
369
432
|
const config = yield* PluginConfig;
|
|
370
433
|
return join2(config.dataDir, "opencode.db");
|
|
371
434
|
});
|
|
372
435
|
}
|
|
373
436
|
function openOpenCodeDb() {
|
|
374
|
-
return
|
|
437
|
+
return Effect4.gen(function* () {
|
|
375
438
|
const dbPath = yield* getOpenCodeDbPath();
|
|
376
|
-
const fs = yield*
|
|
439
|
+
const fs = yield* FileSystem3.FileSystem;
|
|
377
440
|
const exists = yield* fs.exists(dbPath);
|
|
378
441
|
if (!exists) {
|
|
379
442
|
return null;
|
|
@@ -396,7 +459,7 @@ function tryParseJson(value) {
|
|
|
396
459
|
}
|
|
397
460
|
|
|
398
461
|
// ../../packages/plugin-opencode/src/discovery.ts
|
|
399
|
-
import { Effect as
|
|
462
|
+
import { Effect as Effect5 } from "effect";
|
|
400
463
|
function getColumns(db, tableName) {
|
|
401
464
|
const rows = db.query(`PRAGMA table_info(${tableName})`).all();
|
|
402
465
|
return new Set(rows.map((r) => r.name));
|
|
@@ -417,7 +480,7 @@ function inspectSchema(db) {
|
|
|
417
480
|
};
|
|
418
481
|
}
|
|
419
482
|
function discoverOpenCodeProjects() {
|
|
420
|
-
return
|
|
483
|
+
return Effect5.gen(function* () {
|
|
421
484
|
const db = yield* openOpenCodeDb();
|
|
422
485
|
if (!db) {
|
|
423
486
|
return [];
|
|
@@ -508,7 +571,7 @@ function sessionRowToSummary(db, row) {
|
|
|
508
571
|
};
|
|
509
572
|
}
|
|
510
573
|
function listOpenCodeSessions(nativeId) {
|
|
511
|
-
return
|
|
574
|
+
return Effect5.gen(function* () {
|
|
512
575
|
const db = yield* openOpenCodeDb();
|
|
513
576
|
if (!db) {
|
|
514
577
|
return [];
|
|
@@ -575,7 +638,7 @@ var init_discovery = __esm(() => {
|
|
|
575
638
|
});
|
|
576
639
|
|
|
577
640
|
// ../../packages/plugin-opencode/src/parser.ts
|
|
578
|
-
import { Effect as
|
|
641
|
+
import { Effect as Effect6 } from "effect";
|
|
579
642
|
function partToContentBlock(partData, nextToolUseId) {
|
|
580
643
|
switch (partData.type) {
|
|
581
644
|
case "text": {
|
|
@@ -784,7 +847,7 @@ function loadSessionFromDb(db, nativeId, sessionId) {
|
|
|
784
847
|
return { sessionId, project, turns, pluginId: "opencode" };
|
|
785
848
|
}
|
|
786
849
|
function loadOpenCodeSession(nativeId, sessionId) {
|
|
787
|
-
return
|
|
850
|
+
return Effect6.gen(function* () {
|
|
788
851
|
const db = yield* openOpenCodeDb();
|
|
789
852
|
if (!db) {
|
|
790
853
|
return emptySession(nativeId, sessionId);
|
|
@@ -819,12 +882,12 @@ var init_config = __esm(() => {
|
|
|
819
882
|
});
|
|
820
883
|
|
|
821
884
|
// ../../packages/plugin-opencode/src/runtime/bun-sqlite.ts
|
|
822
|
-
import { Effect as
|
|
885
|
+
import { Effect as Effect7, Layer as Layer3 } from "effect";
|
|
823
886
|
var bunSqliteClient, BunSqliteLayer;
|
|
824
887
|
var init_bun_sqlite = __esm(() => {
|
|
825
888
|
init_src();
|
|
826
889
|
bunSqliteClient = {
|
|
827
|
-
open: (dbPath, options) =>
|
|
890
|
+
open: (dbPath, options) => Effect7.try({
|
|
828
891
|
try: () => {
|
|
829
892
|
const sqlite = __require("bun:sqlite");
|
|
830
893
|
return new sqlite.Database(dbPath, {
|
|
@@ -832,18 +895,18 @@ var init_bun_sqlite = __esm(() => {
|
|
|
832
895
|
});
|
|
833
896
|
},
|
|
834
897
|
catch: () => null
|
|
835
|
-
}).pipe(
|
|
898
|
+
}).pipe(Effect7.catchAll(() => Effect7.succeed(null)))
|
|
836
899
|
};
|
|
837
900
|
BunSqliteLayer = Layer3.succeed(SqliteClientTag, bunSqliteClient);
|
|
838
901
|
});
|
|
839
902
|
|
|
840
903
|
// ../../packages/plugin-opencode/src/runtime/node-sqlite.ts
|
|
841
|
-
import { Effect as
|
|
904
|
+
import { Effect as Effect8, Layer as Layer4 } from "effect";
|
|
842
905
|
var nodeSqliteClient, NodeSqliteLayer;
|
|
843
906
|
var init_node_sqlite = __esm(() => {
|
|
844
907
|
init_src();
|
|
845
908
|
nodeSqliteClient = {
|
|
846
|
-
open: (dbPath, options) =>
|
|
909
|
+
open: (dbPath, options) => Effect8.tryPromise({
|
|
847
910
|
try: async () => {
|
|
848
911
|
const sqlite = await import("node:sqlite");
|
|
849
912
|
const db = new sqlite.DatabaseSync(dbPath, {
|
|
@@ -864,15 +927,15 @@ var init_node_sqlite = __esm(() => {
|
|
|
864
927
|
};
|
|
865
928
|
},
|
|
866
929
|
catch: () => null
|
|
867
|
-
}).pipe(
|
|
930
|
+
}).pipe(Effect8.catchAll(() => Effect8.succeed(null)))
|
|
868
931
|
};
|
|
869
932
|
NodeSqliteLayer = Layer4.succeed(SqliteClientTag, nodeSqliteClient);
|
|
870
933
|
});
|
|
871
934
|
|
|
872
935
|
// ../../packages/plugin-opencode/src/index.ts
|
|
873
936
|
import { join as join4 } from "node:path";
|
|
874
|
-
import { FileSystem as
|
|
875
|
-
import { Effect as
|
|
937
|
+
import { FileSystem as FileSystem4 } from "@effect/platform";
|
|
938
|
+
import { Effect as Effect9 } from "effect";
|
|
876
939
|
var openCodePlugin;
|
|
877
940
|
var init_src2 = __esm(() => {
|
|
878
941
|
init_src();
|
|
@@ -885,24 +948,24 @@ var init_src2 = __esm(() => {
|
|
|
885
948
|
id: "opencode",
|
|
886
949
|
displayName: "OpenCode",
|
|
887
950
|
getDefaultDataDir: () => null,
|
|
888
|
-
isDataAvailable:
|
|
951
|
+
isDataAvailable: Effect9.gen(function* () {
|
|
889
952
|
const config = yield* PluginConfig;
|
|
890
|
-
const fs = yield*
|
|
891
|
-
return yield* fs.exists(join4(config.dataDir, "opencode.db")).pipe(
|
|
953
|
+
const fs = yield* FileSystem4.FileSystem;
|
|
954
|
+
return yield* fs.exists(join4(config.dataDir, "opencode.db")).pipe(Effect9.catchAll(() => Effect9.succeed(false)));
|
|
892
955
|
}),
|
|
893
|
-
discoverProjects: discoverOpenCodeProjects().pipe(
|
|
956
|
+
discoverProjects: discoverOpenCodeProjects().pipe(Effect9.catchAll((err) => Effect9.fail(new PluginError({
|
|
894
957
|
pluginId: "opencode",
|
|
895
958
|
operation: "discoverProjects",
|
|
896
959
|
message: String(err),
|
|
897
960
|
cause: err
|
|
898
961
|
})))),
|
|
899
|
-
listSessions: (nativeId) => listOpenCodeSessions(nativeId).pipe(
|
|
962
|
+
listSessions: (nativeId) => listOpenCodeSessions(nativeId).pipe(Effect9.catchAll((err) => Effect9.fail(new PluginError({
|
|
900
963
|
pluginId: "opencode",
|
|
901
964
|
operation: "listSessions",
|
|
902
965
|
message: String(err),
|
|
903
966
|
cause: err
|
|
904
967
|
})))),
|
|
905
|
-
loadSession: (nativeId, sessionId) => loadOpenCodeSession(nativeId, sessionId).pipe(
|
|
968
|
+
loadSession: (nativeId, sessionId) => loadOpenCodeSession(nativeId, sessionId).pipe(Effect9.catchAll((err) => Effect9.fail(new PluginError({
|
|
906
969
|
pluginId: "opencode",
|
|
907
970
|
operation: "loadSession",
|
|
908
971
|
message: String(err),
|
|
@@ -932,7 +995,7 @@ import { execFile } from "node:child_process";
|
|
|
932
995
|
// ../../packages/server/src/effect/bootstrap.ts
|
|
933
996
|
import { join as join15 } from "node:path";
|
|
934
997
|
import { HttpServer } from "@effect/platform";
|
|
935
|
-
import { Cause, Effect as
|
|
998
|
+
import { Cause, Effect as Effect33, Fiber, Layer as Layer8 } from "effect";
|
|
936
999
|
|
|
937
1000
|
// ../../packages/server/src/effect/platform-bun.ts
|
|
938
1001
|
init_src2();
|
|
@@ -948,20 +1011,20 @@ class ServerConfig extends Context3.Tag("@klovi/ServerConfig")() {
|
|
|
948
1011
|
}
|
|
949
1012
|
|
|
950
1013
|
// ../../packages/server/src/effect/server-services.ts
|
|
951
|
-
import { Context as Context4, Effect as
|
|
1014
|
+
import { Context as Context4, Effect as Effect32, Layer as Layer6 } from "effect";
|
|
952
1015
|
|
|
953
1016
|
// ../../packages/server/src/services/auto-discover.ts
|
|
954
1017
|
init_src();
|
|
955
|
-
import { Effect as
|
|
1018
|
+
import { Effect as Effect25 } from "effect";
|
|
956
1019
|
|
|
957
1020
|
// ../../packages/plugin-claude-code/src/index.ts
|
|
958
1021
|
init_src();
|
|
959
|
-
import { Effect as
|
|
1022
|
+
import { Effect as Effect13 } from "effect";
|
|
960
1023
|
|
|
961
1024
|
// ../../packages/plugin-claude-code/src/discovery.ts
|
|
962
1025
|
init_src();
|
|
963
1026
|
import { join as join6 } from "node:path";
|
|
964
|
-
import { Effect as
|
|
1027
|
+
import { Effect as Effect11 } from "effect";
|
|
965
1028
|
|
|
966
1029
|
// ../../packages/plugin-claude-code/src/command-message.ts
|
|
967
1030
|
var COMMAND_ARGS_REGEX = /<command-args>(?<content>[\s\S]*?)<\/command-args>/u;
|
|
@@ -999,62 +1062,44 @@ function parseCommandMessage(text) {
|
|
|
999
1062
|
// ../../packages/plugin-claude-code/src/shared/discovery-utils.ts
|
|
1000
1063
|
init_src();
|
|
1001
1064
|
import { join as join5 } from "node:path";
|
|
1002
|
-
import { FileSystem as
|
|
1003
|
-
import { Effect as
|
|
1065
|
+
import { FileSystem as FileSystem5 } from "@effect/platform";
|
|
1066
|
+
import { Effect as Effect10 } from "effect";
|
|
1004
1067
|
var WINDOWS_DRIVE_LETTER_REGEX = /^[A-Za-z]\//u;
|
|
1068
|
+
var STAT_CONCURRENCY = 32;
|
|
1005
1069
|
function readDirEntriesSafe(dir) {
|
|
1006
|
-
return
|
|
1007
|
-
const fs = yield*
|
|
1008
|
-
const names = yield* fs.readDirectory(dir).pipe(
|
|
1009
|
-
const entries =
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
}
|
|
1015
|
-
}
|
|
1016
|
-
return entries;
|
|
1070
|
+
return Effect10.gen(function* () {
|
|
1071
|
+
const fs = yield* FileSystem5.FileSystem;
|
|
1072
|
+
const names = yield* fs.readDirectory(dir).pipe(Effect10.catchAll(() => Effect10.succeed([])));
|
|
1073
|
+
const entries = yield* Effect10.forEach(names, (name) => Effect10.gen(function* () {
|
|
1074
|
+
const info = yield* fs.stat(join5(dir, name)).pipe(Effect10.catchAll(() => Effect10.succeed(null)));
|
|
1075
|
+
return info ? { name, isDirectory: info.type === "Directory" } : null;
|
|
1076
|
+
}), { concurrency: STAT_CONCURRENCY });
|
|
1077
|
+
return entries.filter((entry) => entry !== null);
|
|
1017
1078
|
});
|
|
1018
1079
|
}
|
|
1019
1080
|
function listFilesBySuffix(dir, suffix) {
|
|
1020
|
-
return
|
|
1021
|
-
const fs = yield*
|
|
1022
|
-
const names = yield* fs.readDirectory(dir).pipe(
|
|
1081
|
+
return Effect10.gen(function* () {
|
|
1082
|
+
const fs = yield* FileSystem5.FileSystem;
|
|
1083
|
+
const names = yield* fs.readDirectory(dir).pipe(Effect10.catchAll(() => Effect10.succeed([])));
|
|
1023
1084
|
return names.filter((name) => name.endsWith(suffix));
|
|
1024
1085
|
});
|
|
1025
1086
|
}
|
|
1026
1087
|
function listFilesWithMtime(dir, suffix) {
|
|
1027
|
-
return
|
|
1028
|
-
const fs = yield*
|
|
1088
|
+
return Effect10.gen(function* () {
|
|
1089
|
+
const fs = yield* FileSystem5.FileSystem;
|
|
1029
1090
|
const names = yield* listFilesBySuffix(dir, suffix);
|
|
1030
|
-
const
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
}
|
|
1036
|
-
}
|
|
1091
|
+
const candidates = yield* Effect10.forEach(names, (fileName) => Effect10.gen(function* () {
|
|
1092
|
+
const info = yield* fs.stat(join5(dir, fileName)).pipe(Effect10.catchAll(() => Effect10.succeed(null)));
|
|
1093
|
+
return info?.mtime._tag === "Some" ? { fileName, mtime: info.mtime.value.toISOString() } : null;
|
|
1094
|
+
}), { concurrency: STAT_CONCURRENCY });
|
|
1095
|
+
const results = candidates.filter((r) => r !== null);
|
|
1037
1096
|
sortByIsoDesc(results, (item) => item.mtime);
|
|
1038
1097
|
return results;
|
|
1039
1098
|
});
|
|
1040
1099
|
}
|
|
1041
|
-
function readTextPrefix(filePath, maxBytes) {
|
|
1042
|
-
return Effect9.gen(function* () {
|
|
1043
|
-
const fs = yield* FileSystem4.FileSystem;
|
|
1044
|
-
const bytes = yield* fs.readFile(filePath);
|
|
1045
|
-
const slice = bytes.subarray(0, maxBytes);
|
|
1046
|
-
return new TextDecoder().decode(slice);
|
|
1047
|
-
});
|
|
1048
|
-
}
|
|
1049
|
-
function readFileText(filePath) {
|
|
1050
|
-
return Effect9.gen(function* () {
|
|
1051
|
-
const fs = yield* FileSystem4.FileSystem;
|
|
1052
|
-
return yield* fs.readFileString(filePath);
|
|
1053
|
-
});
|
|
1054
|
-
}
|
|
1055
1100
|
function fileExists(filePath) {
|
|
1056
|
-
return
|
|
1057
|
-
const fs = yield*
|
|
1101
|
+
return Effect10.gen(function* () {
|
|
1102
|
+
const fs = yield* FileSystem5.FileSystem;
|
|
1058
1103
|
return yield* fs.exists(filePath);
|
|
1059
1104
|
});
|
|
1060
1105
|
}
|
|
@@ -1069,98 +1114,65 @@ function decodeEncodedPath(encoded) {
|
|
|
1069
1114
|
return encoded.replace(/-/gu, "/");
|
|
1070
1115
|
}
|
|
1071
1116
|
|
|
1072
|
-
// ../../packages/plugin-claude-code/src/shared/jsonl-utils.ts
|
|
1073
|
-
function iterateJsonl(text, visitor, options = {}) {
|
|
1074
|
-
const lines = text.split(`
|
|
1075
|
-
`);
|
|
1076
|
-
const start = Math.max(0, options.startAt ?? 0);
|
|
1077
|
-
const end = options.maxLines === undefined ? lines.length : Math.min(lines.length, start + options.maxLines);
|
|
1078
|
-
for (let i = start;i < end; i++) {
|
|
1079
|
-
const line = lines[i];
|
|
1080
|
-
if (!line?.trim()) {
|
|
1081
|
-
continue;
|
|
1082
|
-
}
|
|
1083
|
-
try {
|
|
1084
|
-
const parsed = JSON.parse(line);
|
|
1085
|
-
const shouldContinue = visitor({
|
|
1086
|
-
parsed,
|
|
1087
|
-
line,
|
|
1088
|
-
lineIndex: i,
|
|
1089
|
-
lineNumber: i + 1
|
|
1090
|
-
});
|
|
1091
|
-
if (shouldContinue === false) {
|
|
1092
|
-
break;
|
|
1093
|
-
}
|
|
1094
|
-
} catch (error) {
|
|
1095
|
-
options.onMalformed?.(line, i + 1, error);
|
|
1096
|
-
}
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
|
-
|
|
1100
1117
|
// ../../packages/plugin-claude-code/src/discovery.ts
|
|
1101
|
-
var BYTES_PER_KB = 1024;
|
|
1102
|
-
var CWD_SCAN_KB = 64;
|
|
1103
|
-
var CWD_SCAN_BYTES = CWD_SCAN_KB * BYTES_PER_KB;
|
|
1104
|
-
var SESSION_META_SCAN_BYTES = BYTES_PER_KB * BYTES_PER_KB;
|
|
1105
1118
|
var BRACKETED_TEXT_REGEX = /^\[.+\]$/u;
|
|
1119
|
+
var PROJECT_DIR_CONCURRENCY = 16;
|
|
1120
|
+
var SESSION_FILE_CONCURRENCY = 16;
|
|
1106
1121
|
function inspectProjectSessions(projectDir, sessionFiles) {
|
|
1107
|
-
return
|
|
1122
|
+
return Effect11.gen(function* () {
|
|
1108
1123
|
const lastActivity = sessionFiles[0]?.mtime || "";
|
|
1109
1124
|
let resolvedPath = "";
|
|
1110
1125
|
for (const sessionFile of sessionFiles) {
|
|
1111
1126
|
const filePath = join6(projectDir, sessionFile.fileName);
|
|
1112
|
-
|
|
1113
|
-
|
|
1127
|
+
resolvedPath = yield* extractCwd(filePath);
|
|
1128
|
+
if (resolvedPath) {
|
|
1129
|
+
break;
|
|
1114
1130
|
}
|
|
1115
1131
|
}
|
|
1116
1132
|
return { lastActivity, resolvedPath };
|
|
1117
1133
|
});
|
|
1118
1134
|
}
|
|
1119
1135
|
function discoverClaudeProjects() {
|
|
1120
|
-
return
|
|
1136
|
+
return Effect11.gen(function* () {
|
|
1121
1137
|
const config = yield* PluginConfig;
|
|
1122
1138
|
const projectsDir = join6(config.dataDir, "projects");
|
|
1123
1139
|
const entries = yield* readDirEntriesSafe(projectsDir);
|
|
1124
|
-
const
|
|
1125
|
-
|
|
1126
|
-
if (!entry.isDirectory) {
|
|
1127
|
-
continue;
|
|
1128
|
-
}
|
|
1140
|
+
const directories = entries.filter((entry) => entry.isDirectory);
|
|
1141
|
+
const projects = yield* Effect11.forEach(directories, (entry) => Effect11.gen(function* () {
|
|
1129
1142
|
const projectDir = join6(projectsDir, entry.name);
|
|
1130
1143
|
const sessionFiles = yield* listFilesWithMtime(projectDir, ".jsonl");
|
|
1131
1144
|
if (sessionFiles.length === 0) {
|
|
1132
|
-
|
|
1145
|
+
return null;
|
|
1133
1146
|
}
|
|
1134
1147
|
const projectInfo = yield* inspectProjectSessions(projectDir, sessionFiles);
|
|
1135
1148
|
const resolvedPath = projectInfo.resolvedPath || decodeEncodedPath(entry.name);
|
|
1136
|
-
|
|
1149
|
+
return {
|
|
1137
1150
|
pluginId: "claude-code",
|
|
1138
1151
|
nativeId: entry.name,
|
|
1139
1152
|
resolvedPath,
|
|
1140
1153
|
displayName: resolvedPath,
|
|
1141
1154
|
sessionCount: sessionFiles.length,
|
|
1142
1155
|
lastActivity: projectInfo.lastActivity
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1145
|
-
|
|
1146
|
-
|
|
1156
|
+
};
|
|
1157
|
+
}), { concurrency: PROJECT_DIR_CONCURRENCY });
|
|
1158
|
+
const filtered = projects.filter((p) => p !== null);
|
|
1159
|
+
sortByIsoDesc(filtered, (project) => project.lastActivity);
|
|
1160
|
+
return filtered;
|
|
1147
1161
|
});
|
|
1148
1162
|
}
|
|
1149
1163
|
var PLAN_PREFIX = "Implement the following plan";
|
|
1150
1164
|
function listClaudeSessions(nativeId) {
|
|
1151
|
-
return
|
|
1165
|
+
return Effect11.gen(function* () {
|
|
1152
1166
|
const config = yield* PluginConfig;
|
|
1153
1167
|
const projectDir = join6(config.dataDir, "projects", nativeId);
|
|
1154
1168
|
const files = yield* listFilesBySuffix(projectDir, ".jsonl");
|
|
1155
|
-
const
|
|
1156
|
-
for (const file of files) {
|
|
1169
|
+
const candidates = yield* Effect11.forEach(files, (file) => Effect11.gen(function* () {
|
|
1157
1170
|
const filePath = join6(projectDir, file);
|
|
1158
1171
|
const sessionId = file.replace(".jsonl", "");
|
|
1159
1172
|
const meta = yield* extractSessionMeta(filePath);
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
}
|
|
1173
|
+
return meta ? { sessionId, pluginId: "claude-code", ...meta } : null;
|
|
1174
|
+
}), { concurrency: SESSION_FILE_CONCURRENCY });
|
|
1175
|
+
const sessions = candidates.filter((s) => s !== null);
|
|
1164
1176
|
classifySessionTypes(sessions);
|
|
1165
1177
|
sortByIsoDesc(sessions, (session) => session.timestamp);
|
|
1166
1178
|
return sessions;
|
|
@@ -1183,20 +1195,16 @@ function classifySessionTypes(sessions) {
|
|
|
1183
1195
|
}
|
|
1184
1196
|
}
|
|
1185
1197
|
function extractCwd(filePath) {
|
|
1186
|
-
return
|
|
1187
|
-
const text = yield* readTextPrefix(filePath, CWD_SCAN_BYTES).pipe(Effect10.catchAll(() => Effect10.succeed("")));
|
|
1188
|
-
if (!text) {
|
|
1189
|
-
return "";
|
|
1190
|
-
}
|
|
1198
|
+
return Effect11.gen(function* () {
|
|
1191
1199
|
let cwd = "";
|
|
1192
|
-
|
|
1200
|
+
yield* streamJsonlHead(filePath, ({ parsed }) => {
|
|
1193
1201
|
const obj = parsed;
|
|
1194
1202
|
if (obj.cwd) {
|
|
1195
1203
|
({ cwd } = obj);
|
|
1196
1204
|
return false;
|
|
1197
1205
|
}
|
|
1198
1206
|
return;
|
|
1199
|
-
}, { maxLines: 20 });
|
|
1207
|
+
}, { maxLines: 20 }).pipe(Effect11.catchAll(() => Effect11.void));
|
|
1200
1208
|
return cwd;
|
|
1201
1209
|
});
|
|
1202
1210
|
}
|
|
@@ -1241,11 +1249,7 @@ function processMetaLine(obj, meta) {
|
|
|
1241
1249
|
}
|
|
1242
1250
|
}
|
|
1243
1251
|
function extractSessionMeta(filePath) {
|
|
1244
|
-
return
|
|
1245
|
-
const text = yield* readTextPrefix(filePath, SESSION_META_SCAN_BYTES).pipe(Effect10.catchAll(() => Effect10.succeed("")));
|
|
1246
|
-
if (!text) {
|
|
1247
|
-
return null;
|
|
1248
|
-
}
|
|
1252
|
+
return Effect11.gen(function* () {
|
|
1249
1253
|
const meta = {
|
|
1250
1254
|
timestamp: "",
|
|
1251
1255
|
slug: "",
|
|
@@ -1253,7 +1257,7 @@ function extractSessionMeta(filePath) {
|
|
|
1253
1257
|
model: "",
|
|
1254
1258
|
gitBranch: ""
|
|
1255
1259
|
};
|
|
1256
|
-
|
|
1260
|
+
yield* streamJsonlHead(filePath, ({ parsed }) => {
|
|
1257
1261
|
const obj = parsed;
|
|
1258
1262
|
processMetaLine(obj, meta);
|
|
1259
1263
|
if (isMetaComplete(meta)) {
|
|
@@ -1263,7 +1267,7 @@ function extractSessionMeta(filePath) {
|
|
|
1263
1267
|
}, {
|
|
1264
1268
|
maxLines: 50,
|
|
1265
1269
|
onMalformed: () => {}
|
|
1266
|
-
});
|
|
1270
|
+
}).pipe(Effect11.catchAll(() => Effect11.void));
|
|
1267
1271
|
if (!(meta.timestamp && meta.firstMessage)) {
|
|
1268
1272
|
return null;
|
|
1269
1273
|
}
|
|
@@ -1280,10 +1284,10 @@ function extractSessionMeta(filePath) {
|
|
|
1280
1284
|
// ../../packages/plugin-claude-code/src/parser.ts
|
|
1281
1285
|
init_src();
|
|
1282
1286
|
import { join as join7 } from "node:path";
|
|
1283
|
-
import { Effect as
|
|
1287
|
+
import { Effect as Effect12 } from "effect";
|
|
1284
1288
|
var MAX_RAW_LINE_LENGTH = 500;
|
|
1285
1289
|
function loadClaudeSession(nativeId, sessionId) {
|
|
1286
|
-
return
|
|
1290
|
+
return Effect12.gen(function* () {
|
|
1287
1291
|
const config = yield* PluginConfig;
|
|
1288
1292
|
const filePath = join7(config.dataDir, "projects", nativeId, `${sessionId}.jsonl`);
|
|
1289
1293
|
const { rawLines, parseErrors } = yield* readJsonlLines(filePath);
|
|
@@ -1315,10 +1319,10 @@ function loadClaudeSession(nativeId, sessionId) {
|
|
|
1315
1319
|
});
|
|
1316
1320
|
}
|
|
1317
1321
|
function parseSubAgentSession(sessionId, encodedPath, agentId) {
|
|
1318
|
-
return
|
|
1322
|
+
return Effect12.gen(function* () {
|
|
1319
1323
|
const config = yield* PluginConfig;
|
|
1320
1324
|
const filePath = join7(config.dataDir, "projects", encodedPath, sessionId, "subagents", `agent-${agentId}.jsonl`);
|
|
1321
|
-
const parsed = yield* readJsonlLines(filePath).pipe(
|
|
1325
|
+
const parsed = yield* readJsonlLines(filePath).pipe(Effect12.catchAll(() => Effect12.succeed({ rawLines: [], parseErrors: [] })));
|
|
1322
1326
|
const subAgentMap = extractSubAgentMap(parsed.rawLines);
|
|
1323
1327
|
const turns = buildTurns(parsed.rawLines, parsed.parseErrors);
|
|
1324
1328
|
for (const turn of turns) {
|
|
@@ -1404,11 +1408,10 @@ function findImplSessionId(slug, sessions, currentSessionId) {
|
|
|
1404
1408
|
return match?.sessionId;
|
|
1405
1409
|
}
|
|
1406
1410
|
function readJsonlLines(filePath) {
|
|
1407
|
-
return
|
|
1408
|
-
const text = yield* readFileText(filePath);
|
|
1411
|
+
return Effect12.gen(function* () {
|
|
1409
1412
|
const rawLines = [];
|
|
1410
1413
|
const parseErrors = [];
|
|
1411
|
-
|
|
1414
|
+
yield* streamJsonl(filePath, ({ parsed }) => {
|
|
1412
1415
|
rawLines.push(parsed);
|
|
1413
1416
|
}, {
|
|
1414
1417
|
onMalformed: (line, lineNumber, error) => {
|
|
@@ -1713,20 +1716,20 @@ var claudeCodePlugin = {
|
|
|
1713
1716
|
id: "claude-code",
|
|
1714
1717
|
displayName: "Claude Code",
|
|
1715
1718
|
getDefaultDataDir: () => null,
|
|
1716
|
-
isDataAvailable:
|
|
1719
|
+
isDataAvailable: Effect13.gen(function* () {
|
|
1717
1720
|
const config = yield* PluginConfig;
|
|
1718
|
-
return yield* fileExists(config.dataDir).pipe(
|
|
1721
|
+
return yield* fileExists(config.dataDir).pipe(Effect13.catchAll(() => Effect13.succeed(false)));
|
|
1719
1722
|
}),
|
|
1720
1723
|
discoverProjects: discoverClaudeProjects(),
|
|
1721
1724
|
listSessions: (nativeId) => listClaudeSessions(nativeId),
|
|
1722
|
-
loadSession: (nativeId, sessionId) => loadClaudeSession(nativeId, sessionId).pipe(
|
|
1725
|
+
loadSession: (nativeId, sessionId) => loadClaudeSession(nativeId, sessionId).pipe(Effect13.map((r) => r.session), Effect13.catchAll((err) => Effect13.fail(new PluginError({
|
|
1723
1726
|
pluginId: "claude-code",
|
|
1724
1727
|
operation: "loadSession",
|
|
1725
1728
|
message: String(err),
|
|
1726
1729
|
cause: err
|
|
1727
1730
|
})))),
|
|
1728
|
-
loadSessionDetail: (nativeId, sessionId) =>
|
|
1729
|
-
const [{ session, slug }, sessions] = yield*
|
|
1731
|
+
loadSessionDetail: (nativeId, sessionId) => Effect13.gen(function* () {
|
|
1732
|
+
const [{ session, slug }, sessions] = yield* Effect13.all([
|
|
1730
1733
|
loadClaudeSession(nativeId, sessionId),
|
|
1731
1734
|
listClaudeSessions(nativeId)
|
|
1732
1735
|
]);
|
|
@@ -1735,7 +1738,7 @@ var claudeCodePlugin = {
|
|
|
1735
1738
|
planSessionId: findPlanSessionId(session.turns, slug, sessions, sessionId),
|
|
1736
1739
|
implSessionId: findImplSessionId(slug, sessions, sessionId)
|
|
1737
1740
|
};
|
|
1738
|
-
}).pipe(
|
|
1741
|
+
}).pipe(Effect13.catchAll((err) => Effect13.fail(new PluginError({
|
|
1739
1742
|
pluginId: "claude-code",
|
|
1740
1743
|
operation: "loadSessionDetail",
|
|
1741
1744
|
message: String(err),
|
|
@@ -1747,74 +1750,17 @@ var claudeCodePlugin = {
|
|
|
1747
1750
|
|
|
1748
1751
|
// ../../packages/plugin-codex/src/index.ts
|
|
1749
1752
|
init_src();
|
|
1750
|
-
import { Effect as
|
|
1753
|
+
import { Effect as Effect18 } from "effect";
|
|
1751
1754
|
|
|
1752
1755
|
// ../../packages/plugin-codex/src/discovery.ts
|
|
1753
1756
|
init_src();
|
|
1754
|
-
import { Effect as
|
|
1757
|
+
import { Effect as Effect16 } from "effect";
|
|
1755
1758
|
|
|
1756
1759
|
// ../../packages/plugin-codex/src/session-index.ts
|
|
1757
1760
|
init_src();
|
|
1758
1761
|
import { join as join9 } from "node:path";
|
|
1759
1762
|
import { FileSystem as FileSystem6 } from "@effect/platform";
|
|
1760
1763
|
import { Effect as Effect14 } from "effect";
|
|
1761
|
-
|
|
1762
|
-
// ../../packages/plugin-codex/src/shared/discovery-utils.ts
|
|
1763
|
-
import { FileSystem as FileSystem5 } from "@effect/platform";
|
|
1764
|
-
import { Effect as Effect13 } from "effect";
|
|
1765
|
-
function readTextPrefix2(filePath, maxBytes) {
|
|
1766
|
-
return Effect13.gen(function* () {
|
|
1767
|
-
const fs = yield* FileSystem5.FileSystem;
|
|
1768
|
-
const bytes = yield* fs.readFile(filePath);
|
|
1769
|
-
const slice = bytes.subarray(0, maxBytes);
|
|
1770
|
-
return new TextDecoder().decode(slice);
|
|
1771
|
-
});
|
|
1772
|
-
}
|
|
1773
|
-
function readFileText2(filePath) {
|
|
1774
|
-
return Effect13.gen(function* () {
|
|
1775
|
-
const fs = yield* FileSystem5.FileSystem;
|
|
1776
|
-
return yield* fs.readFileString(filePath);
|
|
1777
|
-
});
|
|
1778
|
-
}
|
|
1779
|
-
function fileExists2(filePath) {
|
|
1780
|
-
return Effect13.gen(function* () {
|
|
1781
|
-
const fs = yield* FileSystem5.FileSystem;
|
|
1782
|
-
return yield* fs.exists(filePath);
|
|
1783
|
-
});
|
|
1784
|
-
}
|
|
1785
|
-
|
|
1786
|
-
// ../../packages/plugin-codex/src/shared/jsonl-utils.ts
|
|
1787
|
-
function iterateJsonl2(text, visitor, options = {}) {
|
|
1788
|
-
const lines = text.split(`
|
|
1789
|
-
`);
|
|
1790
|
-
const start = Math.max(0, options.startAt ?? 0);
|
|
1791
|
-
const end = options.maxLines === undefined ? lines.length : Math.min(lines.length, start + options.maxLines);
|
|
1792
|
-
for (let i = start;i < end; i++) {
|
|
1793
|
-
const line = lines[i];
|
|
1794
|
-
if (!line?.trim()) {
|
|
1795
|
-
continue;
|
|
1796
|
-
}
|
|
1797
|
-
try {
|
|
1798
|
-
const parsed = JSON.parse(line);
|
|
1799
|
-
const shouldContinue = visitor({
|
|
1800
|
-
parsed,
|
|
1801
|
-
line,
|
|
1802
|
-
lineIndex: i,
|
|
1803
|
-
lineNumber: i + 1
|
|
1804
|
-
});
|
|
1805
|
-
if (shouldContinue === false) {
|
|
1806
|
-
break;
|
|
1807
|
-
}
|
|
1808
|
-
} catch (error) {
|
|
1809
|
-
options.onMalformed?.(line, i + 1, error);
|
|
1810
|
-
}
|
|
1811
|
-
}
|
|
1812
|
-
}
|
|
1813
|
-
|
|
1814
|
-
// ../../packages/plugin-codex/src/session-index.ts
|
|
1815
|
-
var BYTES_PER_KB2 = 1024;
|
|
1816
|
-
var FIRST_LINE_SCAN_KB = 512;
|
|
1817
|
-
var FIRST_LINE_SCAN_BYTES = FIRST_LINE_SCAN_KB * BYTES_PER_KB2;
|
|
1818
1764
|
var MS_PER_SECOND2 = 1000;
|
|
1819
1765
|
function isCodexSessionMeta(obj) {
|
|
1820
1766
|
return typeof obj === "object" && obj !== null && "uuid" in obj && "cwd" in obj && "timestamps" in obj && typeof obj.uuid === "string" && typeof obj.cwd === "string";
|
|
@@ -1854,50 +1800,45 @@ function extractTurnContextModel(parsed) {
|
|
|
1854
1800
|
}
|
|
1855
1801
|
return typeof event.payload?.model === "string" ? event.payload.model : null;
|
|
1856
1802
|
}
|
|
1857
|
-
function
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1803
|
+
function streamInferredModel(filePath) {
|
|
1804
|
+
return Effect14.gen(function* () {
|
|
1805
|
+
let model = null;
|
|
1806
|
+
yield* streamJsonlHead(filePath, ({ parsed, lineIndex }) => {
|
|
1807
|
+
if (lineIndex === 0) {
|
|
1808
|
+
return;
|
|
1809
|
+
}
|
|
1810
|
+
const extracted = extractTurnContextModel(parsed);
|
|
1811
|
+
if (isKnownModel(extracted)) {
|
|
1812
|
+
model = extracted;
|
|
1813
|
+
return false;
|
|
1814
|
+
}
|
|
1815
|
+
return;
|
|
1816
|
+
}, { maxLines: 256 }).pipe(Effect14.catchAll(() => Effect14.void));
|
|
1817
|
+
return model;
|
|
1818
|
+
});
|
|
1868
1819
|
}
|
|
1869
1820
|
function parseSessionMeta(filePath, fileMtimeEpoch) {
|
|
1870
1821
|
return Effect14.gen(function* () {
|
|
1871
|
-
const
|
|
1872
|
-
|
|
1822
|
+
const captured = { value: null };
|
|
1823
|
+
yield* streamJsonlHead(filePath, ({ parsed }) => {
|
|
1824
|
+
captured.value = normalizeSessionMeta(parsed, fileMtimeEpoch);
|
|
1825
|
+
return false;
|
|
1826
|
+
}, { maxLines: 1 }).pipe(Effect14.catchAll(() => Effect14.void));
|
|
1827
|
+
const firstLineMeta = captured.value;
|
|
1828
|
+
if (!firstLineMeta) {
|
|
1873
1829
|
return null;
|
|
1874
1830
|
}
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
const firstLine = firstNewline === -1 ? prefix : prefix.slice(0, firstNewline);
|
|
1878
|
-
const trimmedFirstLine = firstLine.trim();
|
|
1879
|
-
if (!trimmedFirstLine) {
|
|
1880
|
-
return null;
|
|
1831
|
+
if (isKnownModel(firstLineMeta.model)) {
|
|
1832
|
+
return firstLineMeta;
|
|
1881
1833
|
}
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
}
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
}
|
|
1891
|
-
const inferred = inferModelFromPrefix(prefix);
|
|
1892
|
-
if (isKnownModel(inferred)) {
|
|
1893
|
-
return { ...meta, model: inferred };
|
|
1894
|
-
}
|
|
1895
|
-
if (isKnownModel(meta.provider_id)) {
|
|
1896
|
-
return { ...meta, model: meta.provider_id };
|
|
1897
|
-
}
|
|
1898
|
-
return meta;
|
|
1899
|
-
} catch {}
|
|
1900
|
-
return null;
|
|
1834
|
+
const inferred = yield* streamInferredModel(filePath);
|
|
1835
|
+
if (isKnownModel(inferred)) {
|
|
1836
|
+
return { ...firstLineMeta, model: inferred };
|
|
1837
|
+
}
|
|
1838
|
+
if (isKnownModel(firstLineMeta.provider_id)) {
|
|
1839
|
+
return { ...firstLineMeta, model: firstLineMeta.provider_id };
|
|
1840
|
+
}
|
|
1841
|
+
return firstLineMeta;
|
|
1901
1842
|
});
|
|
1902
1843
|
}
|
|
1903
1844
|
function walkJsonlFiles(dir, visit) {
|
|
@@ -1983,12 +1924,54 @@ function findCodexSessionFileById(sessionId) {
|
|
|
1983
1924
|
});
|
|
1984
1925
|
}
|
|
1985
1926
|
|
|
1927
|
+
// ../../packages/plugin-codex/src/shared/discovery-utils.ts
|
|
1928
|
+
import { FileSystem as FileSystem7 } from "@effect/platform";
|
|
1929
|
+
import { Effect as Effect15 } from "effect";
|
|
1930
|
+
function readFileText(filePath) {
|
|
1931
|
+
return Effect15.gen(function* () {
|
|
1932
|
+
const fs = yield* FileSystem7.FileSystem;
|
|
1933
|
+
return yield* fs.readFileString(filePath);
|
|
1934
|
+
});
|
|
1935
|
+
}
|
|
1936
|
+
function fileExists2(filePath) {
|
|
1937
|
+
return Effect15.gen(function* () {
|
|
1938
|
+
const fs = yield* FileSystem7.FileSystem;
|
|
1939
|
+
return yield* fs.exists(filePath);
|
|
1940
|
+
});
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
// ../../packages/plugin-codex/src/shared/jsonl-utils.ts
|
|
1944
|
+
function iterateJsonl(text, visitor, options = {}) {
|
|
1945
|
+
const lines = text.split(`
|
|
1946
|
+
`);
|
|
1947
|
+
const start = Math.max(0, options.startAt ?? 0);
|
|
1948
|
+
const end = options.maxLines === undefined ? lines.length : Math.min(lines.length, start + options.maxLines);
|
|
1949
|
+
for (let i = start;i < end; i++) {
|
|
1950
|
+
const line = lines[i];
|
|
1951
|
+
if (!line?.trim()) {
|
|
1952
|
+
continue;
|
|
1953
|
+
}
|
|
1954
|
+
try {
|
|
1955
|
+
const parsed = JSON.parse(line);
|
|
1956
|
+
const shouldContinue = visitor({
|
|
1957
|
+
parsed,
|
|
1958
|
+
line,
|
|
1959
|
+
lineIndex: i,
|
|
1960
|
+
lineNumber: i + 1
|
|
1961
|
+
});
|
|
1962
|
+
if (shouldContinue === false) {
|
|
1963
|
+
break;
|
|
1964
|
+
}
|
|
1965
|
+
} catch (error) {
|
|
1966
|
+
options.onMalformed?.(line, i + 1, error);
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1986
1971
|
// ../../packages/plugin-codex/src/discovery.ts
|
|
1987
|
-
var
|
|
1988
|
-
var SESSION_TITLE_SCAN_KB = 256;
|
|
1989
|
-
var SESSION_TITLE_SCAN_BYTES = SESSION_TITLE_SCAN_KB * BYTES_PER_KB3;
|
|
1972
|
+
var SESSION_FILE_CONCURRENCY2 = 16;
|
|
1990
1973
|
function discoverCodexProjects() {
|
|
1991
|
-
return
|
|
1974
|
+
return Effect16.gen(function* () {
|
|
1992
1975
|
const sessions = yield* scanCodexSessions();
|
|
1993
1976
|
const byCwd = new Map;
|
|
1994
1977
|
for (const session of sessions) {
|
|
@@ -2020,9 +2003,41 @@ function discoverCodexProjects() {
|
|
|
2020
2003
|
return projects;
|
|
2021
2004
|
});
|
|
2022
2005
|
}
|
|
2023
|
-
function
|
|
2006
|
+
function visitForFirstUserMessage(parsed, captured) {
|
|
2007
|
+
const event = parsed;
|
|
2008
|
+
if (event.type === "item.completed" && event.item?.type === "agent_message" && event.item.text) {
|
|
2009
|
+
const maxPreviewLength = 200;
|
|
2010
|
+
captured.value = event.item.text.slice(0, maxPreviewLength);
|
|
2011
|
+
return true;
|
|
2012
|
+
}
|
|
2013
|
+
if (event.type === "event_msg" && event.payload?.type === "user_message") {
|
|
2014
|
+
const payloadText = event.payload.message || event.payload.text;
|
|
2015
|
+
if (typeof payloadText === "string" && payloadText) {
|
|
2016
|
+
const maxMsgLength = 200;
|
|
2017
|
+
captured.value = payloadText.slice(0, maxMsgLength);
|
|
2018
|
+
return true;
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
return false;
|
|
2022
|
+
}
|
|
2023
|
+
function streamFirstUserMessage(filePath) {
|
|
2024
|
+
return Effect16.gen(function* () {
|
|
2025
|
+
const captured = { value: null };
|
|
2026
|
+
yield* streamJsonlHead(filePath, ({ parsed, lineIndex }) => {
|
|
2027
|
+
if (lineIndex === 0) {
|
|
2028
|
+
return;
|
|
2029
|
+
}
|
|
2030
|
+
if (visitForFirstUserMessage(parsed, captured)) {
|
|
2031
|
+
return false;
|
|
2032
|
+
}
|
|
2033
|
+
return;
|
|
2034
|
+
}, { maxLines: 200 }).pipe(Effect16.catchAll(() => Effect16.void));
|
|
2035
|
+
return captured.value;
|
|
2036
|
+
});
|
|
2037
|
+
}
|
|
2038
|
+
function extractFirstUserMessageFromText(text) {
|
|
2024
2039
|
let message = null;
|
|
2025
|
-
|
|
2040
|
+
iterateJsonl(text, ({ parsed }) => {
|
|
2026
2041
|
const event = parsed;
|
|
2027
2042
|
if (event.type === "item.completed" && event.item?.type === "agent_message" && event.item.text) {
|
|
2028
2043
|
const maxPreviewLength = 200;
|
|
@@ -2042,23 +2057,21 @@ function extractFirstUserMessage(text) {
|
|
|
2042
2057
|
return message;
|
|
2043
2058
|
}
|
|
2044
2059
|
function listCodexSessions(nativeId) {
|
|
2045
|
-
return
|
|
2060
|
+
return Effect16.gen(function* () {
|
|
2046
2061
|
const allSessions = yield* scanCodexSessions();
|
|
2047
2062
|
const matching = allSessions.filter((s) => s.meta.cwd === nativeId);
|
|
2048
|
-
const sessions =
|
|
2049
|
-
for (const s of matching) {
|
|
2063
|
+
const sessions = yield* Effect16.forEach(matching, (s) => Effect16.gen(function* () {
|
|
2050
2064
|
let firstMessage = s.meta.name ?? "";
|
|
2051
2065
|
if (!firstMessage) {
|
|
2052
|
-
|
|
2053
|
-
firstMessage = extractFirstUserMessage(prefix) ?? "";
|
|
2066
|
+
firstMessage = (yield* streamFirstUserMessage(s.filePath)) ?? "";
|
|
2054
2067
|
if (!firstMessage) {
|
|
2055
|
-
const fullText = yield*
|
|
2056
|
-
firstMessage =
|
|
2068
|
+
const fullText = yield* readFileText(s.filePath).pipe(Effect16.catchAll(() => Effect16.succeed("")));
|
|
2069
|
+
firstMessage = extractFirstUserMessageFromText(fullText) ?? "";
|
|
2057
2070
|
}
|
|
2058
2071
|
firstMessage ||= "Codex session";
|
|
2059
2072
|
}
|
|
2060
2073
|
const timestamp = epochSecondsToIso(s.meta.timestamps.created);
|
|
2061
|
-
|
|
2074
|
+
return {
|
|
2062
2075
|
sessionId: s.meta.uuid,
|
|
2063
2076
|
timestamp,
|
|
2064
2077
|
slug: s.meta.uuid,
|
|
@@ -2066,8 +2079,8 @@ function listCodexSessions(nativeId) {
|
|
|
2066
2079
|
model: s.meta.model || "unknown",
|
|
2067
2080
|
gitBranch: "",
|
|
2068
2081
|
pluginId: "codex-cli"
|
|
2069
|
-
}
|
|
2070
|
-
}
|
|
2082
|
+
};
|
|
2083
|
+
}), { concurrency: SESSION_FILE_CONCURRENCY2 });
|
|
2071
2084
|
sortByIsoDesc(sessions, (session) => session.timestamp);
|
|
2072
2085
|
return sessions;
|
|
2073
2086
|
});
|
|
@@ -2075,7 +2088,7 @@ function listCodexSessions(nativeId) {
|
|
|
2075
2088
|
|
|
2076
2089
|
// ../../packages/plugin-codex/src/parser.ts
|
|
2077
2090
|
init_src();
|
|
2078
|
-
import { Effect as
|
|
2091
|
+
import { Effect as Effect17 } from "effect";
|
|
2079
2092
|
function isKnownModel2(model) {
|
|
2080
2093
|
return typeof model === "string" && model.length > 0 && model !== "unknown";
|
|
2081
2094
|
}
|
|
@@ -2413,7 +2426,7 @@ function buildCodexTurns(events, model, timestamp) {
|
|
|
2413
2426
|
return state.turns;
|
|
2414
2427
|
}
|
|
2415
2428
|
function loadCodexSession(_nativeId, sessionId) {
|
|
2416
|
-
return
|
|
2429
|
+
return Effect17.gen(function* () {
|
|
2417
2430
|
const filePath = yield* findCodexSessionFileById(sessionId);
|
|
2418
2431
|
if (!filePath) {
|
|
2419
2432
|
return {
|
|
@@ -2423,11 +2436,11 @@ function loadCodexSession(_nativeId, sessionId) {
|
|
|
2423
2436
|
pluginId: "codex-cli"
|
|
2424
2437
|
};
|
|
2425
2438
|
}
|
|
2426
|
-
const text = yield*
|
|
2439
|
+
const text = yield* readFileText(filePath);
|
|
2427
2440
|
let meta = null;
|
|
2428
2441
|
const events = [];
|
|
2429
2442
|
let turnContextModel = null;
|
|
2430
|
-
|
|
2443
|
+
iterateJsonl(text, ({ parsed, lineIndex }) => {
|
|
2431
2444
|
if (lineIndex === 0) {
|
|
2432
2445
|
const normalized = normalizeSessionMeta(parsed);
|
|
2433
2446
|
if (normalized) {
|
|
@@ -2467,13 +2480,13 @@ var codexCliPlugin = {
|
|
|
2467
2480
|
id: "codex-cli",
|
|
2468
2481
|
displayName: "Codex",
|
|
2469
2482
|
getDefaultDataDir: () => null,
|
|
2470
|
-
isDataAvailable:
|
|
2483
|
+
isDataAvailable: Effect18.gen(function* () {
|
|
2471
2484
|
const config = yield* PluginConfig;
|
|
2472
|
-
return yield* fileExists2(config.dataDir).pipe(
|
|
2485
|
+
return yield* fileExists2(config.dataDir).pipe(Effect18.catchAll(() => Effect18.succeed(false)));
|
|
2473
2486
|
}),
|
|
2474
2487
|
discoverProjects: discoverCodexProjects(),
|
|
2475
2488
|
listSessions: (nativeId) => listCodexSessions(nativeId),
|
|
2476
|
-
loadSession: (nativeId, sessionId) => loadCodexSession(nativeId, sessionId).pipe(
|
|
2489
|
+
loadSession: (nativeId, sessionId) => loadCodexSession(nativeId, sessionId).pipe(Effect18.catchAll((err) => Effect18.fail(new PluginError({
|
|
2477
2490
|
pluginId: "codex-cli",
|
|
2478
2491
|
operation: "loadSession",
|
|
2479
2492
|
message: String(err),
|
|
@@ -2484,8 +2497,8 @@ var codexCliPlugin = {
|
|
|
2484
2497
|
|
|
2485
2498
|
// ../../packages/plugin-cursor/src/index.ts
|
|
2486
2499
|
init_src();
|
|
2487
|
-
import { FileSystem as
|
|
2488
|
-
import { Effect as
|
|
2500
|
+
import { FileSystem as FileSystem10 } from "@effect/platform";
|
|
2501
|
+
import { Effect as Effect24 } from "effect";
|
|
2489
2502
|
|
|
2490
2503
|
// ../../packages/plugin-cursor/src/config.ts
|
|
2491
2504
|
import { posix, win32 } from "node:path";
|
|
@@ -2543,16 +2556,16 @@ var DEFAULT_CURSOR_DIR = getDefaultCursorDir();
|
|
|
2543
2556
|
init_src();
|
|
2544
2557
|
import { join as join12 } from "node:path";
|
|
2545
2558
|
import { fileURLToPath } from "node:url";
|
|
2546
|
-
import { Effect as
|
|
2559
|
+
import { Effect as Effect22 } from "effect";
|
|
2547
2560
|
|
|
2548
2561
|
// ../../packages/plugin-cursor/src/db.ts
|
|
2549
2562
|
init_src();
|
|
2550
|
-
import { FileSystem as
|
|
2551
|
-
import { Effect as
|
|
2563
|
+
import { FileSystem as FileSystem8 } from "@effect/platform";
|
|
2564
|
+
import { Effect as Effect19 } from "effect";
|
|
2552
2565
|
function openCursorDbIfExists(dbPath) {
|
|
2553
|
-
return
|
|
2554
|
-
const fs = yield*
|
|
2555
|
-
const exists = yield* fs.exists(dbPath).pipe(
|
|
2566
|
+
return Effect19.gen(function* () {
|
|
2567
|
+
const fs = yield* FileSystem8.FileSystem;
|
|
2568
|
+
const exists = yield* fs.exists(dbPath).pipe(Effect19.catchAll(() => Effect19.succeed(false)));
|
|
2556
2569
|
if (!exists) {
|
|
2557
2570
|
return null;
|
|
2558
2571
|
}
|
|
@@ -2564,25 +2577,25 @@ function openCursorGlobalDb() {
|
|
|
2564
2577
|
return openCursorDbIfExists(getCursorGlobalDbPath());
|
|
2565
2578
|
}
|
|
2566
2579
|
function getCursorWorkspaceStorageDirEffect() {
|
|
2567
|
-
return
|
|
2580
|
+
return Effect19.succeed(getCursorWorkspaceStorageDir());
|
|
2568
2581
|
}
|
|
2569
2582
|
|
|
2570
2583
|
// ../../packages/plugin-cursor/src/plans.ts
|
|
2571
2584
|
import { basename } from "node:path";
|
|
2572
|
-
import { Effect as
|
|
2585
|
+
import { Effect as Effect21 } from "effect";
|
|
2573
2586
|
|
|
2574
2587
|
// ../../packages/plugin-cursor/src/shared/discovery-utils.ts
|
|
2575
2588
|
init_src();
|
|
2576
2589
|
import { join as join11 } from "node:path";
|
|
2577
|
-
import { FileSystem as
|
|
2578
|
-
import { Effect as
|
|
2590
|
+
import { FileSystem as FileSystem9 } from "@effect/platform";
|
|
2591
|
+
import { Effect as Effect20 } from "effect";
|
|
2579
2592
|
function readDirEntriesSafe2(dir) {
|
|
2580
|
-
return
|
|
2581
|
-
const fs = yield*
|
|
2582
|
-
const names = yield* fs.readDirectory(dir).pipe(
|
|
2593
|
+
return Effect20.gen(function* () {
|
|
2594
|
+
const fs = yield* FileSystem9.FileSystem;
|
|
2595
|
+
const names = yield* fs.readDirectory(dir).pipe(Effect20.catchAll(() => Effect20.succeed([])));
|
|
2583
2596
|
const entries = [];
|
|
2584
2597
|
for (const name of names) {
|
|
2585
|
-
const info = yield* fs.stat(join11(dir, name)).pipe(
|
|
2598
|
+
const info = yield* fs.stat(join11(dir, name)).pipe(Effect20.catchAll(() => Effect20.succeed(null)));
|
|
2586
2599
|
if (info) {
|
|
2587
2600
|
entries.push({ name, isDirectory: info.type === "Directory" });
|
|
2588
2601
|
}
|
|
@@ -2590,28 +2603,28 @@ function readDirEntriesSafe2(dir) {
|
|
|
2590
2603
|
return entries;
|
|
2591
2604
|
});
|
|
2592
2605
|
}
|
|
2593
|
-
function
|
|
2594
|
-
return
|
|
2595
|
-
const fs = yield*
|
|
2606
|
+
function readFileText2(filePath) {
|
|
2607
|
+
return Effect20.gen(function* () {
|
|
2608
|
+
const fs = yield* FileSystem9.FileSystem;
|
|
2596
2609
|
return yield* fs.readFileString(filePath);
|
|
2597
2610
|
});
|
|
2598
2611
|
}
|
|
2599
2612
|
function fileExists3(filePath) {
|
|
2600
|
-
return
|
|
2601
|
-
const fs = yield*
|
|
2602
|
-
return yield* fs.exists(filePath).pipe(
|
|
2613
|
+
return Effect20.gen(function* () {
|
|
2614
|
+
const fs = yield* FileSystem9.FileSystem;
|
|
2615
|
+
return yield* fs.exists(filePath).pipe(Effect20.catchAll(() => Effect20.succeed(false)));
|
|
2603
2616
|
});
|
|
2604
2617
|
}
|
|
2605
2618
|
function listFilesWithMtime2(dir, suffix) {
|
|
2606
|
-
return
|
|
2607
|
-
const fs = yield*
|
|
2608
|
-
const names = yield* fs.readDirectory(dir).pipe(
|
|
2619
|
+
return Effect20.gen(function* () {
|
|
2620
|
+
const fs = yield* FileSystem9.FileSystem;
|
|
2621
|
+
const names = yield* fs.readDirectory(dir).pipe(Effect20.catchAll(() => Effect20.succeed([])));
|
|
2609
2622
|
const results = [];
|
|
2610
2623
|
for (const name of names) {
|
|
2611
2624
|
if (!name.endsWith(suffix)) {
|
|
2612
2625
|
continue;
|
|
2613
2626
|
}
|
|
2614
|
-
const info = yield* fs.stat(join11(dir, name)).pipe(
|
|
2627
|
+
const info = yield* fs.stat(join11(dir, name)).pipe(Effect20.catchAll(() => Effect20.succeed(null)));
|
|
2615
2628
|
if (info?.mtime._tag === "Some") {
|
|
2616
2629
|
results.push({ fileName: name, mtime: info.mtime.value.toISOString() });
|
|
2617
2630
|
}
|
|
@@ -2678,7 +2691,7 @@ function getPlanFallbackName(filePath) {
|
|
|
2678
2691
|
return basename(filePath).replace(PLAN_FILE_SUFFIX_REGEX, "");
|
|
2679
2692
|
}
|
|
2680
2693
|
function readPlanDisplayName(filePath) {
|
|
2681
|
-
return
|
|
2694
|
+
return readFileText2(filePath).pipe(Effect21.map((text) => {
|
|
2682
2695
|
const parsed = parsePlanFrontmatter(text);
|
|
2683
2696
|
return parsed.name || extractFirstHeading(parsed.body) || getPlanFallbackName(filePath);
|
|
2684
2697
|
}));
|
|
@@ -2692,7 +2705,7 @@ function makeSystemTurn(text, timestamp) {
|
|
|
2692
2705
|
};
|
|
2693
2706
|
}
|
|
2694
2707
|
function loadCursorPlanSession(plan) {
|
|
2695
|
-
return
|
|
2708
|
+
return readFileText2(plan.filePath).pipe(Effect21.map((text) => {
|
|
2696
2709
|
const parsed = parsePlanFrontmatter(text);
|
|
2697
2710
|
return {
|
|
2698
2711
|
sessionId: plan.rawSessionId,
|
|
@@ -2713,7 +2726,7 @@ function tryParseJson2(value) {
|
|
|
2713
2726
|
}
|
|
2714
2727
|
|
|
2715
2728
|
// ../../packages/plugin-cursor/src/shared/jsonl-utils.ts
|
|
2716
|
-
function
|
|
2729
|
+
function iterateJsonl2(text, visitor, options = {}) {
|
|
2717
2730
|
const lines = text.split(`
|
|
2718
2731
|
`);
|
|
2719
2732
|
const start = Math.max(0, options.startAt ?? 0);
|
|
@@ -2900,7 +2913,7 @@ function parseProjectPathFromWorkspaceJson(content) {
|
|
|
2900
2913
|
return fileUrlToPath(parsed.folder);
|
|
2901
2914
|
}
|
|
2902
2915
|
function discoverWorkspaceDescriptors(targetProjectPath) {
|
|
2903
|
-
return
|
|
2916
|
+
return Effect22.gen(function* () {
|
|
2904
2917
|
const workspaceStorageDir = yield* getCursorWorkspaceStorageDirEffect();
|
|
2905
2918
|
const workspaceEntries = yield* readDirEntriesSafe2(workspaceStorageDir);
|
|
2906
2919
|
const workspaceDescriptors = [];
|
|
@@ -2915,7 +2928,7 @@ function discoverWorkspaceDescriptors(targetProjectPath) {
|
|
|
2915
2928
|
if (!exists) {
|
|
2916
2929
|
continue;
|
|
2917
2930
|
}
|
|
2918
|
-
const projectPath = yield*
|
|
2931
|
+
const projectPath = yield* readFileText2(workspaceJsonPath).pipe(Effect22.map(parseProjectPathFromWorkspaceJson), Effect22.catchAll(() => Effect22.succeed(null)));
|
|
2919
2932
|
if (!projectPath || targetProjectPath && projectPath !== targetProjectPath) {
|
|
2920
2933
|
continue;
|
|
2921
2934
|
}
|
|
@@ -2948,7 +2961,7 @@ function collectComposerSessions({
|
|
|
2948
2961
|
composersById,
|
|
2949
2962
|
options
|
|
2950
2963
|
}) {
|
|
2951
|
-
return
|
|
2964
|
+
return Effect22.gen(function* () {
|
|
2952
2965
|
for (const descriptor of workspaceDescriptors) {
|
|
2953
2966
|
const workspaceDb = yield* openCursorDbIfExists(descriptor.workspaceDbPath);
|
|
2954
2967
|
if (!workspaceDb) {
|
|
@@ -2978,7 +2991,7 @@ function collectComposerSessions({
|
|
|
2978
2991
|
}
|
|
2979
2992
|
function readFirstTranscriptUserMessage(text) {
|
|
2980
2993
|
let firstMessage = "";
|
|
2981
|
-
|
|
2994
|
+
iterateJsonl2(text, ({ parsed }) => {
|
|
2982
2995
|
const line = parsed;
|
|
2983
2996
|
if (line.role !== "user" || !Array.isArray(line.message?.content)) {
|
|
2984
2997
|
return;
|
|
@@ -2995,7 +3008,7 @@ function readFirstTranscriptUserMessage(text) {
|
|
|
2995
3008
|
return firstMessage;
|
|
2996
3009
|
}
|
|
2997
3010
|
function listTranscriptFiles(agentTranscriptsDir) {
|
|
2998
|
-
return
|
|
3011
|
+
return Effect22.gen(function* () {
|
|
2999
3012
|
const agentDirs = yield* readDirEntriesSafe2(agentTranscriptsDir);
|
|
3000
3013
|
const files = [];
|
|
3001
3014
|
for (const entry of agentDirs) {
|
|
@@ -3036,7 +3049,7 @@ function createBackgroundAgentSummary(projectPath, transcript, firstMessage) {
|
|
|
3036
3049
|
};
|
|
3037
3050
|
}
|
|
3038
3051
|
function discoverBackgroundAgentsForProject(projectPath, options = { readPreviewText: true }) {
|
|
3039
|
-
return
|
|
3052
|
+
return Effect22.gen(function* () {
|
|
3040
3053
|
const config = yield* PluginConfig;
|
|
3041
3054
|
const agentTranscriptsDir = join12(config.dataDir, "projects", encodeCursorProjectPath(projectPath), "agent-transcripts");
|
|
3042
3055
|
const agentsById = new Map;
|
|
@@ -3048,7 +3061,7 @@ function discoverBackgroundAgentsForProject(projectPath, options = { readPreview
|
|
|
3048
3061
|
for (const transcript of transcripts) {
|
|
3049
3062
|
let firstMessage = "Cursor background agent";
|
|
3050
3063
|
if (options.readPreviewText) {
|
|
3051
|
-
const text = yield*
|
|
3064
|
+
const text = yield* readFileText2(transcript.filePath).pipe(Effect22.catchAll(() => Effect22.succeed("")));
|
|
3052
3065
|
firstMessage = readFirstTranscriptUserMessage(text) || firstMessage;
|
|
3053
3066
|
}
|
|
3054
3067
|
agentsById.set(transcript.agentId, createBackgroundAgentSummary(projectPath, transcript, firstMessage));
|
|
@@ -3057,7 +3070,7 @@ function discoverBackgroundAgentsForProject(projectPath, options = { readPreview
|
|
|
3057
3070
|
});
|
|
3058
3071
|
}
|
|
3059
3072
|
function discoverBackgroundAgents(projectPaths, options = { readPreviewText: true }) {
|
|
3060
|
-
return
|
|
3073
|
+
return Effect22.gen(function* () {
|
|
3061
3074
|
const agentsById = new Map;
|
|
3062
3075
|
for (const projectPath of new Set(projectPaths)) {
|
|
3063
3076
|
const projectAgents = yield* discoverBackgroundAgentsForProject(projectPath, options);
|
|
@@ -3100,7 +3113,7 @@ function createPlanSummary(entry, projectPath, filePath, displayName) {
|
|
|
3100
3113
|
};
|
|
3101
3114
|
}
|
|
3102
3115
|
function discoverMappedPlans(globalDb, composersById, agentsById, options = { loadDisplayName: true }) {
|
|
3103
|
-
return
|
|
3116
|
+
return Effect22.gen(function* () {
|
|
3104
3117
|
const plansById = new Map;
|
|
3105
3118
|
if (!globalDb) {
|
|
3106
3119
|
return plansById;
|
|
@@ -3121,7 +3134,7 @@ function discoverMappedPlans(globalDb, composersById, agentsById, options = { lo
|
|
|
3121
3134
|
if (!exists) {
|
|
3122
3135
|
continue;
|
|
3123
3136
|
}
|
|
3124
|
-
const displayName = options.loadDisplayName ? yield* readPlanDisplayName(filePath).pipe(
|
|
3137
|
+
const displayName = options.loadDisplayName ? yield* readPlanDisplayName(filePath).pipe(Effect22.catchAll(() => Effect22.succeed(""))) : "";
|
|
3125
3138
|
const summary = createPlanSummary(entry, projectPath, filePath, displayName || entry.name?.trim() || "Cursor plan");
|
|
3126
3139
|
if (!summary) {
|
|
3127
3140
|
continue;
|
|
@@ -3154,7 +3167,7 @@ function buildProjects(sessionsByProject) {
|
|
|
3154
3167
|
return projects;
|
|
3155
3168
|
}
|
|
3156
3169
|
function buildCursorProjectIndex(nativeId) {
|
|
3157
|
-
return
|
|
3170
|
+
return Effect22.gen(function* () {
|
|
3158
3171
|
const globalDb = yield* openCursorGlobalDb();
|
|
3159
3172
|
const sessionsByProject = new Map;
|
|
3160
3173
|
const composersById = new Map;
|
|
@@ -3184,7 +3197,7 @@ function buildCursorProjectIndex(nativeId) {
|
|
|
3184
3197
|
});
|
|
3185
3198
|
}
|
|
3186
3199
|
function buildCursorIndex() {
|
|
3187
|
-
return
|
|
3200
|
+
return Effect22.gen(function* () {
|
|
3188
3201
|
const globalDb = yield* openCursorGlobalDb();
|
|
3189
3202
|
const sessionsByProject = new Map;
|
|
3190
3203
|
const composersById = new Map;
|
|
@@ -3227,7 +3240,7 @@ function toSessionSummary(session) {
|
|
|
3227
3240
|
};
|
|
3228
3241
|
}
|
|
3229
3242
|
function buildCursorDiscoveryIndex() {
|
|
3230
|
-
return buildCursorIndex().pipe(
|
|
3243
|
+
return buildCursorIndex().pipe(Effect22.map((index) => ({
|
|
3231
3244
|
projects: index.projects,
|
|
3232
3245
|
sessionsByNativeId: new Map([...index.sessionsByProject.entries()].map(([projectPath, sessions]) => [
|
|
3233
3246
|
projectPath,
|
|
@@ -3236,7 +3249,7 @@ function buildCursorDiscoveryIndex() {
|
|
|
3236
3249
|
})));
|
|
3237
3250
|
}
|
|
3238
3251
|
function discoverCursorProjects() {
|
|
3239
|
-
return
|
|
3252
|
+
return Effect22.gen(function* () {
|
|
3240
3253
|
const globalDb = yield* openCursorGlobalDb();
|
|
3241
3254
|
const sessionsByProject = new Map;
|
|
3242
3255
|
const composersById = new Map;
|
|
@@ -3263,7 +3276,7 @@ function discoverCursorProjects() {
|
|
|
3263
3276
|
});
|
|
3264
3277
|
}
|
|
3265
3278
|
function listCursorSessions(nativeId) {
|
|
3266
|
-
return buildCursorProjectIndex(nativeId).pipe(
|
|
3279
|
+
return buildCursorProjectIndex(nativeId).pipe(Effect22.map((index) => {
|
|
3267
3280
|
const sessions = index.sessionsByProject.get(nativeId) ?? [];
|
|
3268
3281
|
sortByIsoDesc(sessions, (session) => session.timestamp);
|
|
3269
3282
|
return sessions.map(toSessionSummary);
|
|
@@ -3271,7 +3284,7 @@ function listCursorSessions(nativeId) {
|
|
|
3271
3284
|
}
|
|
3272
3285
|
|
|
3273
3286
|
// ../../packages/plugin-cursor/src/parser.ts
|
|
3274
|
-
import { Effect as
|
|
3287
|
+
import { Effect as Effect23 } from "effect";
|
|
3275
3288
|
var COMPOSER_PREFIX_REGEX = /^composer:/u;
|
|
3276
3289
|
var AGENT_PREFIX_REGEX = /^agent:/u;
|
|
3277
3290
|
var PLAN_PREFIX_REGEX = /^plan:/u;
|
|
@@ -3410,7 +3423,7 @@ function partialCursorSessionNotice(timestamp) {
|
|
|
3410
3423
|
Klovi could not reconstruct the full Composer transcript from Cursor state. Showing recovered content only.`);
|
|
3411
3424
|
}
|
|
3412
3425
|
function loadCursorComposerSession(summary) {
|
|
3413
|
-
return
|
|
3426
|
+
return Effect23.gen(function* () {
|
|
3414
3427
|
const globalDb = yield* openCursorGlobalDb();
|
|
3415
3428
|
const baseTimestampMs = summary.createdAtMs;
|
|
3416
3429
|
try {
|
|
@@ -3460,11 +3473,11 @@ function loadCursorComposerSession(summary) {
|
|
|
3460
3473
|
});
|
|
3461
3474
|
}
|
|
3462
3475
|
function loadCursorAgentSession(summary) {
|
|
3463
|
-
return
|
|
3464
|
-
const text = yield*
|
|
3476
|
+
return Effect23.gen(function* () {
|
|
3477
|
+
const text = yield* readFileText2(summary.filePath);
|
|
3465
3478
|
const turns = [];
|
|
3466
3479
|
let turnIndex = 0;
|
|
3467
|
-
|
|
3480
|
+
iterateJsonl2(text, ({ parsed }) => {
|
|
3468
3481
|
const line = parsed;
|
|
3469
3482
|
if (!Array.isArray(line.message?.content)) {
|
|
3470
3483
|
return;
|
|
@@ -3496,11 +3509,11 @@ function loadCursorAgentSession(summary) {
|
|
|
3496
3509
|
});
|
|
3497
3510
|
}
|
|
3498
3511
|
function loadCursorSession(nativeId, sessionId) {
|
|
3499
|
-
return
|
|
3512
|
+
return Effect23.gen(function* () {
|
|
3500
3513
|
const index = yield* buildCursorProjectIndex(nativeId);
|
|
3501
3514
|
const session = index.composersById.get(sessionId.replace(COMPOSER_PREFIX_REGEX, "")) ?? index.agentsById.get(sessionId.replace(AGENT_PREFIX_REGEX, "")) ?? index.plansById.get(sessionId.replace(PLAN_PREFIX_REGEX, ""));
|
|
3502
3515
|
if (!session || session.projectPath !== nativeId) {
|
|
3503
|
-
return yield*
|
|
3516
|
+
return yield* Effect23.fail(new Error(`Cursor session not found: ${sessionId}`));
|
|
3504
3517
|
}
|
|
3505
3518
|
if (session.kind === "composer") {
|
|
3506
3519
|
return yield* loadCursorComposerSession(session);
|
|
@@ -3516,38 +3529,38 @@ var cursorPlugin = {
|
|
|
3516
3529
|
id: "cursor",
|
|
3517
3530
|
displayName: "Cursor",
|
|
3518
3531
|
getDefaultDataDir: () => null,
|
|
3519
|
-
isDataAvailable:
|
|
3532
|
+
isDataAvailable: Effect24.gen(function* () {
|
|
3520
3533
|
const config = yield* PluginConfig;
|
|
3521
|
-
const fs = yield*
|
|
3522
|
-
const userDataExists = yield* fileExists3(config.dataDir).pipe(
|
|
3534
|
+
const fs = yield* FileSystem10.FileSystem;
|
|
3535
|
+
const userDataExists = yield* fileExists3(config.dataDir).pipe(Effect24.catchAll(() => Effect24.succeed(false)));
|
|
3523
3536
|
if (userDataExists) {
|
|
3524
3537
|
return true;
|
|
3525
3538
|
}
|
|
3526
|
-
const globalDbExists = yield* fs.exists(getCursorGlobalDbPath()).pipe(
|
|
3539
|
+
const globalDbExists = yield* fs.exists(getCursorGlobalDbPath()).pipe(Effect24.catchAll(() => Effect24.succeed(false)));
|
|
3527
3540
|
if (globalDbExists) {
|
|
3528
3541
|
return true;
|
|
3529
3542
|
}
|
|
3530
|
-
return yield* fs.exists(getCursorWorkspaceStorageDir()).pipe(
|
|
3543
|
+
return yield* fs.exists(getCursorWorkspaceStorageDir()).pipe(Effect24.catchAll(() => Effect24.succeed(false)));
|
|
3531
3544
|
}),
|
|
3532
|
-
discoverProjects: discoverCursorProjects().pipe(
|
|
3545
|
+
discoverProjects: discoverCursorProjects().pipe(Effect24.catchAll((err) => Effect24.fail(new PluginError({
|
|
3533
3546
|
pluginId: "cursor",
|
|
3534
3547
|
operation: "discoverProjects",
|
|
3535
3548
|
message: String(err),
|
|
3536
3549
|
cause: err
|
|
3537
3550
|
})))),
|
|
3538
|
-
discoverIndex: buildCursorDiscoveryIndex().pipe(
|
|
3551
|
+
discoverIndex: buildCursorDiscoveryIndex().pipe(Effect24.catchAll((err) => Effect24.fail(new PluginError({
|
|
3539
3552
|
pluginId: "cursor",
|
|
3540
3553
|
operation: "discoverIndex",
|
|
3541
3554
|
message: String(err),
|
|
3542
3555
|
cause: err
|
|
3543
3556
|
})))),
|
|
3544
|
-
listSessions: (nativeId) => listCursorSessions(nativeId).pipe(
|
|
3557
|
+
listSessions: (nativeId) => listCursorSessions(nativeId).pipe(Effect24.catchAll((err) => Effect24.fail(new PluginError({
|
|
3545
3558
|
pluginId: "cursor",
|
|
3546
3559
|
operation: "listSessions",
|
|
3547
3560
|
message: String(err),
|
|
3548
3561
|
cause: err
|
|
3549
3562
|
})))),
|
|
3550
|
-
loadSession: (nativeId, sessionId) => loadCursorSession(nativeId, sessionId).pipe(
|
|
3563
|
+
loadSession: (nativeId, sessionId) => loadCursorSession(nativeId, sessionId).pipe(Effect24.catchAll((err) => Effect24.fail(new PluginError({
|
|
3551
3564
|
pluginId: "cursor",
|
|
3552
3565
|
operation: "loadSession",
|
|
3553
3566
|
message: String(err),
|
|
@@ -3588,7 +3601,7 @@ class PluginRegistry2 extends PluginRegistry {
|
|
|
3588
3601
|
|
|
3589
3602
|
// ../../packages/server/src/services/auto-discover.ts
|
|
3590
3603
|
function createRegistry(settings) {
|
|
3591
|
-
return
|
|
3604
|
+
return Effect25.gen(function* () {
|
|
3592
3605
|
const registry = new PluginRegistry2;
|
|
3593
3606
|
for (const { plugin, defaultDir, defaultEnabled } of BUILTIN_PLUGIN_DESCRIPTORS) {
|
|
3594
3607
|
const pluginSettings = settings?.plugins[plugin.id];
|
|
@@ -3598,7 +3611,7 @@ function createRegistry(settings) {
|
|
|
3598
3611
|
}
|
|
3599
3612
|
const dataDir = pluginSettings?.dataDir ?? defaultDir;
|
|
3600
3613
|
const configLayer = makePluginConfigLayer({ dataDir });
|
|
3601
|
-
const available = yield* plugin.isDataAvailable.pipe(
|
|
3614
|
+
const available = yield* plugin.isDataAvailable.pipe(Effect25.provide(configLayer), Effect25.catchAll(() => Effect25.succeed(false)));
|
|
3602
3615
|
if (available) {
|
|
3603
3616
|
registry.register(plugin, { dataDir });
|
|
3604
3617
|
}
|
|
@@ -3608,12 +3621,12 @@ function createRegistry(settings) {
|
|
|
3608
3621
|
}
|
|
3609
3622
|
|
|
3610
3623
|
// ../../packages/server/src/services/onboarding-service.ts
|
|
3611
|
-
import { Effect as
|
|
3624
|
+
import { Effect as Effect27 } from "effect";
|
|
3612
3625
|
|
|
3613
3626
|
// ../../packages/server/src/services/settings.ts
|
|
3614
3627
|
import { dirname, join as join13 } from "node:path";
|
|
3615
|
-
import { FileSystem as
|
|
3616
|
-
import { Effect as
|
|
3628
|
+
import { FileSystem as FileSystem11 } from "@effect/platform";
|
|
3629
|
+
import { Effect as Effect26 } from "effect";
|
|
3617
3630
|
|
|
3618
3631
|
// ../../packages/server/src/services/errors.ts
|
|
3619
3632
|
import { Data as Data2 } from "effect";
|
|
@@ -3658,16 +3671,16 @@ function getDefaultSettings() {
|
|
|
3658
3671
|
};
|
|
3659
3672
|
}
|
|
3660
3673
|
function loadSettings(path) {
|
|
3661
|
-
return
|
|
3662
|
-
const fs = yield*
|
|
3663
|
-
const content = yield* fs.readFileString(path).pipe(
|
|
3674
|
+
return Effect26.gen(function* () {
|
|
3675
|
+
const fs = yield* FileSystem11.FileSystem;
|
|
3676
|
+
const content = yield* fs.readFileString(path).pipe(Effect26.catchAll(() => Effect26.succeed(null)));
|
|
3664
3677
|
if (content === null) {
|
|
3665
3678
|
return getDefaultSettings();
|
|
3666
3679
|
}
|
|
3667
|
-
const parsed = yield*
|
|
3680
|
+
const parsed = yield* Effect26.try({
|
|
3668
3681
|
try: () => JSON.parse(content),
|
|
3669
3682
|
catch: () => null
|
|
3670
|
-
}).pipe(
|
|
3683
|
+
}).pipe(Effect26.catchAll(() => Effect26.succeed(null)));
|
|
3671
3684
|
if (parsed === null || parsed["version"] !== 1 || typeof parsed["plugins"] !== "object") {
|
|
3672
3685
|
return getDefaultSettings();
|
|
3673
3686
|
}
|
|
@@ -3675,37 +3688,37 @@ function loadSettings(path) {
|
|
|
3675
3688
|
});
|
|
3676
3689
|
}
|
|
3677
3690
|
function saveSettings(path, settings) {
|
|
3678
|
-
return
|
|
3679
|
-
const fs = yield*
|
|
3691
|
+
return Effect26.gen(function* () {
|
|
3692
|
+
const fs = yield* FileSystem11.FileSystem;
|
|
3680
3693
|
const dir = dirname(path);
|
|
3681
|
-
yield* fs.makeDirectory(dir, { recursive: true }).pipe(
|
|
3694
|
+
yield* fs.makeDirectory(dir, { recursive: true }).pipe(Effect26.mapError((cause) => new SettingsWriteError({ path, cause })));
|
|
3682
3695
|
const tmpPath = join13(dir, `.settings-${Date.now()}.tmp`);
|
|
3683
|
-
yield* fs.writeFileString(tmpPath, JSON.stringify(settings, null, 2)).pipe(
|
|
3684
|
-
yield* fs.rename(tmpPath, path).pipe(
|
|
3696
|
+
yield* fs.writeFileString(tmpPath, JSON.stringify(settings, null, 2)).pipe(Effect26.mapError((cause) => new SettingsWriteError({ path, cause })));
|
|
3697
|
+
yield* fs.rename(tmpPath, path).pipe(Effect26.mapError((cause) => new SettingsWriteError({ path, cause })));
|
|
3685
3698
|
});
|
|
3686
3699
|
}
|
|
3687
3700
|
function settingsFileExists(path) {
|
|
3688
|
-
return
|
|
3689
|
-
const fs = yield*
|
|
3690
|
-
return yield* fs.exists(path).pipe(
|
|
3701
|
+
return Effect26.gen(function* () {
|
|
3702
|
+
const fs = yield* FileSystem11.FileSystem;
|
|
3703
|
+
return yield* fs.exists(path).pipe(Effect26.catchAll(() => Effect26.succeed(false)));
|
|
3691
3704
|
});
|
|
3692
3705
|
}
|
|
3693
3706
|
function deleteSettingsFile(path) {
|
|
3694
|
-
return
|
|
3695
|
-
const fs = yield*
|
|
3696
|
-
yield* fs.remove(path).pipe(
|
|
3707
|
+
return Effect26.gen(function* () {
|
|
3708
|
+
const fs = yield* FileSystem11.FileSystem;
|
|
3709
|
+
yield* fs.remove(path).pipe(Effect26.catchAll(() => Effect26.void));
|
|
3697
3710
|
});
|
|
3698
3711
|
}
|
|
3699
3712
|
|
|
3700
3713
|
// ../../packages/server/src/services/onboarding-service.ts
|
|
3701
3714
|
function isFirstLaunch(settingsPath) {
|
|
3702
|
-
return
|
|
3715
|
+
return Effect27.gen(function* () {
|
|
3703
3716
|
const exists = yield* settingsFileExists(settingsPath);
|
|
3704
3717
|
return { firstLaunch: !exists };
|
|
3705
3718
|
});
|
|
3706
3719
|
}
|
|
3707
3720
|
function completeOnboarding(settingsPath) {
|
|
3708
|
-
return
|
|
3721
|
+
return Effect27.gen(function* () {
|
|
3709
3722
|
const { firstLaunch } = yield* isFirstLaunch(settingsPath);
|
|
3710
3723
|
if (firstLaunch) {
|
|
3711
3724
|
yield* saveSettings(settingsPath, getDefaultSettings());
|
|
@@ -3714,7 +3727,7 @@ function completeOnboarding(settingsPath) {
|
|
|
3714
3727
|
});
|
|
3715
3728
|
}
|
|
3716
3729
|
function resetSettings(settingsPath) {
|
|
3717
|
-
return
|
|
3730
|
+
return Effect27.gen(function* () {
|
|
3718
3731
|
yield* deleteSettingsFile(settingsPath);
|
|
3719
3732
|
return { ok: true };
|
|
3720
3733
|
});
|
|
@@ -3722,60 +3735,81 @@ function resetSettings(settingsPath) {
|
|
|
3722
3735
|
|
|
3723
3736
|
// ../../packages/server/src/services/sessions-service.ts
|
|
3724
3737
|
init_src();
|
|
3725
|
-
import { Effect as
|
|
3738
|
+
import { Effect as Effect28 } from "effect";
|
|
3739
|
+
var DEFAULT_HEAD_SIZE = 100;
|
|
3726
3740
|
function getProjects(registry) {
|
|
3727
|
-
return
|
|
3741
|
+
return Effect28.gen(function* () {
|
|
3728
3742
|
const projects = yield* registry.discoverAllProjects();
|
|
3729
3743
|
return { projects };
|
|
3730
3744
|
});
|
|
3731
3745
|
}
|
|
3732
3746
|
function getSessions(registry, params) {
|
|
3733
|
-
return
|
|
3747
|
+
return Effect28.gen(function* () {
|
|
3734
3748
|
const discovered = yield* registry.discoverAllProjectsWithSessions();
|
|
3735
|
-
if (!discovered.projects.
|
|
3749
|
+
if (!discovered.projects.some((project) => project.encodedPath === params.encodedPath)) {
|
|
3736
3750
|
return { sessions: [] };
|
|
3737
3751
|
}
|
|
3738
3752
|
return { sessions: discovered.sessionsByEncodedPath.get(params.encodedPath) ?? [] };
|
|
3739
3753
|
});
|
|
3740
3754
|
}
|
|
3741
|
-
function
|
|
3742
|
-
return
|
|
3755
|
+
function loadSessionInternal(registry, params) {
|
|
3756
|
+
return Effect28.gen(function* () {
|
|
3743
3757
|
const parsed = parseSessionId(params.sessionId);
|
|
3744
3758
|
if (!(parsed.pluginId && parsed.rawSessionId)) {
|
|
3745
|
-
return yield*
|
|
3759
|
+
return yield* Effect28.fail(new InvalidSessionIdError({ value: params.sessionId }));
|
|
3746
3760
|
}
|
|
3747
3761
|
const { pluginId } = parsed;
|
|
3748
3762
|
const { rawSessionId } = parsed;
|
|
3749
3763
|
const projects = yield* registry.discoverAllProjects();
|
|
3750
3764
|
const project = projects.find((p) => p.encodedPath === params.project);
|
|
3751
3765
|
if (!project) {
|
|
3752
|
-
return yield*
|
|
3766
|
+
return yield* Effect28.fail(new ProjectNotFoundError({ encodedPath: params.project }));
|
|
3753
3767
|
}
|
|
3754
3768
|
const source = project.sources.find((s) => s.pluginId === pluginId);
|
|
3755
3769
|
if (!source) {
|
|
3756
|
-
return yield*
|
|
3770
|
+
return yield* Effect28.fail(new PluginSourceNotFoundError({ pluginId, project: params.project }));
|
|
3757
3771
|
}
|
|
3758
3772
|
const plugin = registry.getPlugin(pluginId);
|
|
3759
3773
|
const pluginConfig = registry.getPluginConfig(pluginId);
|
|
3760
3774
|
const configLayer = makePluginConfigLayer(pluginConfig);
|
|
3761
|
-
const sessionDetail = plugin.loadSessionDetail ? yield* plugin.loadSessionDetail(source.nativeId, rawSessionId).pipe(
|
|
3762
|
-
const session = sessionDetail?.session ?? (yield* plugin.loadSession(source.nativeId, rawSessionId).pipe(
|
|
3775
|
+
const sessionDetail = plugin.loadSessionDetail ? yield* plugin.loadSessionDetail(source.nativeId, rawSessionId).pipe(Effect28.provide(configLayer), Effect28.catchAll(() => Effect28.succeed(undefined))) : undefined;
|
|
3776
|
+
const session = sessionDetail?.session ?? (yield* plugin.loadSession(source.nativeId, rawSessionId).pipe(Effect28.provide(configLayer), Effect28.catchAll(() => Effect28.die("loadSession failed"))));
|
|
3763
3777
|
session.sessionId = encodeSessionId(pluginId, rawSessionId);
|
|
3764
3778
|
session.pluginId = pluginId;
|
|
3765
3779
|
session.planSessionId = sessionDetail?.planSessionId ? encodeSessionId(pluginId, sessionDetail.planSessionId) : undefined;
|
|
3766
3780
|
session.implSessionId = sessionDetail?.implSessionId ? encodeSessionId(pluginId, sessionDetail.implSessionId) : undefined;
|
|
3767
|
-
return { session };
|
|
3781
|
+
return { session, pluginId, rawSessionId };
|
|
3782
|
+
});
|
|
3783
|
+
}
|
|
3784
|
+
function getSession(registry, params) {
|
|
3785
|
+
return loadSessionInternal(registry, params).pipe(Effect28.map(({ session }) => ({ session })));
|
|
3786
|
+
}
|
|
3787
|
+
function getSessionHead(registry, params) {
|
|
3788
|
+
return Effect28.gen(function* () {
|
|
3789
|
+
const { session } = yield* loadSessionInternal(registry, params);
|
|
3790
|
+
const headSize = params.headSize ?? DEFAULT_HEAD_SIZE;
|
|
3791
|
+
const totalTurns = session.turns.length;
|
|
3792
|
+
const headSession = { ...session, turns: session.turns.slice(0, headSize) };
|
|
3793
|
+
return { session: headSession, totalTurns };
|
|
3794
|
+
});
|
|
3795
|
+
}
|
|
3796
|
+
function getSessionTail(registry, params) {
|
|
3797
|
+
return Effect28.gen(function* () {
|
|
3798
|
+
const { session } = yield* loadSessionInternal(registry, params);
|
|
3799
|
+
const fromTurn = Math.max(0, params.fromTurn);
|
|
3800
|
+
const totalTurns = session.turns.length;
|
|
3801
|
+
return { turns: fromTurn >= totalTurns ? [] : session.turns.slice(fromTurn) };
|
|
3768
3802
|
});
|
|
3769
3803
|
}
|
|
3770
3804
|
function getSubAgent(registry, params) {
|
|
3771
|
-
return
|
|
3805
|
+
return Effect28.gen(function* () {
|
|
3772
3806
|
const parsed = parseSessionId(params.sessionId);
|
|
3773
3807
|
if (!(parsed.pluginId && parsed.rawSessionId)) {
|
|
3774
|
-
return yield*
|
|
3808
|
+
return yield* Effect28.fail(new InvalidSessionIdError({ value: params.sessionId }));
|
|
3775
3809
|
}
|
|
3776
3810
|
const plugin = registry.getPlugin(parsed.pluginId);
|
|
3777
3811
|
if (!plugin.loadSubAgentSession) {
|
|
3778
|
-
return yield*
|
|
3812
|
+
return yield* Effect28.fail(new SubAgentNotSupportedError({ pluginId: parsed.pluginId }));
|
|
3779
3813
|
}
|
|
3780
3814
|
const pluginConfig = registry.getPluginConfig(parsed.pluginId);
|
|
3781
3815
|
const configLayer = makePluginConfigLayer(pluginConfig);
|
|
@@ -3783,7 +3817,7 @@ function getSubAgent(registry, params) {
|
|
|
3783
3817
|
sessionId: parsed.rawSessionId,
|
|
3784
3818
|
project: params.project,
|
|
3785
3819
|
agentId: params.agentId
|
|
3786
|
-
}).pipe(
|
|
3820
|
+
}).pipe(Effect28.provide(configLayer), Effect28.catchAll(() => Effect28.die("loadSubAgentSession failed")));
|
|
3787
3821
|
return { session };
|
|
3788
3822
|
});
|
|
3789
3823
|
}
|
|
@@ -3792,7 +3826,7 @@ function projectNameFromPath(fullPath) {
|
|
|
3792
3826
|
return parts.slice(-2).join("/");
|
|
3793
3827
|
}
|
|
3794
3828
|
function searchSessions(registry) {
|
|
3795
|
-
return
|
|
3829
|
+
return Effect28.gen(function* () {
|
|
3796
3830
|
const discovered = yield* registry.discoverAllProjectsWithSessions();
|
|
3797
3831
|
const allSessions = [];
|
|
3798
3832
|
for (const project of discovered.projects) {
|
|
@@ -3812,10 +3846,10 @@ function searchSessions(registry) {
|
|
|
3812
3846
|
}
|
|
3813
3847
|
|
|
3814
3848
|
// ../../packages/server/src/services/settings-service.ts
|
|
3815
|
-
import { Effect as
|
|
3849
|
+
import { Effect as Effect29 } from "effect";
|
|
3816
3850
|
var DEFAULT_CHECK_INTERVAL_HOURS = 6;
|
|
3817
3851
|
function buildPluginSettingsResponse(settingsPath) {
|
|
3818
|
-
return
|
|
3852
|
+
return Effect29.gen(function* () {
|
|
3819
3853
|
const settings = yield* loadSettings(settingsPath);
|
|
3820
3854
|
const plugins = BUILTIN_PLUGIN_DESCRIPTORS.map(({ plugin, defaultDir, defaultEnabled }) => {
|
|
3821
3855
|
const { id } = plugin;
|
|
@@ -3839,13 +3873,13 @@ function getPluginSettings(settingsPath) {
|
|
|
3839
3873
|
return buildPluginSettingsResponse(settingsPath);
|
|
3840
3874
|
}
|
|
3841
3875
|
function getGeneralSettings(settingsPath) {
|
|
3842
|
-
return
|
|
3876
|
+
return Effect29.gen(function* () {
|
|
3843
3877
|
const settings = yield* loadSettings(settingsPath);
|
|
3844
3878
|
return { showSecurityWarning: settings.general?.showSecurityWarning ?? true };
|
|
3845
3879
|
});
|
|
3846
3880
|
}
|
|
3847
3881
|
function updateGeneralSettings(settingsPath, params) {
|
|
3848
|
-
return
|
|
3882
|
+
return Effect29.gen(function* () {
|
|
3849
3883
|
const settings = yield* loadSettings(settingsPath);
|
|
3850
3884
|
if (!settings.general) {
|
|
3851
3885
|
settings.general = {};
|
|
@@ -3858,9 +3892,9 @@ function updateGeneralSettings(settingsPath, params) {
|
|
|
3858
3892
|
});
|
|
3859
3893
|
}
|
|
3860
3894
|
function updatePluginSetting(settingsPath, params) {
|
|
3861
|
-
return
|
|
3895
|
+
return Effect29.gen(function* () {
|
|
3862
3896
|
if (!BUILTIN_PLUGIN_ID_SET.has(params.pluginId)) {
|
|
3863
|
-
return yield*
|
|
3897
|
+
return yield* Effect29.fail(new UnknownPluginError({ pluginId: params.pluginId }));
|
|
3864
3898
|
}
|
|
3865
3899
|
const settings = yield* loadSettings(settingsPath);
|
|
3866
3900
|
const descriptor = BUILTIN_PLUGIN_DESCRIPTORS.find(({ plugin }) => plugin.id === params.pluginId);
|
|
@@ -3880,7 +3914,7 @@ function updatePluginSetting(settingsPath, params) {
|
|
|
3880
3914
|
});
|
|
3881
3915
|
}
|
|
3882
3916
|
function getUpdateSettings(settingsPath) {
|
|
3883
|
-
return
|
|
3917
|
+
return Effect29.gen(function* () {
|
|
3884
3918
|
const settings = yield* loadSettings(settingsPath);
|
|
3885
3919
|
return {
|
|
3886
3920
|
channel: settings.updates?.channel ?? "stable",
|
|
@@ -3890,7 +3924,7 @@ function getUpdateSettings(settingsPath) {
|
|
|
3890
3924
|
});
|
|
3891
3925
|
}
|
|
3892
3926
|
function updateUpdateSettings(settingsPath, params) {
|
|
3893
|
-
return
|
|
3927
|
+
return Effect29.gen(function* () {
|
|
3894
3928
|
const settings = yield* loadSettings(settingsPath);
|
|
3895
3929
|
if (!settings.updates) {
|
|
3896
3930
|
settings.updates = { channel: "stable", checkIntervalHours: 6, autoDownload: true };
|
|
@@ -3916,12 +3950,12 @@ function updateUpdateSettings(settingsPath, params) {
|
|
|
3916
3950
|
|
|
3917
3951
|
// ../../packages/server/src/services/stats-service.ts
|
|
3918
3952
|
import { dirname as dirname2, join as join14 } from "node:path";
|
|
3919
|
-
import { FileSystem as
|
|
3920
|
-
import { Effect as
|
|
3953
|
+
import { FileSystem as FileSystem12 } from "@effect/platform";
|
|
3954
|
+
import { Effect as Effect31 } from "effect";
|
|
3921
3955
|
|
|
3922
3956
|
// ../../packages/server/src/services/stats.ts
|
|
3923
3957
|
init_src();
|
|
3924
|
-
import { Effect as
|
|
3958
|
+
import { Effect as Effect30 } from "effect";
|
|
3925
3959
|
function emptyStats(projects = 0) {
|
|
3926
3960
|
return {
|
|
3927
3961
|
projects,
|
|
@@ -3981,7 +4015,7 @@ function countVisibleMessages(turns) {
|
|
|
3981
4015
|
return turns.filter((turn) => turn.kind !== "parse_error").length;
|
|
3982
4016
|
}
|
|
3983
4017
|
function collectSessionsWithProjects(registry, stats) {
|
|
3984
|
-
return
|
|
4018
|
+
return Effect30.gen(function* () {
|
|
3985
4019
|
const discovered = yield* registry.discoverAllProjectsWithSessions();
|
|
3986
4020
|
stats.projects = discovered.projects.length;
|
|
3987
4021
|
const sessionsWithProject = [];
|
|
@@ -4001,7 +4035,7 @@ function applyRecentSessionStats(stats, sessionsWithProject) {
|
|
|
4001
4035
|
stats.thisWeekSessions = recent.thisWeekSessions;
|
|
4002
4036
|
}
|
|
4003
4037
|
function loadSessionForStats(registry, project, session) {
|
|
4004
|
-
return
|
|
4038
|
+
return Effect30.gen(function* () {
|
|
4005
4039
|
if (!session.pluginId) {
|
|
4006
4040
|
return null;
|
|
4007
4041
|
}
|
|
@@ -4013,7 +4047,7 @@ function loadSessionForStats(registry, project, session) {
|
|
|
4013
4047
|
const pluginConfig = registry.getPluginConfig(session.pluginId);
|
|
4014
4048
|
const { rawSessionId } = parseSessionId(session.sessionId);
|
|
4015
4049
|
const configLayer = makePluginConfigLayer(pluginConfig);
|
|
4016
|
-
const loaded = yield* plugin.loadSession(source.nativeId, rawSessionId).pipe(
|
|
4050
|
+
const loaded = yield* plugin.loadSession(source.nativeId, rawSessionId).pipe(Effect30.provide(configLayer), Effect30.catchAll(() => Effect30.succeed(null)));
|
|
4017
4051
|
return loaded?.turns ?? null;
|
|
4018
4052
|
});
|
|
4019
4053
|
}
|
|
@@ -4045,11 +4079,11 @@ function applyTurnStats(stats, turns, fallbackModel) {
|
|
|
4045
4079
|
}
|
|
4046
4080
|
}
|
|
4047
4081
|
function computeStats(registry) {
|
|
4048
|
-
return
|
|
4082
|
+
return Effect30.gen(function* () {
|
|
4049
4083
|
const stats = emptyStats();
|
|
4050
4084
|
const sessionsWithProject = yield* collectSessionsWithProjects(registry, stats);
|
|
4051
4085
|
applyRecentSessionStats(stats, sessionsWithProject);
|
|
4052
|
-
const turnsPerSession = yield*
|
|
4086
|
+
const turnsPerSession = yield* Effect30.forEach(sessionsWithProject, (item) => loadSessionForStats(registry, item.project, item.session).pipe(Effect30.map((turns) => ({ item, turns }))), { concurrency: "unbounded" });
|
|
4053
4087
|
for (const { item, turns } of turnsPerSession) {
|
|
4054
4088
|
if (!turns) {
|
|
4055
4089
|
continue;
|
|
@@ -4120,27 +4154,27 @@ function isStatsCacheFile(value) {
|
|
|
4120
4154
|
return candidate["version"] === 1 && typeof candidate["cachedAt"] === "string" && isDashboardStats(candidate["stats"]);
|
|
4121
4155
|
}
|
|
4122
4156
|
function loadStatsCache(settingsPath) {
|
|
4123
|
-
return
|
|
4124
|
-
const fs = yield*
|
|
4157
|
+
return Effect31.gen(function* () {
|
|
4158
|
+
const fs = yield* FileSystem12.FileSystem;
|
|
4125
4159
|
const cachePath = getStatsCachePath(settingsPath);
|
|
4126
|
-
const raw = yield* fs.readFileString(cachePath).pipe(
|
|
4160
|
+
const raw = yield* fs.readFileString(cachePath).pipe(Effect31.catchAll(() => Effect31.succeed(null)));
|
|
4127
4161
|
if (raw === null) {
|
|
4128
4162
|
return null;
|
|
4129
4163
|
}
|
|
4130
|
-
const parsed = yield*
|
|
4164
|
+
const parsed = yield* Effect31.try({
|
|
4131
4165
|
try: () => JSON.parse(raw),
|
|
4132
4166
|
catch: () => null
|
|
4133
|
-
}).pipe(
|
|
4167
|
+
}).pipe(Effect31.catchAll(() => Effect31.succeed(null)));
|
|
4134
4168
|
return parsed !== null && isStatsCacheFile(parsed) ? parsed : null;
|
|
4135
4169
|
});
|
|
4136
4170
|
}
|
|
4137
4171
|
function persistStatsCache(settingsPath, stats, expectedEpoch) {
|
|
4138
|
-
return
|
|
4139
|
-
const fs = yield*
|
|
4172
|
+
return Effect31.gen(function* () {
|
|
4173
|
+
const fs = yield* FileSystem12.FileSystem;
|
|
4140
4174
|
const cachePath = getStatsCachePath(settingsPath);
|
|
4141
4175
|
const cacheDir = dirname2(cachePath);
|
|
4142
4176
|
const cachedAt = new Date().toISOString();
|
|
4143
|
-
yield* fs.makeDirectory(cacheDir, { recursive: true }).pipe(
|
|
4177
|
+
yield* fs.makeDirectory(cacheDir, { recursive: true }).pipe(Effect31.catchAll(() => Effect31.void));
|
|
4144
4178
|
if (getRefreshEpoch(cachePath) !== expectedEpoch) {
|
|
4145
4179
|
return null;
|
|
4146
4180
|
}
|
|
@@ -4150,15 +4184,15 @@ function persistStatsCache(settingsPath, stats, expectedEpoch) {
|
|
|
4150
4184
|
cachedAt,
|
|
4151
4185
|
stats
|
|
4152
4186
|
};
|
|
4153
|
-
const wroteTempFile = yield* fs.writeFileString(tmpPath, JSON.stringify(payload, null, 2)).pipe(
|
|
4187
|
+
const wroteTempFile = yield* fs.writeFileString(tmpPath, JSON.stringify(payload, null, 2)).pipe(Effect31.map(() => true), Effect31.catchAll(() => Effect31.succeed(false)));
|
|
4154
4188
|
if (!wroteTempFile) {
|
|
4155
4189
|
return null;
|
|
4156
4190
|
}
|
|
4157
4191
|
if (getRefreshEpoch(cachePath) !== expectedEpoch) {
|
|
4158
|
-
yield* fs.remove(tmpPath).pipe(
|
|
4192
|
+
yield* fs.remove(tmpPath).pipe(Effect31.catchAll(() => Effect31.void));
|
|
4159
4193
|
return null;
|
|
4160
4194
|
}
|
|
4161
|
-
const renamed = yield* fs.rename(tmpPath, cachePath).pipe(
|
|
4195
|
+
const renamed = yield* fs.rename(tmpPath, cachePath).pipe(Effect31.map(() => true), Effect31.catchAll(() => fs.remove(tmpPath).pipe(Effect31.catchAll(() => Effect31.void), Effect31.map(() => false))));
|
|
4162
4196
|
if (!renamed) {
|
|
4163
4197
|
return null;
|
|
4164
4198
|
}
|
|
@@ -4166,38 +4200,38 @@ function persistStatsCache(settingsPath, stats, expectedEpoch) {
|
|
|
4166
4200
|
});
|
|
4167
4201
|
}
|
|
4168
4202
|
function computeAndPersistStats(settingsPath, registry, expectedEpoch) {
|
|
4169
|
-
return
|
|
4203
|
+
return Effect31.gen(function* () {
|
|
4170
4204
|
const stats = yield* scanStats(registry);
|
|
4171
4205
|
const cachedAt = yield* persistStatsCache(settingsPath, stats, expectedEpoch);
|
|
4172
4206
|
return { stats, ...cachedAt ? { cachedAt } : {} };
|
|
4173
4207
|
});
|
|
4174
4208
|
}
|
|
4175
4209
|
function refreshStats(settingsPath, registry) {
|
|
4176
|
-
return
|
|
4210
|
+
return Effect31.gen(function* () {
|
|
4177
4211
|
const cachePath = getStatsCachePath(settingsPath);
|
|
4178
4212
|
const expectedEpoch = getRefreshEpoch(cachePath);
|
|
4179
4213
|
refreshingCachePaths.add(cachePath);
|
|
4180
4214
|
const result = yield* computeAndPersistStats(settingsPath, registry, expectedEpoch);
|
|
4181
4215
|
return { ...result, refreshing: false };
|
|
4182
|
-
}).pipe(
|
|
4216
|
+
}).pipe(Effect31.ensuring(Effect31.sync(() => {
|
|
4183
4217
|
refreshingCachePaths.delete(getStatsCachePath(settingsPath));
|
|
4184
4218
|
})));
|
|
4185
4219
|
}
|
|
4186
4220
|
function scheduleRefresh(settingsPath, registry) {
|
|
4187
|
-
return
|
|
4221
|
+
return Effect31.gen(function* () {
|
|
4188
4222
|
const cachePath = getStatsCachePath(settingsPath);
|
|
4189
4223
|
if (refreshingCachePaths.has(cachePath)) {
|
|
4190
4224
|
return;
|
|
4191
4225
|
}
|
|
4192
4226
|
const expectedEpoch = getRefreshEpoch(cachePath);
|
|
4193
4227
|
refreshingCachePaths.add(cachePath);
|
|
4194
|
-
yield* computeAndPersistStats(settingsPath, registry, expectedEpoch).pipe(
|
|
4228
|
+
yield* computeAndPersistStats(settingsPath, registry, expectedEpoch).pipe(Effect31.catchAllCause(() => Effect31.void), Effect31.ensuring(Effect31.sync(() => {
|
|
4195
4229
|
refreshingCachePaths.delete(cachePath);
|
|
4196
|
-
})),
|
|
4230
|
+
})), Effect31.forkDaemon);
|
|
4197
4231
|
});
|
|
4198
4232
|
}
|
|
4199
4233
|
function getStats(settingsPath, registry) {
|
|
4200
|
-
return
|
|
4234
|
+
return Effect31.gen(function* () {
|
|
4201
4235
|
const cachePath = getStatsCachePath(settingsPath);
|
|
4202
4236
|
const cached = yield* loadStatsCache(settingsPath);
|
|
4203
4237
|
if (!cached) {
|
|
@@ -4216,11 +4250,11 @@ function getStats(settingsPath, registry) {
|
|
|
4216
4250
|
});
|
|
4217
4251
|
}
|
|
4218
4252
|
function invalidateStatsCache(settingsPath) {
|
|
4219
|
-
return
|
|
4220
|
-
const fs = yield*
|
|
4253
|
+
return Effect31.gen(function* () {
|
|
4254
|
+
const fs = yield* FileSystem12.FileSystem;
|
|
4221
4255
|
const cachePath = getStatsCachePath(settingsPath);
|
|
4222
4256
|
bumpRefreshEpoch(cachePath);
|
|
4223
|
-
yield* fs.remove(cachePath).pipe(
|
|
4257
|
+
yield* fs.remove(cachePath).pipe(Effect31.catchAll(() => Effect31.void));
|
|
4224
4258
|
});
|
|
4225
4259
|
}
|
|
4226
4260
|
|
|
@@ -4236,27 +4270,29 @@ function getVersion(state) {
|
|
|
4236
4270
|
// ../../packages/server/src/effect/server-services.ts
|
|
4237
4271
|
class KloviServices extends Context4.Tag("@klovi/KloviServices")() {
|
|
4238
4272
|
}
|
|
4239
|
-
var KloviServicesLive = Layer6.effect(KloviServices,
|
|
4273
|
+
var KloviServicesLive = Layer6.effect(KloviServices, Effect32.gen(function* () {
|
|
4240
4274
|
const config = yield* ServerConfig;
|
|
4241
4275
|
const { settingsPath } = config;
|
|
4242
4276
|
const versionState = makeVersionState(config.version, config.commit);
|
|
4243
4277
|
const settings = yield* loadSettings(settingsPath);
|
|
4244
4278
|
let registry = yield* createRegistry(settings);
|
|
4245
|
-
const refreshRegistry = () =>
|
|
4279
|
+
const refreshRegistry = () => Effect32.gen(function* () {
|
|
4246
4280
|
const freshSettings = yield* loadSettings(settingsPath);
|
|
4247
4281
|
registry = yield* createRegistry(freshSettings);
|
|
4248
4282
|
});
|
|
4249
4283
|
return {
|
|
4250
4284
|
acceptRisks: () => completeOnboarding(settingsPath),
|
|
4251
|
-
getVersion: () =>
|
|
4285
|
+
getVersion: () => Effect32.succeed(getVersion(versionState)),
|
|
4252
4286
|
getStats: () => getStats(settingsPath, registry),
|
|
4253
4287
|
getProjects: () => getProjects(registry),
|
|
4254
4288
|
getSessions: (params) => getSessions(registry, params),
|
|
4255
4289
|
getSession: (params) => getSession(registry, params),
|
|
4290
|
+
getSessionHead: (params) => getSessionHead(registry, params),
|
|
4291
|
+
getSessionTail: (params) => getSessionTail(registry, params),
|
|
4256
4292
|
getSubAgent: (params) => getSubAgent(registry, params),
|
|
4257
4293
|
searchSessions: () => searchSessions(registry),
|
|
4258
4294
|
getPluginSettings: () => getPluginSettings(settingsPath),
|
|
4259
|
-
updatePluginSetting: (params) =>
|
|
4295
|
+
updatePluginSetting: (params) => Effect32.gen(function* () {
|
|
4260
4296
|
const result = yield* updatePluginSetting(settingsPath, params);
|
|
4261
4297
|
yield* refreshRegistry();
|
|
4262
4298
|
yield* invalidateStatsCache(settingsPath);
|
|
@@ -4265,7 +4301,7 @@ var KloviServicesLive = Layer6.effect(KloviServices, Effect31.gen(function* () {
|
|
|
4265
4301
|
getGeneralSettings: () => getGeneralSettings(settingsPath),
|
|
4266
4302
|
updateGeneralSettings: (params) => updateGeneralSettings(settingsPath, params),
|
|
4267
4303
|
isFirstLaunch: () => isFirstLaunch(settingsPath),
|
|
4268
|
-
resetSettings: () =>
|
|
4304
|
+
resetSettings: () => Effect32.gen(function* () {
|
|
4269
4305
|
const result = yield* resetSettings(settingsPath);
|
|
4270
4306
|
yield* refreshRegistry();
|
|
4271
4307
|
yield* invalidateStatsCache(settingsPath);
|
|
@@ -4317,28 +4353,28 @@ async function bootstrapServer(options, makeServe) {
|
|
|
4317
4353
|
resolveAddress = resolve;
|
|
4318
4354
|
rejectAddress = reject;
|
|
4319
4355
|
});
|
|
4320
|
-
const addressCapture = Layer8.effectDiscard(HttpServer.addressWith((address) =>
|
|
4356
|
+
const addressCapture = Layer8.effectDiscard(HttpServer.addressWith((address) => Effect33.gen(function* () {
|
|
4321
4357
|
const addr = address;
|
|
4322
4358
|
const url2 = `http://${addr.hostname}:${addr.port}`;
|
|
4323
|
-
yield*
|
|
4359
|
+
yield* Effect33.log(`Klovi server listening on ${url2}`);
|
|
4324
4360
|
resolveAddress(url2);
|
|
4325
4361
|
})));
|
|
4326
4362
|
const serveLayer = makeServe();
|
|
4327
4363
|
const fullLayer = Layer8.merge(serveLayer, addressCapture).pipe(Layer8.provide(servicesLayer), Layer8.provide(configLayer), Layer8.provide(platformLayer));
|
|
4328
|
-
const fiber =
|
|
4329
|
-
|
|
4364
|
+
const fiber = Effect33.runFork(Layer8.launch(fullLayer));
|
|
4365
|
+
Effect33.runFork(Fiber.join(fiber).pipe(Effect33.catchAllCause((cause) => Effect33.sync(() => rejectAddress(Cause.squash(cause))))));
|
|
4330
4366
|
const url = await addressPromise;
|
|
4331
4367
|
return {
|
|
4332
4368
|
url,
|
|
4333
4369
|
stop: () => {
|
|
4334
|
-
|
|
4370
|
+
Effect33.runFork(Fiber.interrupt(fiber));
|
|
4335
4371
|
}
|
|
4336
4372
|
};
|
|
4337
4373
|
}
|
|
4338
4374
|
|
|
4339
4375
|
// ../../packages/server/src/effect/http-app.ts
|
|
4340
4376
|
import { HttpRouter, HttpServer as HttpServer2, HttpServerRequest, HttpServerResponse } from "@effect/platform";
|
|
4341
|
-
import { Effect as
|
|
4377
|
+
import { Effect as Effect34 } from "effect";
|
|
4342
4378
|
|
|
4343
4379
|
// ../../packages/server/src/rpc-error.ts
|
|
4344
4380
|
class RPCError extends Error {
|
|
@@ -4379,7 +4415,7 @@ function mapDomainErrorToStatus(err) {
|
|
|
4379
4415
|
const message = err instanceof Error ? err.message : "Internal server error";
|
|
4380
4416
|
return { status: 500, message };
|
|
4381
4417
|
}
|
|
4382
|
-
var rpcHandler =
|
|
4418
|
+
var rpcHandler = Effect34.gen(function* () {
|
|
4383
4419
|
const services = yield* KloviServices;
|
|
4384
4420
|
const routeParams = yield* HttpRouter.params;
|
|
4385
4421
|
const req = yield* HttpServerRequest.HttpServerRequest;
|
|
@@ -4389,7 +4425,7 @@ var rpcHandler = Effect33.gen(function* () {
|
|
|
4389
4425
|
}
|
|
4390
4426
|
if (!isRpcMethod(method, services)) {
|
|
4391
4427
|
const httpNotFound = 404;
|
|
4392
|
-
return yield*
|
|
4428
|
+
return yield* Effect34.fail(new RPCError(httpNotFound, `Unknown method: ${method}`));
|
|
4393
4429
|
}
|
|
4394
4430
|
let params = {};
|
|
4395
4431
|
const bodyText = yield* req.text;
|
|
@@ -4403,39 +4439,39 @@ var rpcHandler = Effect33.gen(function* () {
|
|
|
4403
4439
|
const handler = services[method];
|
|
4404
4440
|
const value = yield* handler(params);
|
|
4405
4441
|
return HttpServerResponse.unsafeJson(value);
|
|
4406
|
-
}).pipe(
|
|
4442
|
+
}).pipe(Effect34.catchAll((err) => {
|
|
4407
4443
|
const { status, message } = mapDomainErrorToStatus(err);
|
|
4408
|
-
return
|
|
4444
|
+
return Effect34.succeed(HttpServerResponse.unsafeJson({ error: message }, { status }));
|
|
4409
4445
|
}));
|
|
4410
|
-
var emptyMethodHandler =
|
|
4446
|
+
var emptyMethodHandler = Effect34.succeed(HttpServerResponse.unsafeJson({ error: "Method name required" }, { status: 400 }));
|
|
4411
4447
|
var makeRpcRouter = () => HttpRouter.empty.pipe(HttpRouter.post("/api/rpc/", emptyMethodHandler), HttpRouter.post("/api/rpc/:method", rpcHandler));
|
|
4412
|
-
var notFoundHandler =
|
|
4413
|
-
var makeHttpApp = () => makeRpcRouter().pipe(
|
|
4448
|
+
var notFoundHandler = Effect34.succeed(HttpServerResponse.unsafeJson({ error: "Not found" }, { status: 404 }));
|
|
4449
|
+
var makeHttpApp = () => makeRpcRouter().pipe(Effect34.catchTag("RouteNotFound", () => notFoundHandler));
|
|
4414
4450
|
var makeServeLayer = () => makeHttpApp().pipe(HttpServer2.serve());
|
|
4415
4451
|
|
|
4416
4452
|
// src/http-app.ts
|
|
4417
4453
|
import { HttpServer as HttpServer3 } from "@effect/platform";
|
|
4418
|
-
import { Effect as
|
|
4454
|
+
import { Effect as Effect36 } from "effect";
|
|
4419
4455
|
|
|
4420
4456
|
// src/static-handler.ts
|
|
4421
4457
|
import { HttpServerRequest as HttpServerRequest2, HttpServerResponse as HttpServerResponse2 } from "@effect/platform";
|
|
4422
|
-
import { Effect as
|
|
4458
|
+
import { Effect as Effect35 } from "effect";
|
|
4423
4459
|
var notFound = HttpServerResponse2.unsafeJson({ error: "Not found" }, { status: 404 });
|
|
4424
4460
|
var isNavigationRequest = (pathname) => {
|
|
4425
4461
|
const lastSegment2 = pathname.split("/").pop() ?? "";
|
|
4426
4462
|
return !lastSegment2.includes(".");
|
|
4427
4463
|
};
|
|
4428
|
-
var makeStaticHandler = (staticDir) =>
|
|
4464
|
+
var makeStaticHandler = (staticDir) => Effect35.gen(function* () {
|
|
4429
4465
|
const req = yield* HttpServerRequest2.HttpServerRequest;
|
|
4430
4466
|
const url = new URL(req.url, "http://localhost");
|
|
4431
4467
|
const filePath = url.pathname === "/" ? "/index.html" : url.pathname;
|
|
4432
|
-
return yield* HttpServerResponse2.file(`${staticDir}${filePath}`).pipe(
|
|
4468
|
+
return yield* HttpServerResponse2.file(`${staticDir}${filePath}`).pipe(Effect35.orElse(() => isNavigationRequest(url.pathname) ? HttpServerResponse2.file(`${staticDir}/index.html`).pipe(Effect35.orElse(() => Effect35.succeed(notFound))) : Effect35.succeed(notFound)));
|
|
4433
4469
|
});
|
|
4434
4470
|
|
|
4435
4471
|
// src/http-app.ts
|
|
4436
4472
|
var makePackageHttpApp = (staticDir) => {
|
|
4437
4473
|
const router = makeRpcRouter();
|
|
4438
|
-
return router.pipe(
|
|
4474
|
+
return router.pipe(Effect36.catchTag("RouteNotFound", () => makeStaticHandler(staticDir)));
|
|
4439
4475
|
};
|
|
4440
4476
|
var makePackageServeLayer = (staticDir) => {
|
|
4441
4477
|
if (!staticDir) {
|