@kimuson/claude-code-viewer 0.4.4 → 0.4.6
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/main.js +1186 -129
- package/dist/main.js.map +4 -4
- package/dist/static/assets/index-C7tocoDE.js +1 -0
- package/dist/static/assets/index-CdW3wSEF.js +6 -0
- package/dist/static/assets/{index-yRcA1geA.js → index-Cl49R9Vk.js} +24 -24
- package/dist/static/assets/index-DuTdxGMm.css +1 -0
- package/dist/static/assets/{label-Dws6Ewzu.js → label-RSRHgdFZ.js} +6 -6
- package/dist/static/assets/{messages-DLi20Ryh.js → messages-BLhYHIq_.js} +1 -1
- package/dist/static/assets/{messages-H7YsW45d.js → messages-BQx1DYxh.js} +1 -1
- package/dist/static/assets/{messages-ChjmV9GI.js → messages-DZXmj7Ql.js} +1 -1
- package/dist/static/assets/{index-hJaFyqiY.js → session-Bqd0eFgp.js} +65 -54
- package/dist/static/assets/{index-DutDIkvw.js → session-CqkSC6oA.js} +1 -1
- package/dist/static/index.html +2 -2
- package/package.json +4 -4
- package/dist/static/assets/index-52rNYQAH.js +0 -1
- package/dist/static/assets/index-DFmNnbaj.js +0 -1
- package/dist/static/assets/index-DLA4zb1O.js +0 -6
- package/dist/static/assets/index-XKo9yk8p.js +0 -1
- package/dist/static/assets/index-tkMROyMY.css +0 -1
package/dist/main.js
CHANGED
|
@@ -6,7 +6,7 @@ import { resolve as resolve6 } from "node:path";
|
|
|
6
6
|
import { NodeContext as NodeContext2 } from "@effect/platform-node";
|
|
7
7
|
import { serve } from "@hono/node-server";
|
|
8
8
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
9
|
-
import { Effect as
|
|
9
|
+
import { Effect as Effect39 } from "effect";
|
|
10
10
|
|
|
11
11
|
// src/server/core/claude-code/presentation/ClaudeCodeController.ts
|
|
12
12
|
import { FileSystem as FileSystem4, Path as Path6 } from "@effect/platform";
|
|
@@ -382,40 +382,70 @@ var FileHistorySnapshotEntrySchema = z11.object({
|
|
|
382
382
|
});
|
|
383
383
|
|
|
384
384
|
// src/lib/conversation-schema/entry/QueueOperationEntrySchema.ts
|
|
385
|
+
import { z as z13 } from "zod";
|
|
386
|
+
|
|
387
|
+
// src/lib/conversation-schema/content/DocumentContentSchema.ts
|
|
385
388
|
import { z as z12 } from "zod";
|
|
386
|
-
var
|
|
387
|
-
z12.
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
389
|
+
var DocumentContentSchema = z12.object({
|
|
390
|
+
type: z12.literal("document"),
|
|
391
|
+
source: z12.union([
|
|
392
|
+
z12.object({
|
|
393
|
+
media_type: z12.literal("text/plain"),
|
|
394
|
+
type: z12.literal("text"),
|
|
395
|
+
data: z12.string()
|
|
396
|
+
}),
|
|
397
|
+
z12.object({
|
|
398
|
+
media_type: z12.enum(["application/pdf"]),
|
|
399
|
+
type: z12.literal("base64"),
|
|
400
|
+
data: z12.string()
|
|
401
|
+
})
|
|
402
|
+
])
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
// src/lib/conversation-schema/entry/QueueOperationEntrySchema.ts
|
|
406
|
+
var QueueOperationContentSchema = z13.union([
|
|
407
|
+
z13.string(),
|
|
408
|
+
TextContentSchema,
|
|
409
|
+
ToolResultContentSchema,
|
|
410
|
+
ImageContentSchema,
|
|
411
|
+
DocumentContentSchema
|
|
412
|
+
]);
|
|
413
|
+
var QueueOperationEntrySchema = z13.union([
|
|
414
|
+
z13.object({
|
|
415
|
+
type: z13.literal("queue-operation"),
|
|
416
|
+
operation: z13.literal("enqueue"),
|
|
417
|
+
content: z13.union([
|
|
418
|
+
z13.string(),
|
|
419
|
+
z13.array(z13.union([z13.string(), QueueOperationContentSchema]))
|
|
420
|
+
]),
|
|
421
|
+
sessionId: z13.string(),
|
|
422
|
+
timestamp: z13.iso.datetime()
|
|
393
423
|
}),
|
|
394
|
-
|
|
395
|
-
type:
|
|
396
|
-
operation:
|
|
397
|
-
sessionId:
|
|
398
|
-
timestamp:
|
|
424
|
+
z13.object({
|
|
425
|
+
type: z13.literal("queue-operation"),
|
|
426
|
+
operation: z13.literal("dequeue"),
|
|
427
|
+
sessionId: z13.string(),
|
|
428
|
+
timestamp: z13.iso.datetime()
|
|
399
429
|
})
|
|
400
430
|
]);
|
|
401
431
|
|
|
402
432
|
// src/lib/conversation-schema/entry/SummaryEntrySchema.ts
|
|
403
|
-
import { z as
|
|
404
|
-
var SummaryEntrySchema =
|
|
405
|
-
type:
|
|
406
|
-
summary:
|
|
407
|
-
leafUuid:
|
|
433
|
+
import { z as z14 } from "zod";
|
|
434
|
+
var SummaryEntrySchema = z14.object({
|
|
435
|
+
type: z14.literal("summary"),
|
|
436
|
+
summary: z14.string(),
|
|
437
|
+
leafUuid: z14.string().uuid()
|
|
408
438
|
});
|
|
409
439
|
|
|
410
440
|
// src/lib/conversation-schema/entry/SystemEntrySchema.ts
|
|
411
|
-
import { z as
|
|
441
|
+
import { z as z15 } from "zod";
|
|
412
442
|
var SystemEntrySchema = BaseEntrySchema.extend({
|
|
413
443
|
// discriminator
|
|
414
|
-
type:
|
|
444
|
+
type: z15.literal("system"),
|
|
415
445
|
// required
|
|
416
|
-
content:
|
|
417
|
-
toolUseID:
|
|
418
|
-
level:
|
|
446
|
+
content: z15.string(),
|
|
447
|
+
toolUseID: z15.string(),
|
|
448
|
+
level: z15.enum(["info"])
|
|
419
449
|
});
|
|
420
450
|
|
|
421
451
|
// src/lib/conversation-schema/entry/UserEntrySchema.ts
|
|
@@ -423,26 +453,6 @@ import { z as z17 } from "zod";
|
|
|
423
453
|
|
|
424
454
|
// src/lib/conversation-schema/message/UserMessageSchema.ts
|
|
425
455
|
import { z as z16 } from "zod";
|
|
426
|
-
|
|
427
|
-
// src/lib/conversation-schema/content/DocumentContentSchema.ts
|
|
428
|
-
import { z as z15 } from "zod";
|
|
429
|
-
var DocumentContentSchema = z15.object({
|
|
430
|
-
type: z15.literal("document"),
|
|
431
|
-
source: z15.union([
|
|
432
|
-
z15.object({
|
|
433
|
-
media_type: z15.literal("text/plain"),
|
|
434
|
-
type: z15.literal("text"),
|
|
435
|
-
data: z15.string()
|
|
436
|
-
}),
|
|
437
|
-
z15.object({
|
|
438
|
-
media_type: z15.enum(["application/pdf"]),
|
|
439
|
-
type: z15.literal("base64"),
|
|
440
|
-
data: z15.string()
|
|
441
|
-
})
|
|
442
|
-
])
|
|
443
|
-
});
|
|
444
|
-
|
|
445
|
-
// src/lib/conversation-schema/message/UserMessageSchema.ts
|
|
446
456
|
var UserMessageContentSchema = z16.union([
|
|
447
457
|
z16.string(),
|
|
448
458
|
TextContentSchema,
|
|
@@ -1181,13 +1191,64 @@ import { Context as Context17, Effect as Effect20, Layer as Layer18 } from "effe
|
|
|
1181
1191
|
|
|
1182
1192
|
// src/server/core/platform/services/UserConfigService.ts
|
|
1183
1193
|
import { Context as Context11, Effect as Effect13, Layer as Layer12, Ref as Ref5 } from "effect";
|
|
1194
|
+
|
|
1195
|
+
// src/lib/i18n/localeDetection.ts
|
|
1196
|
+
var DEFAULT_LOCALE = "en";
|
|
1197
|
+
var normalizeTag = (tag) => {
|
|
1198
|
+
if (!tag) {
|
|
1199
|
+
return void 0;
|
|
1200
|
+
}
|
|
1201
|
+
const normalized = tag.trim().toLowerCase().replaceAll("_", "-");
|
|
1202
|
+
if (normalized.length === 0 || normalized === "*") {
|
|
1203
|
+
return void 0;
|
|
1204
|
+
}
|
|
1205
|
+
if (normalized.startsWith("zh")) {
|
|
1206
|
+
return "zh_CN";
|
|
1207
|
+
}
|
|
1208
|
+
if (normalized.startsWith("ja") || normalized.startsWith("jp")) {
|
|
1209
|
+
return "ja";
|
|
1210
|
+
}
|
|
1211
|
+
if (normalized.startsWith("en")) {
|
|
1212
|
+
return "en";
|
|
1213
|
+
}
|
|
1214
|
+
return void 0;
|
|
1215
|
+
};
|
|
1216
|
+
var detectLocaleFromAcceptLanguage = (header) => {
|
|
1217
|
+
if (!header) {
|
|
1218
|
+
return void 0;
|
|
1219
|
+
}
|
|
1220
|
+
const preferences = header.split(",").map((part, index) => {
|
|
1221
|
+
const [rawTag, ...params] = part.trim().split(";");
|
|
1222
|
+
const qParam = params.map((param) => param.trim()).find((param) => param.startsWith("q="));
|
|
1223
|
+
const quality = qParam ? Number.parseFloat(qParam.slice(2)) : 1;
|
|
1224
|
+
return {
|
|
1225
|
+
tag: rawTag,
|
|
1226
|
+
quality: Number.isNaN(quality) ? 1 : quality,
|
|
1227
|
+
index
|
|
1228
|
+
};
|
|
1229
|
+
}).filter((item) => Boolean(item.tag)).sort((a, b) => {
|
|
1230
|
+
if (b.quality !== a.quality) {
|
|
1231
|
+
return b.quality - a.quality;
|
|
1232
|
+
}
|
|
1233
|
+
return a.index - b.index;
|
|
1234
|
+
});
|
|
1235
|
+
for (const preference of preferences) {
|
|
1236
|
+
const locale = normalizeTag(preference.tag);
|
|
1237
|
+
if (locale) {
|
|
1238
|
+
return locale;
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
return void 0;
|
|
1242
|
+
};
|
|
1243
|
+
|
|
1244
|
+
// src/server/core/platform/services/UserConfigService.ts
|
|
1184
1245
|
var LayerImpl10 = Effect13.gen(function* () {
|
|
1185
1246
|
const configRef = yield* Ref5.make({
|
|
1186
1247
|
hideNoUserMessageSession: true,
|
|
1187
1248
|
unifySameTitleSession: false,
|
|
1188
1249
|
enterKeyBehavior: "shift-enter-send",
|
|
1189
1250
|
permissionMode: "default",
|
|
1190
|
-
locale:
|
|
1251
|
+
locale: DEFAULT_LOCALE,
|
|
1191
1252
|
theme: "system"
|
|
1192
1253
|
});
|
|
1193
1254
|
const setUserConfig = (newConfig) => Effect13.gen(function* () {
|
|
@@ -1377,6 +1438,97 @@ var VirtualConversationDatabase = class extends Context12.Tag(
|
|
|
1377
1438
|
import { FileSystem as FileSystem5 } from "@effect/platform";
|
|
1378
1439
|
import { Context as Context13, Effect as Effect15, Layer as Layer14, Ref as Ref7 } from "effect";
|
|
1379
1440
|
|
|
1441
|
+
// src/server/core/session/constants/pricing.ts
|
|
1442
|
+
var MODEL_PRICING = {
|
|
1443
|
+
"claude-3.5-sonnet": {
|
|
1444
|
+
input: 3,
|
|
1445
|
+
output: 15,
|
|
1446
|
+
cache_creation: 3.75,
|
|
1447
|
+
cache_read: 0.3
|
|
1448
|
+
},
|
|
1449
|
+
"claude-3-opus": {
|
|
1450
|
+
input: 15,
|
|
1451
|
+
output: 75,
|
|
1452
|
+
cache_creation: 18.75,
|
|
1453
|
+
cache_read: 1.5
|
|
1454
|
+
},
|
|
1455
|
+
"claude-3-haiku": {
|
|
1456
|
+
input: 0.25,
|
|
1457
|
+
output: 1.25,
|
|
1458
|
+
cache_creation: 0.3,
|
|
1459
|
+
cache_read: 0.03
|
|
1460
|
+
},
|
|
1461
|
+
"claude-instant-1.2": {
|
|
1462
|
+
input: 1.63,
|
|
1463
|
+
output: 5.51,
|
|
1464
|
+
cache_creation: 2.0375,
|
|
1465
|
+
// 1.63 * 1.25
|
|
1466
|
+
cache_read: 0.163
|
|
1467
|
+
// 1.63 * 0.1
|
|
1468
|
+
},
|
|
1469
|
+
"claude-2": {
|
|
1470
|
+
input: 8,
|
|
1471
|
+
output: 24,
|
|
1472
|
+
cache_creation: 10,
|
|
1473
|
+
// 8.0 * 1.25
|
|
1474
|
+
cache_read: 0.8
|
|
1475
|
+
// 8.0 * 0.1
|
|
1476
|
+
}
|
|
1477
|
+
};
|
|
1478
|
+
var DEFAULT_MODEL_PRICING = MODEL_PRICING["claude-3.5-sonnet"];
|
|
1479
|
+
|
|
1480
|
+
// src/server/core/session/functions/calculateSessionCost.ts
|
|
1481
|
+
function normalizeModelName(modelName) {
|
|
1482
|
+
const normalized = modelName.toLowerCase();
|
|
1483
|
+
if (normalized.includes("sonnet-4") || normalized.includes("3-5-sonnet") || normalized.includes("3.5-sonnet")) {
|
|
1484
|
+
return "claude-3.5-sonnet";
|
|
1485
|
+
}
|
|
1486
|
+
if (normalized.includes("3-opus") || normalized.includes("opus-20")) {
|
|
1487
|
+
return "claude-3-opus";
|
|
1488
|
+
}
|
|
1489
|
+
if (normalized.includes("3-haiku") || normalized.includes("haiku-20")) {
|
|
1490
|
+
return "claude-3-haiku";
|
|
1491
|
+
}
|
|
1492
|
+
if (normalized.includes("instant-1.2") || normalized.includes("instant-1")) {
|
|
1493
|
+
return "claude-instant-1.2";
|
|
1494
|
+
}
|
|
1495
|
+
if (normalized.startsWith("claude-2")) {
|
|
1496
|
+
return "claude-2";
|
|
1497
|
+
}
|
|
1498
|
+
return "claude-3.5-sonnet";
|
|
1499
|
+
}
|
|
1500
|
+
function getModelPricing(modelName) {
|
|
1501
|
+
const normalized = normalizeModelName(modelName);
|
|
1502
|
+
return MODEL_PRICING[normalized] ?? DEFAULT_MODEL_PRICING;
|
|
1503
|
+
}
|
|
1504
|
+
function calculateTokenCost(usage, modelName) {
|
|
1505
|
+
const pricing = getModelPricing(modelName);
|
|
1506
|
+
const inputMTok = usage.input_tokens / 1e6;
|
|
1507
|
+
const outputMTok = usage.output_tokens / 1e6;
|
|
1508
|
+
const cacheCreationMTok = (usage.cache_creation_input_tokens ?? 0) / 1e6;
|
|
1509
|
+
const cacheReadMTok = (usage.cache_read_input_tokens ?? 0) / 1e6;
|
|
1510
|
+
const inputTokensUsd = inputMTok * pricing.input;
|
|
1511
|
+
const outputTokensUsd = outputMTok * pricing.output;
|
|
1512
|
+
const cacheCreationUsd = cacheCreationMTok * pricing.cache_creation;
|
|
1513
|
+
const cacheReadUsd = cacheReadMTok * pricing.cache_read;
|
|
1514
|
+
const totalUsd = inputTokensUsd + outputTokensUsd + cacheCreationUsd + cacheReadUsd;
|
|
1515
|
+
return {
|
|
1516
|
+
totalUsd,
|
|
1517
|
+
breakdown: {
|
|
1518
|
+
inputTokensUsd,
|
|
1519
|
+
outputTokensUsd,
|
|
1520
|
+
cacheCreationUsd,
|
|
1521
|
+
cacheReadUsd
|
|
1522
|
+
},
|
|
1523
|
+
tokenUsage: {
|
|
1524
|
+
inputTokens: usage.input_tokens,
|
|
1525
|
+
outputTokens: usage.output_tokens,
|
|
1526
|
+
cacheCreationTokens: usage.cache_creation_input_tokens ?? 0,
|
|
1527
|
+
cacheReadTokens: usage.cache_read_input_tokens ?? 0
|
|
1528
|
+
}
|
|
1529
|
+
};
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1380
1532
|
// src/server/core/session/functions/extractFirstUserText.ts
|
|
1381
1533
|
var extractFirstUserText = (conversation) => {
|
|
1382
1534
|
if (conversation.type !== "user") {
|
|
@@ -1463,6 +1615,68 @@ var SessionMetaService = class extends Context13.Tag("SessionMetaService")() {
|
|
|
1463
1615
|
}
|
|
1464
1616
|
return firstUserMessage;
|
|
1465
1617
|
});
|
|
1618
|
+
const aggregateTokenUsageAndCost = (content) => {
|
|
1619
|
+
let totalInputTokens = 0;
|
|
1620
|
+
let totalOutputTokens = 0;
|
|
1621
|
+
let totalCacheCreationTokens = 0;
|
|
1622
|
+
let totalCacheReadTokens = 0;
|
|
1623
|
+
let totalInputTokensUsd = 0;
|
|
1624
|
+
let totalOutputTokensUsd = 0;
|
|
1625
|
+
let totalCacheCreationUsd = 0;
|
|
1626
|
+
let totalCacheReadUsd = 0;
|
|
1627
|
+
let lastModelName = "claude-3.5-sonnet";
|
|
1628
|
+
const conversations = parseJsonl(content);
|
|
1629
|
+
for (const conversation of conversations) {
|
|
1630
|
+
if (conversation.type === "assistant") {
|
|
1631
|
+
const usage = conversation.message.usage;
|
|
1632
|
+
const modelName = conversation.message.model;
|
|
1633
|
+
const messageCost = calculateTokenCost(
|
|
1634
|
+
{
|
|
1635
|
+
input_tokens: usage.input_tokens,
|
|
1636
|
+
output_tokens: usage.output_tokens,
|
|
1637
|
+
cache_creation_input_tokens: usage.cache_creation_input_tokens ?? 0,
|
|
1638
|
+
cache_read_input_tokens: usage.cache_read_input_tokens ?? 0
|
|
1639
|
+
},
|
|
1640
|
+
modelName
|
|
1641
|
+
);
|
|
1642
|
+
totalInputTokens += usage.input_tokens;
|
|
1643
|
+
totalOutputTokens += usage.output_tokens;
|
|
1644
|
+
totalCacheCreationTokens += usage.cache_creation_input_tokens ?? 0;
|
|
1645
|
+
totalCacheReadTokens += usage.cache_read_input_tokens ?? 0;
|
|
1646
|
+
totalInputTokensUsd += messageCost.breakdown.inputTokensUsd;
|
|
1647
|
+
totalOutputTokensUsd += messageCost.breakdown.outputTokensUsd;
|
|
1648
|
+
totalCacheCreationUsd += messageCost.breakdown.cacheCreationUsd;
|
|
1649
|
+
totalCacheReadUsd += messageCost.breakdown.cacheReadUsd;
|
|
1650
|
+
lastModelName = modelName;
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
const totalCost = {
|
|
1654
|
+
totalUsd: totalInputTokensUsd + totalOutputTokensUsd + totalCacheCreationUsd + totalCacheReadUsd,
|
|
1655
|
+
breakdown: {
|
|
1656
|
+
inputTokensUsd: totalInputTokensUsd,
|
|
1657
|
+
outputTokensUsd: totalOutputTokensUsd,
|
|
1658
|
+
cacheCreationUsd: totalCacheCreationUsd,
|
|
1659
|
+
cacheReadUsd: totalCacheReadUsd
|
|
1660
|
+
},
|
|
1661
|
+
tokenUsage: {
|
|
1662
|
+
inputTokens: totalInputTokens,
|
|
1663
|
+
outputTokens: totalOutputTokens,
|
|
1664
|
+
cacheCreationTokens: totalCacheCreationTokens,
|
|
1665
|
+
cacheReadTokens: totalCacheReadTokens
|
|
1666
|
+
}
|
|
1667
|
+
};
|
|
1668
|
+
const aggregatedUsage = {
|
|
1669
|
+
input_tokens: totalInputTokens,
|
|
1670
|
+
output_tokens: totalOutputTokens,
|
|
1671
|
+
cache_creation_input_tokens: totalCacheCreationTokens,
|
|
1672
|
+
cache_read_input_tokens: totalCacheReadTokens
|
|
1673
|
+
};
|
|
1674
|
+
return {
|
|
1675
|
+
totalUsage: aggregatedUsage,
|
|
1676
|
+
totalCost,
|
|
1677
|
+
modelName: lastModelName
|
|
1678
|
+
};
|
|
1679
|
+
};
|
|
1466
1680
|
const getSessionMeta = (projectId, sessionId) => Effect15.gen(function* () {
|
|
1467
1681
|
const metaCache = yield* Ref7.get(sessionMetaCacheRef);
|
|
1468
1682
|
const cached = metaCache.get(sessionId);
|
|
@@ -1476,9 +1690,15 @@ var SessionMetaService = class extends Context13.Tag("SessionMetaService")() {
|
|
|
1476
1690
|
sessionPath,
|
|
1477
1691
|
lines
|
|
1478
1692
|
);
|
|
1693
|
+
const { totalCost } = aggregateTokenUsageAndCost(content);
|
|
1479
1694
|
const sessionMeta = {
|
|
1480
1695
|
messageCount: lines.length,
|
|
1481
|
-
firstUserMessage
|
|
1696
|
+
firstUserMessage,
|
|
1697
|
+
cost: {
|
|
1698
|
+
totalUsd: totalCost.totalUsd,
|
|
1699
|
+
breakdown: totalCost.breakdown,
|
|
1700
|
+
tokenUsage: totalCost.tokenUsage
|
|
1701
|
+
}
|
|
1482
1702
|
};
|
|
1483
1703
|
yield* Ref7.update(sessionMetaCacheRef, (cache) => {
|
|
1484
1704
|
cache.set(sessionId, sessionMeta);
|
|
@@ -1569,7 +1789,22 @@ var LayerImpl11 = Effect16.gen(function* () {
|
|
|
1569
1789
|
jsonlFilePath: `${decodeProjectId(projectId)}/${sessionId}.jsonl`,
|
|
1570
1790
|
meta: {
|
|
1571
1791
|
messageCount: 0,
|
|
1572
|
-
firstUserMessage: null
|
|
1792
|
+
firstUserMessage: null,
|
|
1793
|
+
cost: {
|
|
1794
|
+
totalUsd: 0,
|
|
1795
|
+
breakdown: {
|
|
1796
|
+
inputTokensUsd: 0,
|
|
1797
|
+
outputTokensUsd: 0,
|
|
1798
|
+
cacheCreationUsd: 0,
|
|
1799
|
+
cacheReadUsd: 0
|
|
1800
|
+
},
|
|
1801
|
+
tokenUsage: {
|
|
1802
|
+
inputTokens: 0,
|
|
1803
|
+
outputTokens: 0,
|
|
1804
|
+
cacheCreationTokens: 0,
|
|
1805
|
+
cacheReadTokens: 0
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1573
1808
|
},
|
|
1574
1809
|
conversations: virtualConversation.conversations,
|
|
1575
1810
|
lastModifiedAt: lastConversation !== void 0 ? new Date(lastConversation.timestamp) : /* @__PURE__ */ new Date()
|
|
@@ -1670,7 +1905,22 @@ var LayerImpl11 = Effect16.gen(function* () {
|
|
|
1670
1905
|
lastModifiedAt: last !== void 0 ? new Date(last.timestamp) : /* @__PURE__ */ new Date(),
|
|
1671
1906
|
meta: {
|
|
1672
1907
|
messageCount: conversations.length,
|
|
1673
|
-
firstUserMessage: firstUserText ? parseUserMessage(firstUserText) : null
|
|
1908
|
+
firstUserMessage: firstUserText ? parseUserMessage(firstUserText) : null,
|
|
1909
|
+
cost: {
|
|
1910
|
+
totalUsd: 0,
|
|
1911
|
+
breakdown: {
|
|
1912
|
+
inputTokensUsd: 0,
|
|
1913
|
+
outputTokensUsd: 0,
|
|
1914
|
+
cacheCreationUsd: 0,
|
|
1915
|
+
cacheReadUsd: 0
|
|
1916
|
+
},
|
|
1917
|
+
tokenUsage: {
|
|
1918
|
+
inputTokens: 0,
|
|
1919
|
+
outputTokens: 0,
|
|
1920
|
+
cacheCreationTokens: 0,
|
|
1921
|
+
cacheReadTokens: 0
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1674
1924
|
}
|
|
1675
1925
|
};
|
|
1676
1926
|
}).sort((a, b) => {
|
|
@@ -4740,10 +4990,791 @@ var SchedulerController = class extends Context28.Tag("SchedulerController")() {
|
|
|
4740
4990
|
};
|
|
4741
4991
|
|
|
4742
4992
|
// src/server/core/session/presentation/SessionController.ts
|
|
4743
|
-
import { Context as Context29, Effect as
|
|
4744
|
-
|
|
4993
|
+
import { Context as Context29, Effect as Effect35, Layer as Layer30 } from "effect";
|
|
4994
|
+
|
|
4995
|
+
// src/server/core/session/services/ExportService.ts
|
|
4996
|
+
import { Effect as Effect34 } from "effect";
|
|
4997
|
+
var escapeHtml = (text) => {
|
|
4998
|
+
const map = {
|
|
4999
|
+
"&": "&",
|
|
5000
|
+
"<": "<",
|
|
5001
|
+
">": ">",
|
|
5002
|
+
'"': """,
|
|
5003
|
+
"'": "'"
|
|
5004
|
+
};
|
|
5005
|
+
return text.replace(/[&<>"']/g, (char) => map[char] ?? char);
|
|
5006
|
+
};
|
|
5007
|
+
var formatJsonWithNewlines = (obj) => {
|
|
5008
|
+
const jsonString = JSON.stringify(obj, null, 2);
|
|
5009
|
+
return jsonString.replace(/\\n/g, "\n").replace(/\\t/g, " ").replace(/\\r/g, "\r");
|
|
5010
|
+
};
|
|
5011
|
+
var formatTimestamp = (timestamp) => {
|
|
5012
|
+
const date = new Date(timestamp);
|
|
5013
|
+
return date.toLocaleString("en-US", {
|
|
5014
|
+
year: "numeric",
|
|
5015
|
+
month: "short",
|
|
5016
|
+
day: "numeric",
|
|
5017
|
+
hour: "2-digit",
|
|
5018
|
+
minute: "2-digit",
|
|
5019
|
+
second: "2-digit"
|
|
5020
|
+
});
|
|
5021
|
+
};
|
|
5022
|
+
var renderMarkdown = (content) => {
|
|
5023
|
+
let html = escapeHtml(content);
|
|
5024
|
+
html = html.replace(
|
|
5025
|
+
/```(\w+)?\n([\s\S]*?)```/g,
|
|
5026
|
+
(_match, lang, code) => `
|
|
5027
|
+
<div class="code-block">
|
|
5028
|
+
${lang ? `<div class="code-header"><span class="code-lang">${escapeHtml(lang.toUpperCase())}</span></div>` : ""}
|
|
5029
|
+
<pre><code class="language-${escapeHtml(lang || "text")}">${code.trim()}</code></pre>
|
|
5030
|
+
</div>
|
|
5031
|
+
`
|
|
5032
|
+
);
|
|
5033
|
+
html = html.replace(/`([^`]+)`/g, '<code class="inline-code">$1</code>');
|
|
5034
|
+
html = html.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
|
|
5035
|
+
html = html.replace(/\*(.+?)\*/g, "<em>$1</em>");
|
|
5036
|
+
html = html.replace(/^### (.+)$/gm, '<h3 class="markdown-h3">$1</h3>');
|
|
5037
|
+
html = html.replace(/^## (.+)$/gm, '<h2 class="markdown-h2">$1</h2>');
|
|
5038
|
+
html = html.replace(/^# (.+)$/gm, '<h1 class="markdown-h1">$1</h1>');
|
|
5039
|
+
html = html.replace(
|
|
5040
|
+
/\[([^\]]+)\]\(([^)]+)\)/g,
|
|
5041
|
+
'<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>'
|
|
5042
|
+
);
|
|
5043
|
+
html = html.split("\n\n").map((para) => {
|
|
5044
|
+
if (para.startsWith("<h") || para.startsWith("<div") || para.startsWith("<pre") || para.trim() === "") {
|
|
5045
|
+
return para;
|
|
5046
|
+
}
|
|
5047
|
+
return `<p class="markdown-p">${para.replace(/\n/g, "<br>")}</p>`;
|
|
5048
|
+
}).join("\n");
|
|
5049
|
+
return html;
|
|
5050
|
+
};
|
|
5051
|
+
var renderUserEntry = (entry) => {
|
|
5052
|
+
const contentArray = Array.isArray(entry.message.content) ? entry.message.content : [entry.message.content];
|
|
5053
|
+
const contentHtml = contentArray.map((msg) => {
|
|
5054
|
+
if (typeof msg === "string") {
|
|
5055
|
+
return `<div class="markdown-content">${renderMarkdown(msg)}</div>`;
|
|
5056
|
+
}
|
|
5057
|
+
if (msg.type === "text") {
|
|
5058
|
+
return `<div class="markdown-content">${renderMarkdown(msg.text)}</div>`;
|
|
5059
|
+
}
|
|
5060
|
+
if (msg.type === "image") {
|
|
5061
|
+
return `<img src="data:${msg.source.media_type};base64,${msg.source.data}" alt="User uploaded image" class="message-image" />`;
|
|
5062
|
+
}
|
|
5063
|
+
if (msg.type === "document") {
|
|
5064
|
+
return `<div class="document-content"><strong>Document:</strong> ${escapeHtml(msg.source.media_type)}</div>`;
|
|
5065
|
+
}
|
|
5066
|
+
if (msg.type === "tool_result") {
|
|
5067
|
+
return "";
|
|
5068
|
+
}
|
|
5069
|
+
return "";
|
|
5070
|
+
}).join("");
|
|
5071
|
+
if (!contentHtml.trim()) {
|
|
5072
|
+
return "";
|
|
5073
|
+
}
|
|
5074
|
+
return `
|
|
5075
|
+
<div class="conversation-entry user-entry">
|
|
5076
|
+
<div class="entry-header">
|
|
5077
|
+
<span class="entry-role">User</span>
|
|
5078
|
+
<span class="entry-timestamp">${formatTimestamp(entry.timestamp)}</span>
|
|
5079
|
+
</div>
|
|
5080
|
+
<div class="entry-content">
|
|
5081
|
+
${contentHtml}
|
|
5082
|
+
</div>
|
|
5083
|
+
</div>
|
|
5084
|
+
`;
|
|
5085
|
+
};
|
|
5086
|
+
var renderAssistantEntry = (entry) => {
|
|
5087
|
+
const contentHtml = entry.message.content.map((msg) => {
|
|
5088
|
+
if (msg.type === "text") {
|
|
5089
|
+
return `<div class="markdown-content">${renderMarkdown(msg.text)}</div>`;
|
|
5090
|
+
}
|
|
5091
|
+
if (msg.type === "thinking") {
|
|
5092
|
+
const charCount = msg.thinking.length;
|
|
5093
|
+
return `
|
|
5094
|
+
<div class="thinking-block collapsible">
|
|
5095
|
+
<div class="thinking-header collapsible-trigger">
|
|
5096
|
+
<svg class="icon-lightbulb" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
5097
|
+
<path d="M12 2v1m0 18v1m9-10h1M2 12H1m17.66-7.66l.71.71M3.63 20.37l.71.71m0-14.14l-.71.71m17.02 12.73l-.71.71M12 7a5 5 0 0 1 5 5 5 5 0 0 1-1.47 3.53c-.6.6-.94 1.42-.94 2.27V18a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1v-.2c0-.85-.34-1.67-.94-2.27A5 5 0 0 1 7 12a5 5 0 0 1 5-5Z"/>
|
|
5098
|
+
</svg>
|
|
5099
|
+
<span class="thinking-title">Thinking</span>
|
|
5100
|
+
<span class="expand-hint">(${charCount} characters \xB7 click to collapse)</span>
|
|
5101
|
+
<svg class="icon-chevron" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
5102
|
+
<polyline points="6 9 12 15 18 9"></polyline>
|
|
5103
|
+
</svg>
|
|
5104
|
+
</div>
|
|
5105
|
+
<div class="thinking-content collapsible-content">
|
|
5106
|
+
<pre class="thinking-text">${escapeHtml(msg.thinking)}</pre>
|
|
5107
|
+
</div>
|
|
5108
|
+
</div>
|
|
5109
|
+
`;
|
|
5110
|
+
}
|
|
5111
|
+
if (msg.type === "tool_use") {
|
|
5112
|
+
const inputKeys = Object.keys(msg.input).length;
|
|
5113
|
+
return `
|
|
5114
|
+
<div class="tool-use-block collapsible">
|
|
5115
|
+
<div class="tool-use-header collapsible-trigger">
|
|
5116
|
+
<svg class="icon-wrench" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
5117
|
+
<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/>
|
|
5118
|
+
</svg>
|
|
5119
|
+
<span class="tool-name">${escapeHtml(msg.name)}</span>
|
|
5120
|
+
<span class="expand-hint">(${inputKeys} parameter${inputKeys !== 1 ? "s" : ""} \xB7 click to collapse)</span>
|
|
5121
|
+
<svg class="icon-chevron" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
5122
|
+
<polyline points="6 9 12 15 18 9"></polyline>
|
|
5123
|
+
</svg>
|
|
5124
|
+
</div>
|
|
5125
|
+
<div class="tool-use-content collapsible-content">
|
|
5126
|
+
<div class="tool-id"><strong>Tool ID:</strong> <code>${escapeHtml(msg.id)}</code></div>
|
|
5127
|
+
<div class="tool-input">
|
|
5128
|
+
<strong>Input Parameters:</strong>
|
|
5129
|
+
<pre class="json-input">${escapeHtml(formatJsonWithNewlines(msg.input))}</pre>
|
|
5130
|
+
</div>
|
|
5131
|
+
</div>
|
|
5132
|
+
</div>
|
|
5133
|
+
`;
|
|
5134
|
+
}
|
|
5135
|
+
return "";
|
|
5136
|
+
}).join("");
|
|
5137
|
+
return `
|
|
5138
|
+
<div class="conversation-entry assistant-entry">
|
|
5139
|
+
<div class="entry-header">
|
|
5140
|
+
<span class="entry-role">Assistant</span>
|
|
5141
|
+
<span class="entry-timestamp">${formatTimestamp(entry.timestamp)}</span>
|
|
5142
|
+
</div>
|
|
5143
|
+
<div class="entry-content">
|
|
5144
|
+
${contentHtml}
|
|
5145
|
+
</div>
|
|
5146
|
+
</div>
|
|
5147
|
+
`;
|
|
5148
|
+
};
|
|
5149
|
+
var renderSystemEntry = (entry) => {
|
|
5150
|
+
return `
|
|
5151
|
+
<div class="conversation-entry system-entry">
|
|
5152
|
+
<div class="entry-header">
|
|
5153
|
+
<span class="entry-role">System</span>
|
|
5154
|
+
<span class="entry-timestamp">${formatTimestamp(entry.timestamp)}</span>
|
|
5155
|
+
</div>
|
|
5156
|
+
<div class="entry-content">
|
|
5157
|
+
<div class="system-message">${escapeHtml(entry.content)}</div>
|
|
5158
|
+
</div>
|
|
5159
|
+
</div>
|
|
5160
|
+
`;
|
|
5161
|
+
};
|
|
5162
|
+
var groupConsecutiveAssistantMessages = (conversations) => {
|
|
5163
|
+
const grouped = [];
|
|
5164
|
+
let currentGroup = [];
|
|
5165
|
+
for (const conv of conversations) {
|
|
5166
|
+
if (conv.type === "assistant") {
|
|
5167
|
+
currentGroup.push(conv);
|
|
5168
|
+
} else if (conv.type === "user" || conv.type === "system") {
|
|
5169
|
+
if (currentGroup.length > 0) {
|
|
5170
|
+
grouped.push({
|
|
5171
|
+
type: currentGroup.length > 1 ? "grouped" : "single",
|
|
5172
|
+
entries: currentGroup
|
|
5173
|
+
});
|
|
5174
|
+
currentGroup = [];
|
|
5175
|
+
}
|
|
5176
|
+
grouped.push({ type: "single", entries: [conv] });
|
|
5177
|
+
}
|
|
5178
|
+
}
|
|
5179
|
+
if (currentGroup.length > 0) {
|
|
5180
|
+
grouped.push({
|
|
5181
|
+
type: currentGroup.length > 1 ? "grouped" : "single",
|
|
5182
|
+
entries: currentGroup
|
|
5183
|
+
});
|
|
5184
|
+
}
|
|
5185
|
+
return grouped;
|
|
5186
|
+
};
|
|
5187
|
+
var renderGroupedAssistantEntries = (entries) => {
|
|
5188
|
+
const allContent = entries.flatMap((entry) => entry.message.content);
|
|
5189
|
+
const firstEntry = entries[0];
|
|
5190
|
+
if (!firstEntry) {
|
|
5191
|
+
return "";
|
|
5192
|
+
}
|
|
5193
|
+
const contentHtml = allContent.map((msg) => {
|
|
5194
|
+
if (msg.type === "text") {
|
|
5195
|
+
return `<div class="markdown-content">${renderMarkdown(msg.text)}</div>`;
|
|
5196
|
+
}
|
|
5197
|
+
if (msg.type === "thinking") {
|
|
5198
|
+
const charCount = msg.thinking.length;
|
|
5199
|
+
return `
|
|
5200
|
+
<div class="thinking-block collapsible">
|
|
5201
|
+
<div class="thinking-header collapsible-trigger">
|
|
5202
|
+
<svg class="icon-lightbulb" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
5203
|
+
<path d="M12 2v1m0 18v1m9-10h1M2 12H1m17.66-7.66l.71.71M3.63 20.37l.71.71m0-14.14l-.71.71m17.02 12.73l-.71.71M12 7a5 5 0 0 1 5 5 5 5 0 0 1-1.47 3.53c-.6.6-.94 1.42-.94 2.27V18a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1v-.2c0-.85-.34-1.67-.94-2.27A5 5 0 0 1 7 12a5 5 0 0 1 5-5Z"/>
|
|
5204
|
+
</svg>
|
|
5205
|
+
<span class="thinking-title">Thinking</span>
|
|
5206
|
+
<span class="expand-hint">(${charCount} characters \xB7 click to collapse)</span>
|
|
5207
|
+
<svg class="icon-chevron" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
5208
|
+
<polyline points="6 9 12 15 18 9"></polyline>
|
|
5209
|
+
</svg>
|
|
5210
|
+
</div>
|
|
5211
|
+
<div class="thinking-content collapsible-content">
|
|
5212
|
+
<pre class="thinking-text">${escapeHtml(msg.thinking)}</pre>
|
|
5213
|
+
</div>
|
|
5214
|
+
</div>
|
|
5215
|
+
`;
|
|
5216
|
+
}
|
|
5217
|
+
if (msg.type === "tool_use") {
|
|
5218
|
+
const inputKeys = Object.keys(msg.input).length;
|
|
5219
|
+
return `
|
|
5220
|
+
<div class="tool-use-block collapsible">
|
|
5221
|
+
<div class="tool-use-header collapsible-trigger">
|
|
5222
|
+
<svg class="icon-wrench" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
5223
|
+
<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/>
|
|
5224
|
+
</svg>
|
|
5225
|
+
<span class="tool-name">${escapeHtml(msg.name)}</span>
|
|
5226
|
+
<span class="expand-hint">(${inputKeys} parameter${inputKeys !== 1 ? "s" : ""} \xB7 click to collapse)</span>
|
|
5227
|
+
<svg class="icon-chevron" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
5228
|
+
<polyline points="6 9 12 15 18 9"></polyline>
|
|
5229
|
+
</svg>
|
|
5230
|
+
</div>
|
|
5231
|
+
<div class="tool-use-content collapsible-content">
|
|
5232
|
+
<div class="tool-id"><strong>Tool ID:</strong> <code>${escapeHtml(msg.id)}</code></div>
|
|
5233
|
+
<div class="tool-input">
|
|
5234
|
+
<strong>Input Parameters:</strong>
|
|
5235
|
+
<pre class="json-input">${escapeHtml(formatJsonWithNewlines(msg.input))}</pre>
|
|
5236
|
+
</div>
|
|
5237
|
+
</div>
|
|
5238
|
+
</div>
|
|
5239
|
+
`;
|
|
5240
|
+
}
|
|
5241
|
+
return "";
|
|
5242
|
+
}).join("");
|
|
5243
|
+
return `
|
|
5244
|
+
<div class="conversation-entry assistant-entry">
|
|
5245
|
+
<div class="entry-header">
|
|
5246
|
+
<span class="entry-role">Assistant</span>
|
|
5247
|
+
<span class="entry-timestamp">${formatTimestamp(firstEntry.timestamp)}</span>
|
|
5248
|
+
</div>
|
|
5249
|
+
<div class="entry-content">
|
|
5250
|
+
${contentHtml}
|
|
5251
|
+
</div>
|
|
5252
|
+
</div>
|
|
5253
|
+
`;
|
|
5254
|
+
};
|
|
5255
|
+
var generateSessionHtml = (session, projectId) => Effect34.gen(function* () {
|
|
5256
|
+
const grouped = groupConsecutiveAssistantMessages(session.conversations);
|
|
5257
|
+
const conversationsHtml = grouped.map((group) => {
|
|
5258
|
+
if (group.type === "grouped") {
|
|
5259
|
+
return renderGroupedAssistantEntries(
|
|
5260
|
+
group.entries
|
|
5261
|
+
);
|
|
5262
|
+
}
|
|
5263
|
+
const conv = group.entries[0];
|
|
5264
|
+
if (!conv) {
|
|
5265
|
+
return "";
|
|
5266
|
+
}
|
|
5267
|
+
if (conv.type === "user") {
|
|
5268
|
+
return renderUserEntry(conv);
|
|
5269
|
+
}
|
|
5270
|
+
if (conv.type === "assistant") {
|
|
5271
|
+
return renderAssistantEntry(conv);
|
|
5272
|
+
}
|
|
5273
|
+
if (conv.type === "system") {
|
|
5274
|
+
return renderSystemEntry(conv);
|
|
5275
|
+
}
|
|
5276
|
+
return "";
|
|
5277
|
+
}).filter((html2) => html2 !== "").join("\n");
|
|
5278
|
+
const html = `<!DOCTYPE html>
|
|
5279
|
+
<html lang="en">
|
|
5280
|
+
<head>
|
|
5281
|
+
<meta charset="UTF-8">
|
|
5282
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
5283
|
+
<title>Claude Code Session - ${escapeHtml(session.id)}</title>
|
|
5284
|
+
<style>
|
|
5285
|
+
* {
|
|
5286
|
+
margin: 0;
|
|
5287
|
+
padding: 0;
|
|
5288
|
+
box-sizing: border-box;
|
|
5289
|
+
}
|
|
5290
|
+
|
|
5291
|
+
:root {
|
|
5292
|
+
--background: 0 0% 100%;
|
|
5293
|
+
--foreground: 0 0% 3.9%;
|
|
5294
|
+
--muted: 0 0% 96.1%;
|
|
5295
|
+
--muted-foreground: 0 0% 45.1%;
|
|
5296
|
+
--border: 0 0% 89.8%;
|
|
5297
|
+
--primary: 0 0% 9%;
|
|
5298
|
+
--blue-50: 214 100% 97%;
|
|
5299
|
+
--blue-200: 213 97% 87%;
|
|
5300
|
+
--blue-600: 217 91% 60%;
|
|
5301
|
+
--blue-800: 217 91% 35%;
|
|
5302
|
+
}
|
|
5303
|
+
|
|
5304
|
+
body {
|
|
5305
|
+
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
5306
|
+
line-height: 1.6;
|
|
5307
|
+
color: hsl(var(--foreground));
|
|
5308
|
+
background: hsl(var(--background));
|
|
5309
|
+
padding: 2rem;
|
|
5310
|
+
max-width: 1200px;
|
|
5311
|
+
margin: 0 auto;
|
|
5312
|
+
}
|
|
5313
|
+
|
|
5314
|
+
.header {
|
|
5315
|
+
border-bottom: 1px solid hsl(var(--border));
|
|
5316
|
+
padding-bottom: 2rem;
|
|
5317
|
+
margin-bottom: 2rem;
|
|
5318
|
+
}
|
|
5319
|
+
|
|
5320
|
+
.header h1 {
|
|
5321
|
+
font-size: 2rem;
|
|
5322
|
+
font-weight: 700;
|
|
5323
|
+
margin-bottom: 0.5rem;
|
|
5324
|
+
}
|
|
5325
|
+
|
|
5326
|
+
.header .metadata {
|
|
5327
|
+
color: hsl(var(--muted-foreground));
|
|
5328
|
+
font-size: 0.875rem;
|
|
5329
|
+
}
|
|
5330
|
+
|
|
5331
|
+
.conversation-list {
|
|
5332
|
+
display: flex;
|
|
5333
|
+
flex-direction: column;
|
|
5334
|
+
gap: 1.5rem;
|
|
5335
|
+
}
|
|
5336
|
+
|
|
5337
|
+
.conversation-entry {
|
|
5338
|
+
border-radius: 0.5rem;
|
|
5339
|
+
overflow: hidden;
|
|
5340
|
+
}
|
|
5341
|
+
|
|
5342
|
+
.entry-header {
|
|
5343
|
+
display: flex;
|
|
5344
|
+
justify-content: space-between;
|
|
5345
|
+
align-items: center;
|
|
5346
|
+
padding: 0.75rem 1rem;
|
|
5347
|
+
font-size: 0.875rem;
|
|
5348
|
+
font-weight: 500;
|
|
5349
|
+
border-bottom: 1px solid;
|
|
5350
|
+
}
|
|
5351
|
+
|
|
5352
|
+
.entry-timestamp {
|
|
5353
|
+
color: hsl(var(--muted-foreground));
|
|
5354
|
+
font-size: 0.75rem;
|
|
5355
|
+
}
|
|
5356
|
+
|
|
5357
|
+
.entry-content {
|
|
5358
|
+
padding: 1.5rem;
|
|
5359
|
+
}
|
|
5360
|
+
|
|
5361
|
+
/* User entry styles */
|
|
5362
|
+
.user-entry {
|
|
5363
|
+
background: hsl(var(--muted) / 0.3);
|
|
5364
|
+
border: 1px solid hsl(var(--border));
|
|
5365
|
+
}
|
|
5366
|
+
|
|
5367
|
+
.user-entry .entry-header {
|
|
5368
|
+
background: hsl(var(--muted) / 0.5);
|
|
5369
|
+
border-bottom-color: hsl(var(--border));
|
|
5370
|
+
}
|
|
5371
|
+
|
|
5372
|
+
/* Assistant entry styles */
|
|
5373
|
+
.assistant-entry {
|
|
5374
|
+
background: hsl(var(--background));
|
|
5375
|
+
border: 1px solid hsl(var(--border));
|
|
5376
|
+
}
|
|
5377
|
+
|
|
5378
|
+
.assistant-entry .entry-header {
|
|
5379
|
+
background: hsl(var(--muted) / 0.3);
|
|
5380
|
+
border-bottom-color: hsl(var(--border));
|
|
5381
|
+
}
|
|
5382
|
+
|
|
5383
|
+
/* System entry styles */
|
|
5384
|
+
.system-entry {
|
|
5385
|
+
background: hsl(var(--muted) / 0.2);
|
|
5386
|
+
border: 1px dashed hsl(var(--border));
|
|
5387
|
+
}
|
|
5388
|
+
|
|
5389
|
+
.system-entry .entry-header {
|
|
5390
|
+
background: hsl(var(--muted) / 0.4);
|
|
5391
|
+
border-bottom-color: hsl(var(--border));
|
|
5392
|
+
}
|
|
5393
|
+
|
|
5394
|
+
.system-message {
|
|
5395
|
+
font-family: monospace;
|
|
5396
|
+
font-size: 0.875rem;
|
|
5397
|
+
color: hsl(var(--muted-foreground));
|
|
5398
|
+
}
|
|
5399
|
+
|
|
5400
|
+
/* Markdown styles */
|
|
5401
|
+
.markdown-content {
|
|
5402
|
+
width: 100%;
|
|
5403
|
+
margin: 1rem 0.25rem;
|
|
5404
|
+
}
|
|
5405
|
+
|
|
5406
|
+
.markdown-h1 {
|
|
5407
|
+
font-size: 1.875rem;
|
|
5408
|
+
font-weight: 700;
|
|
5409
|
+
margin-bottom: 1.5rem;
|
|
5410
|
+
margin-top: 2rem;
|
|
5411
|
+
padding-bottom: 0.75rem;
|
|
5412
|
+
border-bottom: 1px solid hsl(var(--border));
|
|
5413
|
+
}
|
|
5414
|
+
|
|
5415
|
+
.markdown-h2 {
|
|
5416
|
+
font-size: 1.5rem;
|
|
5417
|
+
font-weight: 600;
|
|
5418
|
+
margin-bottom: 1rem;
|
|
5419
|
+
margin-top: 2rem;
|
|
5420
|
+
padding-bottom: 0.5rem;
|
|
5421
|
+
border-bottom: 1px solid hsl(var(--border) / 0.5);
|
|
5422
|
+
}
|
|
5423
|
+
|
|
5424
|
+
.markdown-h3 {
|
|
5425
|
+
font-size: 1.25rem;
|
|
5426
|
+
font-weight: 600;
|
|
5427
|
+
margin-bottom: 0.75rem;
|
|
5428
|
+
margin-top: 1.5rem;
|
|
5429
|
+
}
|
|
5430
|
+
|
|
5431
|
+
.markdown-p {
|
|
5432
|
+
margin-bottom: 1rem;
|
|
5433
|
+
line-height: 1.75;
|
|
5434
|
+
word-break: break-all;
|
|
5435
|
+
}
|
|
5436
|
+
|
|
5437
|
+
.inline-code {
|
|
5438
|
+
background: hsl(var(--muted) / 0.7);
|
|
5439
|
+
padding: 0.25rem 0.5rem;
|
|
5440
|
+
border-radius: 0.375rem;
|
|
5441
|
+
font-size: 0.875rem;
|
|
5442
|
+
font-family: monospace;
|
|
5443
|
+
border: 1px solid hsl(var(--border));
|
|
5444
|
+
}
|
|
5445
|
+
|
|
5446
|
+
.code-block {
|
|
5447
|
+
position: relative;
|
|
5448
|
+
margin: 1.5rem 0;
|
|
5449
|
+
}
|
|
5450
|
+
|
|
5451
|
+
.code-header {
|
|
5452
|
+
display: flex;
|
|
5453
|
+
align-items: center;
|
|
5454
|
+
justify-content: space-between;
|
|
5455
|
+
background: hsl(var(--muted) / 0.3);
|
|
5456
|
+
padding: 0.5rem 1rem;
|
|
5457
|
+
border-bottom: 1px solid hsl(var(--border));
|
|
5458
|
+
border-top-left-radius: 0.5rem;
|
|
5459
|
+
border-top-right-radius: 0.5rem;
|
|
5460
|
+
border: 1px solid hsl(var(--border));
|
|
5461
|
+
border-bottom: none;
|
|
5462
|
+
}
|
|
5463
|
+
|
|
5464
|
+
.code-lang {
|
|
5465
|
+
font-size: 0.75rem;
|
|
5466
|
+
font-weight: 500;
|
|
5467
|
+
color: hsl(var(--muted-foreground));
|
|
5468
|
+
text-transform: uppercase;
|
|
5469
|
+
letter-spacing: 0.05em;
|
|
5470
|
+
}
|
|
5471
|
+
|
|
5472
|
+
.code-block pre {
|
|
5473
|
+
margin: 0;
|
|
5474
|
+
padding: 1rem;
|
|
5475
|
+
background: hsl(var(--muted) / 0.2);
|
|
5476
|
+
border: 1px solid hsl(var(--border));
|
|
5477
|
+
border-top: none;
|
|
5478
|
+
border-bottom-left-radius: 0.5rem;
|
|
5479
|
+
border-bottom-right-radius: 0.5rem;
|
|
5480
|
+
overflow-x: auto;
|
|
5481
|
+
}
|
|
5482
|
+
|
|
5483
|
+
.code-block code {
|
|
5484
|
+
font-family: 'Monaco', 'Courier New', monospace;
|
|
5485
|
+
font-size: 0.875rem;
|
|
5486
|
+
line-height: 1.5;
|
|
5487
|
+
}
|
|
5488
|
+
|
|
5489
|
+
/* Thinking block styles */
|
|
5490
|
+
.thinking-block {
|
|
5491
|
+
background: hsl(var(--muted) / 0.5);
|
|
5492
|
+
border: 2px dashed hsl(var(--border));
|
|
5493
|
+
border-radius: 0.5rem;
|
|
5494
|
+
margin-bottom: 0.5rem;
|
|
5495
|
+
overflow: hidden;
|
|
5496
|
+
}
|
|
5497
|
+
|
|
5498
|
+
.thinking-header {
|
|
5499
|
+
display: flex;
|
|
5500
|
+
align-items: center;
|
|
5501
|
+
gap: 0.5rem;
|
|
5502
|
+
padding: 0.75rem 1rem;
|
|
5503
|
+
cursor: pointer;
|
|
5504
|
+
background: hsl(var(--muted) / 0.3);
|
|
5505
|
+
transition: background 0.2s;
|
|
5506
|
+
}
|
|
5507
|
+
|
|
5508
|
+
.thinking-header:hover {
|
|
5509
|
+
background: hsl(var(--muted) / 0.5);
|
|
5510
|
+
}
|
|
5511
|
+
|
|
5512
|
+
.icon-lightbulb {
|
|
5513
|
+
color: hsl(var(--muted-foreground));
|
|
5514
|
+
flex-shrink: 0;
|
|
5515
|
+
}
|
|
5516
|
+
|
|
5517
|
+
.thinking-title {
|
|
5518
|
+
font-size: 0.875rem;
|
|
5519
|
+
font-weight: 500;
|
|
5520
|
+
}
|
|
5521
|
+
|
|
5522
|
+
.expand-hint {
|
|
5523
|
+
font-size: 0.75rem;
|
|
5524
|
+
color: hsl(var(--muted-foreground));
|
|
5525
|
+
font-weight: normal;
|
|
5526
|
+
margin-left: 0.5rem;
|
|
5527
|
+
}
|
|
5528
|
+
|
|
5529
|
+
.collapsible:not(.collapsed) .expand-hint {
|
|
5530
|
+
display: none;
|
|
5531
|
+
}
|
|
5532
|
+
|
|
5533
|
+
.icon-chevron {
|
|
5534
|
+
margin-left: auto;
|
|
5535
|
+
color: hsl(var(--muted-foreground));
|
|
5536
|
+
transition: transform 0.2s;
|
|
5537
|
+
}
|
|
5538
|
+
|
|
5539
|
+
.collapsible.collapsed .icon-chevron {
|
|
5540
|
+
transform: rotate(-90deg);
|
|
5541
|
+
}
|
|
5542
|
+
|
|
5543
|
+
.thinking-content {
|
|
5544
|
+
padding: 0.5rem 1rem;
|
|
5545
|
+
}
|
|
5546
|
+
|
|
5547
|
+
.collapsible-content {
|
|
5548
|
+
max-height: 1000px;
|
|
5549
|
+
overflow: hidden;
|
|
5550
|
+
transition: max-height 0.3s ease-out, opacity 0.2s ease-out;
|
|
5551
|
+
}
|
|
5552
|
+
|
|
5553
|
+
.collapsible.collapsed .collapsible-content {
|
|
5554
|
+
max-height: 0;
|
|
5555
|
+
opacity: 0;
|
|
5556
|
+
}
|
|
5557
|
+
|
|
5558
|
+
.thinking-text {
|
|
5559
|
+
font-size: 0.875rem;
|
|
5560
|
+
color: hsl(var(--muted-foreground));
|
|
5561
|
+
font-family: monospace;
|
|
5562
|
+
white-space: pre-wrap;
|
|
5563
|
+
word-break: break-word;
|
|
5564
|
+
}
|
|
5565
|
+
|
|
5566
|
+
/* Tool use block styles */
|
|
5567
|
+
.tool-use-block {
|
|
5568
|
+
border: 1px solid hsl(var(--blue-200));
|
|
5569
|
+
background: hsl(var(--blue-50) / 0.5);
|
|
5570
|
+
border-radius: 0.5rem;
|
|
5571
|
+
margin-bottom: 0.5rem;
|
|
5572
|
+
overflow: hidden;
|
|
5573
|
+
}
|
|
5574
|
+
|
|
5575
|
+
.tool-use-header {
|
|
5576
|
+
display: flex;
|
|
5577
|
+
align-items: center;
|
|
5578
|
+
gap: 0.5rem;
|
|
5579
|
+
padding: 0.375rem 0.75rem;
|
|
5580
|
+
cursor: pointer;
|
|
5581
|
+
background: hsl(var(--blue-50) / 0.3);
|
|
5582
|
+
transition: background 0.2s;
|
|
5583
|
+
}
|
|
5584
|
+
|
|
5585
|
+
.tool-use-header:hover {
|
|
5586
|
+
background: hsl(var(--blue-50) / 0.6);
|
|
5587
|
+
}
|
|
5588
|
+
|
|
5589
|
+
.icon-wrench {
|
|
5590
|
+
color: hsl(var(--blue-600));
|
|
5591
|
+
flex-shrink: 0;
|
|
5592
|
+
}
|
|
5593
|
+
|
|
5594
|
+
.tool-name {
|
|
5595
|
+
font-size: 0.875rem;
|
|
5596
|
+
font-weight: 500;
|
|
5597
|
+
flex: 1;
|
|
5598
|
+
overflow: hidden;
|
|
5599
|
+
text-overflow: ellipsis;
|
|
5600
|
+
white-space: nowrap;
|
|
5601
|
+
}
|
|
5602
|
+
|
|
5603
|
+
.tool-use-content {
|
|
5604
|
+
padding: 0.75rem 1rem;
|
|
5605
|
+
border-top: 1px solid hsl(var(--blue-200));
|
|
5606
|
+
display: flex;
|
|
5607
|
+
flex-direction: column;
|
|
5608
|
+
gap: 0.75rem;
|
|
5609
|
+
}
|
|
5610
|
+
|
|
5611
|
+
.tool-id {
|
|
5612
|
+
font-size: 0.75rem;
|
|
5613
|
+
}
|
|
5614
|
+
|
|
5615
|
+
.tool-id code {
|
|
5616
|
+
background: hsl(var(--background) / 0.5);
|
|
5617
|
+
padding: 0.25rem 0.5rem;
|
|
5618
|
+
border-radius: 0.25rem;
|
|
5619
|
+
border: 1px solid hsl(var(--blue-200));
|
|
5620
|
+
font-family: monospace;
|
|
5621
|
+
font-size: 0.75rem;
|
|
5622
|
+
}
|
|
5623
|
+
|
|
5624
|
+
.tool-input {
|
|
5625
|
+
font-size: 0.75rem;
|
|
5626
|
+
}
|
|
5627
|
+
|
|
5628
|
+
.json-input {
|
|
5629
|
+
background: hsl(var(--background));
|
|
5630
|
+
border: 1px solid hsl(var(--border));
|
|
5631
|
+
border-radius: 0.375rem;
|
|
5632
|
+
padding: 0.75rem;
|
|
5633
|
+
margin-top: 0.5rem;
|
|
5634
|
+
overflow-x: auto;
|
|
5635
|
+
font-family: monospace;
|
|
5636
|
+
font-size: 0.75rem;
|
|
5637
|
+
white-space: pre-wrap;
|
|
5638
|
+
word-break: break-all;
|
|
5639
|
+
overflow-wrap: break-word;
|
|
5640
|
+
}
|
|
5641
|
+
|
|
5642
|
+
.message-image {
|
|
5643
|
+
max-width: 100%;
|
|
5644
|
+
height: auto;
|
|
5645
|
+
border-radius: 0.5rem;
|
|
5646
|
+
margin: 1rem 0;
|
|
5647
|
+
}
|
|
5648
|
+
|
|
5649
|
+
strong {
|
|
5650
|
+
font-weight: 600;
|
|
5651
|
+
}
|
|
5652
|
+
|
|
5653
|
+
em {
|
|
5654
|
+
font-style: italic;
|
|
5655
|
+
}
|
|
5656
|
+
|
|
5657
|
+
a {
|
|
5658
|
+
color: hsl(var(--primary));
|
|
5659
|
+
text-decoration: underline;
|
|
5660
|
+
text-decoration-color: hsl(var(--primary) / 0.3);
|
|
5661
|
+
text-underline-offset: 4px;
|
|
5662
|
+
transition: text-decoration-color 0.2s;
|
|
5663
|
+
}
|
|
5664
|
+
|
|
5665
|
+
a:hover {
|
|
5666
|
+
text-decoration-color: hsl(var(--primary) / 0.6);
|
|
5667
|
+
}
|
|
5668
|
+
|
|
5669
|
+
.header-top {
|
|
5670
|
+
display: flex;
|
|
5671
|
+
justify-content: space-between;
|
|
5672
|
+
align-items: center;
|
|
5673
|
+
margin-bottom: 1rem;
|
|
5674
|
+
}
|
|
5675
|
+
|
|
5676
|
+
.toggle-all-button {
|
|
5677
|
+
padding: 0.5rem 1rem;
|
|
5678
|
+
background: hsl(var(--primary));
|
|
5679
|
+
color: white;
|
|
5680
|
+
border: none;
|
|
5681
|
+
border-radius: 0.375rem;
|
|
5682
|
+
font-size: 0.875rem;
|
|
5683
|
+
font-weight: 500;
|
|
5684
|
+
cursor: pointer;
|
|
5685
|
+
transition: opacity 0.2s;
|
|
5686
|
+
}
|
|
5687
|
+
|
|
5688
|
+
.toggle-all-button:hover {
|
|
5689
|
+
opacity: 0.9;
|
|
5690
|
+
}
|
|
5691
|
+
|
|
5692
|
+
.toggle-all-button:active {
|
|
5693
|
+
opacity: 0.8;
|
|
5694
|
+
}
|
|
5695
|
+
|
|
5696
|
+
.footer {
|
|
5697
|
+
margin-top: 4rem;
|
|
5698
|
+
padding-top: 2rem;
|
|
5699
|
+
border-top: 1px solid hsl(var(--border));
|
|
5700
|
+
text-align: center;
|
|
5701
|
+
color: hsl(var(--muted-foreground));
|
|
5702
|
+
font-size: 0.875rem;
|
|
5703
|
+
}
|
|
5704
|
+
</style>
|
|
5705
|
+
</head>
|
|
5706
|
+
<body>
|
|
5707
|
+
<div class="header">
|
|
5708
|
+
<div class="header-top">
|
|
5709
|
+
<h1>Claude Code Session Export</h1>
|
|
5710
|
+
<button id="toggle-all-btn" class="toggle-all-button">Collapse All</button>
|
|
5711
|
+
</div>
|
|
5712
|
+
<div class="metadata">
|
|
5713
|
+
<div><strong>Session ID:</strong> ${escapeHtml(session.id)}</div>
|
|
5714
|
+
<div><strong>Project ID:</strong> ${escapeHtml(projectId)}</div>
|
|
5715
|
+
<div><strong>Exported:</strong> ${formatTimestamp(Date.now())}</div>
|
|
5716
|
+
<div><strong>Total Conversations:</strong> ${session.conversations.length}</div>
|
|
5717
|
+
</div>
|
|
5718
|
+
</div>
|
|
5719
|
+
|
|
5720
|
+
<div class="conversation-list">
|
|
5721
|
+
${conversationsHtml}
|
|
5722
|
+
</div>
|
|
5723
|
+
|
|
5724
|
+
<div class="footer">
|
|
5725
|
+
<p>Exported from Claude Code Viewer</p>
|
|
5726
|
+
</div>
|
|
5727
|
+
|
|
5728
|
+
<script>
|
|
5729
|
+
// Add click handlers for collapsible blocks
|
|
5730
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
5731
|
+
const triggers = document.querySelectorAll('.collapsible-trigger');
|
|
5732
|
+
const toggleAllBtn = document.getElementById('toggle-all-btn');
|
|
5733
|
+
let allExpanded = true; // Start as expanded since blocks are expanded by default
|
|
5734
|
+
|
|
5735
|
+
// Individual collapsible click handlers
|
|
5736
|
+
triggers.forEach(function(trigger) {
|
|
5737
|
+
trigger.addEventListener('click', function() {
|
|
5738
|
+
const collapsible = this.closest('.collapsible');
|
|
5739
|
+
if (collapsible) {
|
|
5740
|
+
collapsible.classList.toggle('collapsed');
|
|
5741
|
+
}
|
|
5742
|
+
});
|
|
5743
|
+
});
|
|
5744
|
+
|
|
5745
|
+
// Toggle all button
|
|
5746
|
+
if (toggleAllBtn) {
|
|
5747
|
+
toggleAllBtn.addEventListener('click', function() {
|
|
5748
|
+
const collapsibles = document.querySelectorAll('.collapsible');
|
|
5749
|
+
|
|
5750
|
+
if (allExpanded) {
|
|
5751
|
+
// Collapse all
|
|
5752
|
+
collapsibles.forEach(function(collapsible) {
|
|
5753
|
+
collapsible.classList.add('collapsed');
|
|
5754
|
+
});
|
|
5755
|
+
toggleAllBtn.textContent = 'Expand All';
|
|
5756
|
+
allExpanded = false;
|
|
5757
|
+
} else {
|
|
5758
|
+
// Expand all
|
|
5759
|
+
collapsibles.forEach(function(collapsible) {
|
|
5760
|
+
collapsible.classList.remove('collapsed');
|
|
5761
|
+
});
|
|
5762
|
+
toggleAllBtn.textContent = 'Collapse All';
|
|
5763
|
+
allExpanded = true;
|
|
5764
|
+
}
|
|
5765
|
+
});
|
|
5766
|
+
}
|
|
5767
|
+
});
|
|
5768
|
+
</script>
|
|
5769
|
+
</body>
|
|
5770
|
+
</html>`;
|
|
5771
|
+
return html;
|
|
5772
|
+
});
|
|
5773
|
+
|
|
5774
|
+
// src/server/core/session/presentation/SessionController.ts
|
|
5775
|
+
var LayerImpl23 = Effect35.gen(function* () {
|
|
4745
5776
|
const sessionRepository = yield* SessionRepository;
|
|
4746
|
-
const getSession = (options) =>
|
|
5777
|
+
const getSession = (options) => Effect35.gen(function* () {
|
|
4747
5778
|
const { projectId, sessionId } = options;
|
|
4748
5779
|
const { session } = yield* sessionRepository.getSession(
|
|
4749
5780
|
projectId,
|
|
@@ -4754,8 +5785,27 @@ var LayerImpl23 = Effect34.gen(function* () {
|
|
|
4754
5785
|
response: { session }
|
|
4755
5786
|
};
|
|
4756
5787
|
});
|
|
5788
|
+
const exportSessionHtml = (options) => Effect35.gen(function* () {
|
|
5789
|
+
const { projectId, sessionId } = options;
|
|
5790
|
+
const { session } = yield* sessionRepository.getSession(
|
|
5791
|
+
projectId,
|
|
5792
|
+
sessionId
|
|
5793
|
+
);
|
|
5794
|
+
if (session === null) {
|
|
5795
|
+
return {
|
|
5796
|
+
status: 404,
|
|
5797
|
+
response: { error: "Session not found" }
|
|
5798
|
+
};
|
|
5799
|
+
}
|
|
5800
|
+
const html = yield* generateSessionHtml(session, projectId);
|
|
5801
|
+
return {
|
|
5802
|
+
status: 200,
|
|
5803
|
+
response: { html }
|
|
5804
|
+
};
|
|
5805
|
+
});
|
|
4757
5806
|
return {
|
|
4758
|
-
getSession
|
|
5807
|
+
getSession,
|
|
5808
|
+
exportSessionHtml
|
|
4759
5809
|
};
|
|
4760
5810
|
});
|
|
4761
5811
|
var SessionController = class extends Context29.Tag("SessionController")() {
|
|
@@ -4769,12 +5819,12 @@ import { Hono } from "hono";
|
|
|
4769
5819
|
var honoApp = new Hono();
|
|
4770
5820
|
|
|
4771
5821
|
// src/server/hono/initialize.ts
|
|
4772
|
-
import { Context as Context30, Effect as
|
|
5822
|
+
import { Context as Context30, Effect as Effect36, Layer as Layer31, Ref as Ref11, Schedule as Schedule2 } from "effect";
|
|
4773
5823
|
var InitializeService = class extends Context30.Tag("InitializeService")() {
|
|
4774
5824
|
static {
|
|
4775
5825
|
this.Live = Layer31.effect(
|
|
4776
5826
|
this,
|
|
4777
|
-
|
|
5827
|
+
Effect36.gen(function* () {
|
|
4778
5828
|
const eventBus = yield* EventBus;
|
|
4779
5829
|
const fileWatcher = yield* FileWatcherService;
|
|
4780
5830
|
const projectRepository = yield* ProjectRepository;
|
|
@@ -4784,20 +5834,20 @@ var InitializeService = class extends Context30.Tag("InitializeService")() {
|
|
|
4784
5834
|
const virtualConversationDatabase = yield* VirtualConversationDatabase;
|
|
4785
5835
|
const listenersRef = yield* Ref11.make({});
|
|
4786
5836
|
const startInitialization = () => {
|
|
4787
|
-
return
|
|
5837
|
+
return Effect36.gen(function* () {
|
|
4788
5838
|
yield* fileWatcher.startWatching();
|
|
4789
|
-
const daemon =
|
|
5839
|
+
const daemon = Effect36.repeat(
|
|
4790
5840
|
eventBus.emit("heartbeat", {}),
|
|
4791
5841
|
Schedule2.fixed("10 seconds")
|
|
4792
5842
|
);
|
|
4793
5843
|
console.log("start heartbeat");
|
|
4794
|
-
yield*
|
|
5844
|
+
yield* Effect36.forkDaemon(daemon);
|
|
4795
5845
|
console.log("after starting heartbeat fork");
|
|
4796
5846
|
const onSessionChanged = (event) => {
|
|
4797
|
-
|
|
5847
|
+
Effect36.runFork(
|
|
4798
5848
|
projectMetaService.invalidateProject(event.projectId)
|
|
4799
5849
|
);
|
|
4800
|
-
|
|
5850
|
+
Effect36.runFork(
|
|
4801
5851
|
sessionMetaService.invalidateSession(
|
|
4802
5852
|
event.projectId,
|
|
4803
5853
|
event.sessionId
|
|
@@ -4806,7 +5856,7 @@ var InitializeService = class extends Context30.Tag("InitializeService")() {
|
|
|
4806
5856
|
};
|
|
4807
5857
|
const onSessionProcessChanged = (event) => {
|
|
4808
5858
|
if ((event.changed.type === "completed" || event.changed.type === "paused") && event.changed.sessionId !== void 0) {
|
|
4809
|
-
|
|
5859
|
+
Effect36.runFork(
|
|
4810
5860
|
virtualConversationDatabase.deleteVirtualConversations(
|
|
4811
5861
|
event.changed.sessionId
|
|
4812
5862
|
)
|
|
@@ -4820,12 +5870,12 @@ var InitializeService = class extends Context30.Tag("InitializeService")() {
|
|
|
4820
5870
|
});
|
|
4821
5871
|
yield* eventBus.on("sessionChanged", onSessionChanged);
|
|
4822
5872
|
yield* eventBus.on("sessionProcessChanged", onSessionProcessChanged);
|
|
4823
|
-
yield*
|
|
5873
|
+
yield* Effect36.gen(function* () {
|
|
4824
5874
|
console.log("Initializing projects cache");
|
|
4825
5875
|
const { projects } = yield* projectRepository.getProjects();
|
|
4826
5876
|
console.log(`${projects.length} projects cache initialized`);
|
|
4827
5877
|
console.log("Initializing sessions cache");
|
|
4828
|
-
const results = yield*
|
|
5878
|
+
const results = yield* Effect36.all(
|
|
4829
5879
|
projects.map(
|
|
4830
5880
|
(project) => sessionRepository.getSessions(project.id)
|
|
4831
5881
|
),
|
|
@@ -4837,12 +5887,12 @@ var InitializeService = class extends Context30.Tag("InitializeService")() {
|
|
|
4837
5887
|
);
|
|
4838
5888
|
console.log(`${totalSessions} sessions cache initialized`);
|
|
4839
5889
|
}).pipe(
|
|
4840
|
-
|
|
4841
|
-
|
|
5890
|
+
Effect36.catchAll(() => Effect36.void),
|
|
5891
|
+
Effect36.withSpan("initialize-cache")
|
|
4842
5892
|
);
|
|
4843
|
-
}).pipe(
|
|
5893
|
+
}).pipe(Effect36.withSpan("start-initialization"));
|
|
4844
5894
|
};
|
|
4845
|
-
const stopCleanup = () =>
|
|
5895
|
+
const stopCleanup = () => Effect36.gen(function* () {
|
|
4846
5896
|
const listeners = yield* Ref11.get(listenersRef);
|
|
4847
5897
|
if (listeners.sessionChanged) {
|
|
4848
5898
|
yield* eventBus.off("sessionChanged", listeners.sessionChanged);
|
|
@@ -4867,7 +5917,7 @@ var InitializeService = class extends Context30.Tag("InitializeService")() {
|
|
|
4867
5917
|
|
|
4868
5918
|
// src/server/hono/route.ts
|
|
4869
5919
|
import { zValidator } from "@hono/zod-validator";
|
|
4870
|
-
import { Effect as
|
|
5920
|
+
import { Effect as Effect38, Runtime as Runtime3 } from "effect";
|
|
4871
5921
|
import { setCookie as setCookie2 } from "hono/cookie";
|
|
4872
5922
|
import { streamSSE } from "hono/streaming";
|
|
4873
5923
|
import prexit from "prexit";
|
|
@@ -4876,7 +5926,7 @@ import { z as z28 } from "zod";
|
|
|
4876
5926
|
// package.json
|
|
4877
5927
|
var package_default = {
|
|
4878
5928
|
name: "@kimuson/claude-code-viewer",
|
|
4879
|
-
version: "0.4.
|
|
5929
|
+
version: "0.4.5",
|
|
4880
5930
|
type: "module",
|
|
4881
5931
|
license: "MIT",
|
|
4882
5932
|
repository: {
|
|
@@ -4920,8 +5970,8 @@ var package_default = {
|
|
|
4920
5970
|
"@anthropic-ai/claude-agent-sdk": "0.1.30",
|
|
4921
5971
|
"@anthropic-ai/claude-code": "2.0.24",
|
|
4922
5972
|
"@anthropic-ai/sdk": "0.67.0",
|
|
4923
|
-
"@effect/platform": "0.
|
|
4924
|
-
"@effect/platform-node": "0.
|
|
5973
|
+
"@effect/platform": "0.93.2",
|
|
5974
|
+
"@effect/platform-node": "0.100.0",
|
|
4925
5975
|
"@hono/node-server": "1.19.5",
|
|
4926
5976
|
"@hono/zod-validator": "0.7.4",
|
|
4927
5977
|
"@lingui/core": "5.5.1",
|
|
@@ -4944,7 +5994,7 @@ var package_default = {
|
|
|
4944
5994
|
"class-variance-authority": "0.7.1",
|
|
4945
5995
|
clsx: "2.1.1",
|
|
4946
5996
|
"date-fns": "4.1.0",
|
|
4947
|
-
effect: "3.
|
|
5997
|
+
effect: "3.19.3",
|
|
4948
5998
|
"es-toolkit": "1.41.0",
|
|
4949
5999
|
hono: "4.10.3",
|
|
4950
6000
|
jotai: "2.15.0",
|
|
@@ -5138,11 +6188,12 @@ var userConfigSchema = z27.object({
|
|
|
5138
6188
|
locale: localeSchema.optional().default("en"),
|
|
5139
6189
|
theme: z27.enum(["light", "dark", "system"]).optional().default("system")
|
|
5140
6190
|
});
|
|
6191
|
+
var defaultUserConfig = userConfigSchema.parse({});
|
|
5141
6192
|
|
|
5142
6193
|
// src/server/lib/effect/toEffectResponse.ts
|
|
5143
|
-
import { Effect as
|
|
6194
|
+
import { Effect as Effect37 } from "effect";
|
|
5144
6195
|
var effectToResponse = async (ctx, effect) => {
|
|
5145
|
-
const result = await
|
|
6196
|
+
const result = await Effect37.runPromise(effect);
|
|
5146
6197
|
const result2 = ctx.json(result.response, result.status);
|
|
5147
6198
|
return result2;
|
|
5148
6199
|
};
|
|
@@ -5169,16 +6220,13 @@ var configMiddleware = createMiddleware(
|
|
|
5169
6220
|
const cookie = getCookie(c, "ccv-config");
|
|
5170
6221
|
const parsed = parseUserConfig(cookie);
|
|
5171
6222
|
if (cookie === void 0) {
|
|
6223
|
+
const preferredLocale = detectLocaleFromAcceptLanguage(c.req.header("accept-language")) ?? DEFAULT_LOCALE;
|
|
5172
6224
|
setCookie(
|
|
5173
6225
|
c,
|
|
5174
6226
|
"ccv-config",
|
|
5175
6227
|
JSON.stringify({
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
enterKeyBehavior: "shift-enter-send",
|
|
5179
|
-
permissionMode: "default",
|
|
5180
|
-
locale: "ja",
|
|
5181
|
-
theme: "system"
|
|
6228
|
+
...defaultUserConfig,
|
|
6229
|
+
locale: preferredLocale
|
|
5182
6230
|
})
|
|
5183
6231
|
);
|
|
5184
6232
|
}
|
|
@@ -5188,7 +6236,7 @@ var configMiddleware = createMiddleware(
|
|
|
5188
6236
|
);
|
|
5189
6237
|
|
|
5190
6238
|
// src/server/hono/route.ts
|
|
5191
|
-
var routes = (app) =>
|
|
6239
|
+
var routes = (app) => Effect38.gen(function* () {
|
|
5192
6240
|
const projectController = yield* ProjectController;
|
|
5193
6241
|
const sessionController = yield* SessionController;
|
|
5194
6242
|
const gitController = yield* GitController;
|
|
@@ -5203,7 +6251,7 @@ var routes = (app) => Effect37.gen(function* () {
|
|
|
5203
6251
|
const userConfigService = yield* UserConfigService;
|
|
5204
6252
|
const claudeCodeLifeCycleService = yield* ClaudeCodeLifeCycleService;
|
|
5205
6253
|
const initializeService = yield* InitializeService;
|
|
5206
|
-
const runtime = yield*
|
|
6254
|
+
const runtime = yield* Effect38.runtime();
|
|
5207
6255
|
if ((yield* envService.getEnv("NEXT_PHASE")) !== "phase-production-build") {
|
|
5208
6256
|
yield* initializeService.startInitialization();
|
|
5209
6257
|
prexit(async () => {
|
|
@@ -5211,7 +6259,7 @@ var routes = (app) => Effect37.gen(function* () {
|
|
|
5211
6259
|
});
|
|
5212
6260
|
}
|
|
5213
6261
|
return app.use(configMiddleware).use(async (c, next) => {
|
|
5214
|
-
await
|
|
6262
|
+
await Effect38.runPromise(
|
|
5215
6263
|
userConfigService.setUserConfig({
|
|
5216
6264
|
...c.get("userConfig")
|
|
5217
6265
|
})
|
|
@@ -5246,7 +6294,7 @@ var routes = (app) => Effect37.gen(function* () {
|
|
|
5246
6294
|
projectController.getProject({
|
|
5247
6295
|
...c.req.param(),
|
|
5248
6296
|
...c.req.valid("query")
|
|
5249
|
-
}).pipe(
|
|
6297
|
+
}).pipe(Effect38.provide(runtime))
|
|
5250
6298
|
);
|
|
5251
6299
|
return response;
|
|
5252
6300
|
}
|
|
@@ -5263,7 +6311,7 @@ var routes = (app) => Effect37.gen(function* () {
|
|
|
5263
6311
|
c,
|
|
5264
6312
|
projectController.createProject({
|
|
5265
6313
|
...c.req.valid("json")
|
|
5266
|
-
}).pipe(
|
|
6314
|
+
}).pipe(Effect38.provide(runtime))
|
|
5267
6315
|
);
|
|
5268
6316
|
return response;
|
|
5269
6317
|
}
|
|
@@ -5272,21 +6320,30 @@ var routes = (app) => Effect37.gen(function* () {
|
|
|
5272
6320
|
c,
|
|
5273
6321
|
projectController.getProjectLatestSession({
|
|
5274
6322
|
...c.req.param()
|
|
5275
|
-
}).pipe(
|
|
6323
|
+
}).pipe(Effect38.provide(runtime))
|
|
5276
6324
|
);
|
|
5277
6325
|
return response;
|
|
5278
6326
|
}).get("/api/projects/:projectId/sessions/:sessionId", async (c) => {
|
|
5279
6327
|
const response = await effectToResponse(
|
|
5280
6328
|
c,
|
|
5281
|
-
sessionController.getSession({ ...c.req.param() }).pipe(
|
|
6329
|
+
sessionController.getSession({ ...c.req.param() }).pipe(Effect38.provide(runtime))
|
|
5282
6330
|
);
|
|
5283
6331
|
return response;
|
|
5284
|
-
}).get(
|
|
6332
|
+
}).get(
|
|
6333
|
+
"/api/projects/:projectId/sessions/:sessionId/export",
|
|
6334
|
+
async (c) => {
|
|
6335
|
+
const response = await effectToResponse(
|
|
6336
|
+
c,
|
|
6337
|
+
sessionController.exportSessionHtml({ ...c.req.param() }).pipe(Effect38.provide(runtime))
|
|
6338
|
+
);
|
|
6339
|
+
return response;
|
|
6340
|
+
}
|
|
6341
|
+
).get("/api/projects/:projectId/git/current-revisions", async (c) => {
|
|
5285
6342
|
const response = await effectToResponse(
|
|
5286
6343
|
c,
|
|
5287
6344
|
gitController.getCurrentRevisions({
|
|
5288
6345
|
...c.req.param()
|
|
5289
|
-
}).pipe(
|
|
6346
|
+
}).pipe(Effect38.provide(runtime))
|
|
5290
6347
|
);
|
|
5291
6348
|
return response;
|
|
5292
6349
|
}).post(
|
|
@@ -5304,7 +6361,7 @@ var routes = (app) => Effect37.gen(function* () {
|
|
|
5304
6361
|
gitController.getGitDiff({
|
|
5305
6362
|
...c.req.param(),
|
|
5306
6363
|
...c.req.valid("json")
|
|
5307
|
-
}).pipe(
|
|
6364
|
+
}).pipe(Effect38.provide(runtime))
|
|
5308
6365
|
);
|
|
5309
6366
|
return response;
|
|
5310
6367
|
}
|
|
@@ -5317,7 +6374,7 @@ var routes = (app) => Effect37.gen(function* () {
|
|
|
5317
6374
|
gitController.commitFiles({
|
|
5318
6375
|
...c.req.param(),
|
|
5319
6376
|
...c.req.valid("json")
|
|
5320
|
-
}).pipe(
|
|
6377
|
+
}).pipe(Effect38.provide(runtime))
|
|
5321
6378
|
);
|
|
5322
6379
|
return response;
|
|
5323
6380
|
}
|
|
@@ -5330,7 +6387,7 @@ var routes = (app) => Effect37.gen(function* () {
|
|
|
5330
6387
|
gitController.pushCommits({
|
|
5331
6388
|
...c.req.param(),
|
|
5332
6389
|
...c.req.valid("json")
|
|
5333
|
-
}).pipe(
|
|
6390
|
+
}).pipe(Effect38.provide(runtime))
|
|
5334
6391
|
);
|
|
5335
6392
|
return response;
|
|
5336
6393
|
}
|
|
@@ -5343,7 +6400,7 @@ var routes = (app) => Effect37.gen(function* () {
|
|
|
5343
6400
|
gitController.commitAndPush({
|
|
5344
6401
|
...c.req.param(),
|
|
5345
6402
|
...c.req.valid("json")
|
|
5346
|
-
}).pipe(
|
|
6403
|
+
}).pipe(Effect38.provide(runtime))
|
|
5347
6404
|
);
|
|
5348
6405
|
return response;
|
|
5349
6406
|
}
|
|
@@ -5360,19 +6417,19 @@ var routes = (app) => Effect37.gen(function* () {
|
|
|
5360
6417
|
c,
|
|
5361
6418
|
claudeCodeController.getMcpListRoute({
|
|
5362
6419
|
...c.req.param()
|
|
5363
|
-
}).pipe(
|
|
6420
|
+
}).pipe(Effect38.provide(runtime))
|
|
5364
6421
|
);
|
|
5365
6422
|
return response;
|
|
5366
6423
|
}).get("/api/cc/meta", async (c) => {
|
|
5367
6424
|
const response = await effectToResponse(
|
|
5368
6425
|
c,
|
|
5369
|
-
claudeCodeController.getClaudeCodeMeta().pipe(
|
|
6426
|
+
claudeCodeController.getClaudeCodeMeta().pipe(Effect38.provide(runtime))
|
|
5370
6427
|
);
|
|
5371
6428
|
return response;
|
|
5372
6429
|
}).get("/api/cc/features", async (c) => {
|
|
5373
6430
|
const response = await effectToResponse(
|
|
5374
6431
|
c,
|
|
5375
|
-
claudeCodeController.getAvailableFeatures().pipe(
|
|
6432
|
+
claudeCodeController.getAvailableFeatures().pipe(Effect38.provide(runtime))
|
|
5376
6433
|
);
|
|
5377
6434
|
return response;
|
|
5378
6435
|
}).get("/api/cc/session-processes", async (c) => {
|
|
@@ -5416,7 +6473,7 @@ var routes = (app) => Effect37.gen(function* () {
|
|
|
5416
6473
|
claudeCodeSessionProcessController.continueSessionProcess({
|
|
5417
6474
|
...c.req.param(),
|
|
5418
6475
|
...c.req.valid("json")
|
|
5419
|
-
}).pipe(
|
|
6476
|
+
}).pipe(Effect38.provide(runtime))
|
|
5420
6477
|
);
|
|
5421
6478
|
return response;
|
|
5422
6479
|
}
|
|
@@ -5425,7 +6482,7 @@ var routes = (app) => Effect37.gen(function* () {
|
|
|
5425
6482
|
zValidator("json", z28.object({ projectId: z28.string() })),
|
|
5426
6483
|
async (c) => {
|
|
5427
6484
|
const { sessionProcessId } = c.req.param();
|
|
5428
|
-
void
|
|
6485
|
+
void Effect38.runFork(
|
|
5429
6486
|
claudeCodeLifeCycleService.abortTask(sessionProcessId)
|
|
5430
6487
|
);
|
|
5431
6488
|
return c.json({ message: "Task aborted" });
|
|
@@ -5453,7 +6510,7 @@ var routes = (app) => Effect37.gen(function* () {
|
|
|
5453
6510
|
c,
|
|
5454
6511
|
async (rawStream) => {
|
|
5455
6512
|
await Runtime3.runPromise(runtime)(
|
|
5456
|
-
sseController.handleSSE(rawStream).pipe(
|
|
6513
|
+
sseController.handleSSE(rawStream).pipe(Effect38.provide(TypeSafeSSE.make(rawStream)))
|
|
5457
6514
|
);
|
|
5458
6515
|
},
|
|
5459
6516
|
async (err) => {
|
|
@@ -5463,7 +6520,7 @@ var routes = (app) => Effect37.gen(function* () {
|
|
|
5463
6520
|
}).get("/api/scheduler/jobs", async (c) => {
|
|
5464
6521
|
const response = await effectToResponse(
|
|
5465
6522
|
c,
|
|
5466
|
-
schedulerController.getJobs().pipe(
|
|
6523
|
+
schedulerController.getJobs().pipe(Effect38.provide(runtime))
|
|
5467
6524
|
);
|
|
5468
6525
|
return response;
|
|
5469
6526
|
}).post(
|
|
@@ -5474,7 +6531,7 @@ var routes = (app) => Effect37.gen(function* () {
|
|
|
5474
6531
|
c,
|
|
5475
6532
|
schedulerController.addJob({
|
|
5476
6533
|
job: c.req.valid("json")
|
|
5477
|
-
}).pipe(
|
|
6534
|
+
}).pipe(Effect38.provide(runtime))
|
|
5478
6535
|
);
|
|
5479
6536
|
return response;
|
|
5480
6537
|
}
|
|
@@ -5487,7 +6544,7 @@ var routes = (app) => Effect37.gen(function* () {
|
|
|
5487
6544
|
schedulerController.updateJob({
|
|
5488
6545
|
id: c.req.param("id"),
|
|
5489
6546
|
job: c.req.valid("json")
|
|
5490
|
-
}).pipe(
|
|
6547
|
+
}).pipe(Effect38.provide(runtime))
|
|
5491
6548
|
);
|
|
5492
6549
|
return response;
|
|
5493
6550
|
}
|
|
@@ -5496,7 +6553,7 @@ var routes = (app) => Effect37.gen(function* () {
|
|
|
5496
6553
|
c,
|
|
5497
6554
|
schedulerController.deleteJob({
|
|
5498
6555
|
id: c.req.param("id")
|
|
5499
|
-
}).pipe(
|
|
6556
|
+
}).pipe(Effect38.provide(runtime))
|
|
5500
6557
|
);
|
|
5501
6558
|
return response;
|
|
5502
6559
|
}).get(
|
|
@@ -5538,7 +6595,7 @@ var routes = (app) => Effect37.gen(function* () {
|
|
|
5538
6595
|
).get("/api/flags", async (c) => {
|
|
5539
6596
|
const response = await effectToResponse(
|
|
5540
6597
|
c,
|
|
5541
|
-
featureFlagController.getFlags().pipe(
|
|
6598
|
+
featureFlagController.getFlags().pipe(Effect38.provide(runtime))
|
|
5542
6599
|
);
|
|
5543
6600
|
return response;
|
|
5544
6601
|
});
|
|
@@ -5575,42 +6632,42 @@ if (!isDevelopment) {
|
|
|
5575
6632
|
}
|
|
5576
6633
|
var program = routes(honoApp).pipe(
|
|
5577
6634
|
/** Presentation */
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
6635
|
+
Effect39.provide(ProjectController.Live),
|
|
6636
|
+
Effect39.provide(SessionController.Live),
|
|
6637
|
+
Effect39.provide(GitController.Live),
|
|
6638
|
+
Effect39.provide(ClaudeCodeController.Live),
|
|
6639
|
+
Effect39.provide(ClaudeCodeSessionProcessController.Live),
|
|
6640
|
+
Effect39.provide(ClaudeCodePermissionController.Live),
|
|
6641
|
+
Effect39.provide(FileSystemController.Live),
|
|
6642
|
+
Effect39.provide(SSEController.Live),
|
|
6643
|
+
Effect39.provide(SchedulerController.Live),
|
|
6644
|
+
Effect39.provide(FeatureFlagController.Live)
|
|
5588
6645
|
).pipe(
|
|
5589
6646
|
/** Application */
|
|
5590
|
-
|
|
5591
|
-
|
|
6647
|
+
Effect39.provide(InitializeService.Live),
|
|
6648
|
+
Effect39.provide(FileWatcherService.Live)
|
|
5592
6649
|
).pipe(
|
|
5593
6650
|
/** Domain */
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
6651
|
+
Effect39.provide(ClaudeCodeLifeCycleService.Live),
|
|
6652
|
+
Effect39.provide(ClaudeCodePermissionService.Live),
|
|
6653
|
+
Effect39.provide(ClaudeCodeSessionProcessService.Live),
|
|
6654
|
+
Effect39.provide(ClaudeCodeService.Live),
|
|
6655
|
+
Effect39.provide(GitService.Live),
|
|
6656
|
+
Effect39.provide(SchedulerService.Live),
|
|
6657
|
+
Effect39.provide(SchedulerConfigBaseDir.Live)
|
|
5601
6658
|
).pipe(
|
|
5602
6659
|
/** Infrastructure */
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
6660
|
+
Effect39.provide(ProjectRepository.Live),
|
|
6661
|
+
Effect39.provide(SessionRepository.Live),
|
|
6662
|
+
Effect39.provide(ProjectMetaService.Live),
|
|
6663
|
+
Effect39.provide(SessionMetaService.Live),
|
|
6664
|
+
Effect39.provide(VirtualConversationDatabase.Live)
|
|
5608
6665
|
).pipe(
|
|
5609
6666
|
/** Platform */
|
|
5610
|
-
|
|
5611
|
-
|
|
6667
|
+
Effect39.provide(platformLayer),
|
|
6668
|
+
Effect39.provide(NodeContext2.layer)
|
|
5612
6669
|
);
|
|
5613
|
-
await
|
|
6670
|
+
await Effect39.runPromise(program);
|
|
5614
6671
|
var port = isDevelopment ? (
|
|
5615
6672
|
// biome-ignore lint/style/noProcessEnv: allow only here
|
|
5616
6673
|
process.env.DEV_BE_PORT ?? "3401"
|