@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 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 Effect38 } from "effect";
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 QueueOperationEntrySchema = z12.union([
387
- z12.object({
388
- type: z12.literal("queue-operation"),
389
- operation: z12.literal("enqueue"),
390
- content: z12.string(),
391
- sessionId: z12.string(),
392
- timestamp: z12.iso.datetime()
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
- z12.object({
395
- type: z12.literal("queue-operation"),
396
- operation: z12.literal("dequeue"),
397
- sessionId: z12.string(),
398
- timestamp: z12.iso.datetime()
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 z13 } from "zod";
404
- var SummaryEntrySchema = z13.object({
405
- type: z13.literal("summary"),
406
- summary: z13.string(),
407
- leafUuid: z13.string().uuid()
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 z14 } from "zod";
441
+ import { z as z15 } from "zod";
412
442
  var SystemEntrySchema = BaseEntrySchema.extend({
413
443
  // discriminator
414
- type: z14.literal("system"),
444
+ type: z15.literal("system"),
415
445
  // required
416
- content: z14.string(),
417
- toolUseID: z14.string(),
418
- level: z14.enum(["info"])
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: "ja",
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 Effect34, Layer as Layer30 } from "effect";
4744
- var LayerImpl23 = Effect34.gen(function* () {
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
+ "<": "&lt;",
5001
+ ">": "&gt;",
5002
+ '"': "&quot;",
5003
+ "'": "&#039;"
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) => Effect34.gen(function* () {
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 Effect35, Layer as Layer31, Ref as Ref11, Schedule as Schedule2 } from "effect";
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
- Effect35.gen(function* () {
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 Effect35.gen(function* () {
5837
+ return Effect36.gen(function* () {
4788
5838
  yield* fileWatcher.startWatching();
4789
- const daemon = Effect35.repeat(
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* Effect35.forkDaemon(daemon);
5844
+ yield* Effect36.forkDaemon(daemon);
4795
5845
  console.log("after starting heartbeat fork");
4796
5846
  const onSessionChanged = (event) => {
4797
- Effect35.runFork(
5847
+ Effect36.runFork(
4798
5848
  projectMetaService.invalidateProject(event.projectId)
4799
5849
  );
4800
- Effect35.runFork(
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
- Effect35.runFork(
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* Effect35.gen(function* () {
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* Effect35.all(
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
- Effect35.catchAll(() => Effect35.void),
4841
- Effect35.withSpan("initialize-cache")
5890
+ Effect36.catchAll(() => Effect36.void),
5891
+ Effect36.withSpan("initialize-cache")
4842
5892
  );
4843
- }).pipe(Effect35.withSpan("start-initialization"));
5893
+ }).pipe(Effect36.withSpan("start-initialization"));
4844
5894
  };
4845
- const stopCleanup = () => Effect35.gen(function* () {
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 Effect37, Runtime as Runtime3 } from "effect";
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.3",
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.92.1",
4924
- "@effect/platform-node": "0.98.4",
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.18.4",
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 Effect36 } from "effect";
6194
+ import { Effect as Effect37 } from "effect";
5144
6195
  var effectToResponse = async (ctx, effect) => {
5145
- const result = await Effect36.runPromise(effect);
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
- hideNoUserMessageSession: true,
5177
- unifySameTitleSession: true,
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) => Effect37.gen(function* () {
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* Effect37.runtime();
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 Effect37.runPromise(
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(Effect37.provide(runtime))
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(Effect37.provide(runtime))
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(Effect37.provide(runtime))
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(Effect37.provide(runtime))
6329
+ sessionController.getSession({ ...c.req.param() }).pipe(Effect38.provide(runtime))
5282
6330
  );
5283
6331
  return response;
5284
- }).get("/api/projects/:projectId/git/current-revisions", async (c) => {
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(Effect37.provide(runtime))
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(Effect37.provide(runtime))
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(Effect37.provide(runtime))
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(Effect37.provide(runtime))
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(Effect37.provide(runtime))
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(Effect37.provide(runtime))
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(Effect37.provide(runtime))
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(Effect37.provide(runtime))
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(Effect37.provide(runtime))
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 Effect37.runFork(
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(Effect37.provide(TypeSafeSSE.make(rawStream)))
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(Effect37.provide(runtime))
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(Effect37.provide(runtime))
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(Effect37.provide(runtime))
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(Effect37.provide(runtime))
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(Effect37.provide(runtime))
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
- Effect38.provide(ProjectController.Live),
5579
- Effect38.provide(SessionController.Live),
5580
- Effect38.provide(GitController.Live),
5581
- Effect38.provide(ClaudeCodeController.Live),
5582
- Effect38.provide(ClaudeCodeSessionProcessController.Live),
5583
- Effect38.provide(ClaudeCodePermissionController.Live),
5584
- Effect38.provide(FileSystemController.Live),
5585
- Effect38.provide(SSEController.Live),
5586
- Effect38.provide(SchedulerController.Live),
5587
- Effect38.provide(FeatureFlagController.Live)
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
- Effect38.provide(InitializeService.Live),
5591
- Effect38.provide(FileWatcherService.Live)
6647
+ Effect39.provide(InitializeService.Live),
6648
+ Effect39.provide(FileWatcherService.Live)
5592
6649
  ).pipe(
5593
6650
  /** Domain */
5594
- Effect38.provide(ClaudeCodeLifeCycleService.Live),
5595
- Effect38.provide(ClaudeCodePermissionService.Live),
5596
- Effect38.provide(ClaudeCodeSessionProcessService.Live),
5597
- Effect38.provide(ClaudeCodeService.Live),
5598
- Effect38.provide(GitService.Live),
5599
- Effect38.provide(SchedulerService.Live),
5600
- Effect38.provide(SchedulerConfigBaseDir.Live)
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
- Effect38.provide(ProjectRepository.Live),
5604
- Effect38.provide(SessionRepository.Live),
5605
- Effect38.provide(ProjectMetaService.Live),
5606
- Effect38.provide(SessionMetaService.Live),
5607
- Effect38.provide(VirtualConversationDatabase.Live)
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
- Effect38.provide(platformLayer),
5611
- Effect38.provide(NodeContext2.layer)
6667
+ Effect39.provide(platformLayer),
6668
+ Effect39.provide(NodeContext2.layer)
5612
6669
  );
5613
- await Effect38.runPromise(program);
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"