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