@kimuson/claude-code-viewer 0.5.9 → 0.6.0-beta.2

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
@@ -2,12 +2,12 @@
2
2
 
3
3
  // src/server/main.ts
4
4
  import { Command as Command3 } from "commander";
5
- import { Effect as Effect53 } from "effect";
5
+ import { Effect as Effect65 } from "effect";
6
6
 
7
7
  // package.json
8
8
  var package_default = {
9
9
  name: "@kimuson/claude-code-viewer",
10
- version: "0.5.9",
10
+ version: "0.6.0-beta.2",
11
11
  description: "A full-featured web-based Claude Code client that provides complete interactive functionality for managing Claude Code projects.",
12
12
  type: "module",
13
13
  license: "MIT",
@@ -28,7 +28,7 @@ var package_default = {
28
28
  scripts: {
29
29
  dev: "run-p 'dev:*'",
30
30
  "dev:frontend": "vite",
31
- "dev:backend": "NODE_ENV=development tsx watch src/server/main.ts --env-file-if-exists=.env.local",
31
+ "dev:backend": "CCV_ENV=development tsx watch --env-file-if-exists=.env.local src/server/main.ts",
32
32
  start: "node dist/main.js",
33
33
  build: "./scripts/build.sh",
34
34
  "build:frontend": "vite build",
@@ -39,7 +39,7 @@ var package_default = {
39
39
  fix: "run-s 'fix:*'",
40
40
  "fix:biome-format": "biome format --write .",
41
41
  "fix:biome-lint": "biome check --write --unsafe .",
42
- typecheck: "tsc --noEmit",
42
+ typecheck: "tsgo --noEmit",
43
43
  test: "vitest --run",
44
44
  "test:watch": "vitest",
45
45
  e2e: "./scripts/e2e/exec_e2e.sh",
@@ -52,7 +52,7 @@ var package_default = {
52
52
  },
53
53
  dependencies: {
54
54
  "@anthropic-ai/claude-agent-sdk": "0.2.20",
55
- "@anthropic-ai/claude-code": "2.0.24",
55
+ "@anthropic-ai/claude-code": "2.1.29",
56
56
  "@anthropic-ai/sdk": "0.71.2",
57
57
  "@effect/cluster": "0.56.1",
58
58
  "@effect/experimental": "0.58.0",
@@ -63,6 +63,7 @@ var package_default = {
63
63
  "@effect/workflow": "0.16.0",
64
64
  "@hono/node-server": "1.19.9",
65
65
  "@hono/zod-validator": "0.7.6",
66
+ "@hookform/resolvers": "^5.2.2",
66
67
  "@lingui/core": "5.9.0",
67
68
  "@lingui/react": "5.9.0",
68
69
  "@radix-ui/react-avatar": "1.1.11",
@@ -75,13 +76,17 @@ var package_default = {
75
76
  "@radix-ui/react-slot": "1.2.4",
76
77
  "@radix-ui/react-tabs": "1.1.13",
77
78
  "@radix-ui/react-tooltip": "1.2.8",
79
+ "@replit/ruspty": "^3.6.0",
78
80
  "@tailwindcss/vite": "4.1.18",
79
81
  "@tanstack/react-devtools": "0.9.2",
80
82
  "@tanstack/react-query": "5.90.20",
81
83
  "@tanstack/react-router": "1.156.0",
82
84
  "@tanstack/react-router-devtools": "1.156.0",
85
+ "@xterm/addon-fit": "0.10.0",
86
+ "@xterm/xterm": "5.5.0",
83
87
  "class-variance-authority": "0.7.1",
84
88
  clsx: "2.1.1",
89
+ cmdk: "^1.1.1",
85
90
  commander: "^14.0.2",
86
91
  "date-fns": "4.1.0",
87
92
  effect: "3.19.15",
@@ -92,15 +97,18 @@ var package_default = {
92
97
  minisearch: "7.2.0",
93
98
  "parse-git-diff": "0.0.19",
94
99
  prexit: "2.3.0",
100
+ "radix-ui": "^1.4.3",
95
101
  react: "19.2.3",
96
102
  "react-dom": "19.2.3",
97
103
  "react-error-boundary": "6.1.0",
104
+ "react-hook-form": "^7.71.1",
98
105
  "react-markdown": "10.1.0",
99
106
  "react-syntax-highlighter": "16.1.0",
100
107
  "remark-gfm": "4.0.1",
101
108
  sonner: "2.0.7",
102
109
  "tailwind-merge": "3.4.0",
103
110
  ulid: "3.0.2",
111
+ ws: "8.18.3",
104
112
  zod: "4.3.6"
105
113
  },
106
114
  devDependencies: {
@@ -118,6 +126,8 @@ var package_default = {
118
126
  "@types/react": "19.2.9",
119
127
  "@types/react-dom": "19.2.3",
120
128
  "@types/react-syntax-highlighter": "15.5.13",
129
+ "@types/ws": "8.18.0",
130
+ "@typescript/native-preview": "7.0.0-dev.20260207.1",
121
131
  "@vitejs/plugin-react-swc": "4.2.2",
122
132
  dotenv: "17.2.3",
123
133
  esbuild: "0.27.2",
@@ -214,12 +224,12 @@ var checkDeprecatedEnvs = Effect.gen(function* () {
214
224
  });
215
225
 
216
226
  // src/server/startServer.ts
217
- import { readFile as readFile2 } from "node:fs/promises";
218
- import { resolve as resolve6 } from "node:path";
227
+ import { readFile as readFile3 } from "node:fs/promises";
228
+ import { resolve as resolve7 } from "node:path";
219
229
  import { NodeContext as NodeContext2 } from "@effect/platform-node";
220
- import { serve } from "@hono/node-server";
230
+ import { createAdaptorServer } from "@hono/node-server";
221
231
  import { serveStatic } from "@hono/node-server/serve-static";
222
- import { Effect as Effect52, Layer as Layer43 } from "effect";
232
+ import { Effect as Effect64, Layer as Layer44 } from "effect";
223
233
 
224
234
  // src/server/core/agent-session/index.ts
225
235
  import { Layer as Layer3 } from "effect";
@@ -690,6 +700,10 @@ import { Context as Context3, Effect as Effect4, Layer as Layer4, Ref } from "ef
690
700
  var getOptionalEnv2 = (key) => {
691
701
  return process.env[key] ?? void 0;
692
702
  };
703
+ var isFlagEnabled = (value) => {
704
+ if (!value) return false;
705
+ return value === "1" || value.toLowerCase() === "true";
706
+ };
693
707
  var LayerImpl3 = Effect4.gen(function* () {
694
708
  const ccvOptionsRef = yield* Ref.make(void 0);
695
709
  const loadCliOptions = (cliOptions) => {
@@ -703,7 +717,10 @@ var LayerImpl3 = Effect4.gen(function* () {
703
717
  hostname: cliOptions.hostname ?? getOptionalEnv2("HOSTNAME") ?? "localhost",
704
718
  password: cliOptions.password ?? getOptionalEnv2("CCV_PASSWORD") ?? void 0,
705
719
  executable: cliOptions.executable ?? getOptionalEnv2("CCV_CC_EXECUTABLE_PATH") ?? void 0,
706
- claudeDir: cliOptions.claudeDir ?? getOptionalEnv2("CCV_GLOBAL_CLAUDE_DIR")
720
+ claudeDir: cliOptions.claudeDir ?? getOptionalEnv2("CCV_GLOBAL_CLAUDE_DIR"),
721
+ terminalDisabled: cliOptions.terminalDisabled ?? (isFlagEnabled(getOptionalEnv2("CCV_TERMINAL_DISABLED")) ? true : void 0),
722
+ terminalShell: cliOptions.terminalShell ?? getOptionalEnv2("CCV_TERMINAL_SHELL") ?? void 0,
723
+ terminalUnrestricted: cliOptions.terminalUnrestricted ?? (isFlagEnabled(getOptionalEnv2("CCV_TERMINAL_UNRESTRICTED")) ? true : void 0)
707
724
  };
708
725
  });
709
726
  });
@@ -940,8 +957,8 @@ var LayerImpl6 = Effect8.gen(function* () {
940
957
  dirents.filter((name) => name.endsWith(".jsonl")).map(
941
958
  (name) => Effect8.gen(function* () {
942
959
  const fullPath = path.resolve(claudeProjectPath, name);
943
- const stat = yield* fs.stat(fullPath);
944
- const mtime = Option.getOrElse(stat.mtime, () => /* @__PURE__ */ new Date(0));
960
+ const stat2 = yield* fs.stat(fullPath);
961
+ const mtime = Option.getOrElse(stat2.mtime, () => /* @__PURE__ */ new Date(0));
945
962
  return {
946
963
  fullPath,
947
964
  mtime
@@ -1006,13 +1023,13 @@ var LayerImpl7 = Effect9.gen(function* () {
1006
1023
  if (!exists) {
1007
1024
  return yield* Effect9.fail(new Error("Project not found"));
1008
1025
  }
1009
- const stat = yield* fs.stat(fullPath);
1026
+ const stat2 = yield* fs.stat(fullPath);
1010
1027
  const meta = yield* projectMetaService.getProjectMeta(projectId);
1011
1028
  return {
1012
1029
  project: {
1013
1030
  id: projectId,
1014
1031
  claudeProjectPath: fullPath,
1015
- lastModifiedAt: Option2.getOrElse(stat.mtime, () => /* @__PURE__ */ new Date()),
1032
+ lastModifiedAt: Option2.getOrElse(stat2.mtime, () => /* @__PURE__ */ new Date()),
1016
1033
  meta
1017
1034
  }
1018
1035
  };
@@ -1036,10 +1053,10 @@ var LayerImpl7 = Effect9.gen(function* () {
1036
1053
  (yield* context.claudeCodePaths).claudeProjectsDirPath,
1037
1054
  entry
1038
1055
  );
1039
- const stat = yield* Effect9.tryPromise(
1056
+ const stat2 = yield* Effect9.tryPromise(
1040
1057
  () => fs.stat(fullPath).pipe(Effect9.runPromise)
1041
1058
  ).pipe(Effect9.catchAll(() => Effect9.succeed(null)));
1042
- if (!stat || stat.type !== "Directory") {
1059
+ if (!stat2 || stat2.type !== "Directory") {
1043
1060
  return null;
1044
1061
  }
1045
1062
  const id = encodeProjectId(fullPath);
@@ -1047,7 +1064,7 @@ var LayerImpl7 = Effect9.gen(function* () {
1047
1064
  return {
1048
1065
  id,
1049
1066
  claudeProjectPath: fullPath,
1050
- lastModifiedAt: Option2.getOrElse(stat.mtime, () => /* @__PURE__ */ new Date()),
1067
+ lastModifiedAt: Option2.getOrElse(stat2.mtime, () => /* @__PURE__ */ new Date()),
1051
1068
  meta
1052
1069
  };
1053
1070
  })
@@ -1241,7 +1258,6 @@ var parseMcpListOutput = (output) => {
1241
1258
 
1242
1259
  // src/server/core/claude-code/models/ClaudeCode.ts
1243
1260
  import * as agentSdk from "@anthropic-ai/claude-agent-sdk";
1244
- import * as claudeCodeSdk from "@anthropic-ai/claude-code";
1245
1261
  import { Command, Path as Path7 } from "@effect/platform";
1246
1262
  import { Data, Effect as Effect11 } from "effect";
1247
1263
  import { uniq } from "es-toolkit";
@@ -1262,6 +1278,10 @@ var ClaudeCodePathNotFoundError = class extends Data.TaggedError(
1262
1278
  "ClaudeCodePathNotFoundError"
1263
1279
  ) {
1264
1280
  };
1281
+ var ClaudeCodeAgentSdkNotSupportedError = class extends Data.TaggedError(
1282
+ "ClaudeCodeAgentSdkNotSupportedError"
1283
+ ) {
1284
+ };
1265
1285
  var resolveClaudeCodePath = Effect11.gen(function* () {
1266
1286
  const path = yield* Path7.Path;
1267
1287
  const ccvOptionsService = yield* CcvOptionsService;
@@ -1357,57 +1377,42 @@ var getAvailableFeatures = (claudeCodeVersion) => ({
1357
1377
  patch: 77
1358
1378
  }) : false
1359
1379
  });
1360
- var query3 = (prompt, options) => {
1361
- const { canUseTool, permissionMode, hooks, ...baseOptions } = options;
1380
+ var query2 = (prompt, options) => {
1381
+ const {
1382
+ canUseTool,
1383
+ permissionMode,
1384
+ hooks,
1385
+ systemPrompt,
1386
+ settingSources,
1387
+ ...baseOptions
1388
+ } = options;
1362
1389
  return Effect11.gen(function* () {
1363
1390
  const { claudeCodeExecutablePath, claudeCodeVersion } = yield* Config;
1364
1391
  const availableFeatures = getAvailableFeatures(claudeCodeVersion);
1365
1392
  const options2 = {
1366
- pathToClaudeCodeExecutable: claudeCodeExecutablePath,
1367
1393
  ...baseOptions,
1368
- disallowedTools: ["AskUserQuestion"],
1394
+ systemPrompt,
1395
+ settingSources,
1396
+ pathToClaudeCodeExecutable: claudeCodeExecutablePath,
1397
+ disallowedTools: [
1398
+ "AskUserQuestion",
1399
+ ...baseOptions.disallowedTools ?? []
1400
+ ],
1369
1401
  // Cannot answer from web interface instead of CLI
1370
1402
  ...availableFeatures.canUseTool ? { canUseTool, permissionMode } : {
1371
1403
  permissionMode: "bypassPermissions"
1372
1404
  }
1373
1405
  };
1374
- if (availableFeatures.agentSdk) {
1375
- return agentSdk.query({
1376
- prompt,
1377
- options: {
1378
- systemPrompt: { type: "preset", preset: "claude_code" },
1379
- settingSources: ["user", "project", "local"],
1380
- ...options2
1381
- }
1406
+ if (!availableFeatures.agentSdk) {
1407
+ return yield* new ClaudeCodeAgentSdkNotSupportedError({
1408
+ message: "Agent SDK is not supported in this version of Claude Code"
1382
1409
  });
1383
1410
  }
1384
- const fallbackCanUseTool = (() => {
1385
- const canUseTool2 = options2.canUseTool;
1386
- if (canUseTool2 === void 0) {
1387
- return void 0;
1388
- }
1389
- const fn = async (toolName, input, canUseToolOptions) => {
1390
- const response = await canUseTool2(toolName, input, {
1391
- signal: canUseToolOptions.signal,
1392
- suggestions: canUseToolOptions.suggestions,
1393
- toolUseID: void 0
1394
- });
1395
- return response;
1396
- };
1397
- return fn;
1398
- })();
1399
- return claudeCodeSdk.query({
1411
+ return agentSdk.query({
1400
1412
  prompt,
1401
1413
  options: {
1402
- ...baseOptions,
1403
- disallowedTools: ["AskUserQuestion"],
1404
- // Cannot answer from web interface instead of CLI
1405
- permissionMode: (
1406
- // fallback unsupported permission modes
1407
- permissionMode === "delegate" || permissionMode === "dontAsk" ? "bypassPermissions" : permissionMode
1408
- ),
1409
- hooks,
1410
- canUseTool: fallbackCanUseTool
1414
+ settingSources: ["user", "project", "local"],
1415
+ ...options2
1411
1416
  }
1412
1417
  });
1413
1418
  });
@@ -1635,7 +1640,7 @@ var LayerImpl10 = Effect15.gen(function* () {
1635
1640
  return response;
1636
1641
  });
1637
1642
  const createCanUseToolRelatedOptions = (options) => {
1638
- const { taskId, userConfig, sessionId } = options;
1643
+ const { turnId, userConfig, sessionId } = options;
1639
1644
  return Effect15.gen(function* () {
1640
1645
  const claudeCodeConfig = yield* Config;
1641
1646
  if (!getAvailableFeatures(claudeCodeConfig.claudeCodeVersion).canUseTool) {
@@ -1659,7 +1664,7 @@ var LayerImpl10 = Effect15.gen(function* () {
1659
1664
  }
1660
1665
  const permissionRequest = {
1661
1666
  id: ulid(),
1662
- taskId,
1667
+ turnId,
1663
1668
  sessionId,
1664
1669
  toolName,
1665
1670
  toolInput,
@@ -1840,10 +1845,10 @@ var controllablePromise = () => {
1840
1845
  const promiseRef = {
1841
1846
  status: "pending"
1842
1847
  };
1843
- const promise = new Promise((resolve7, reject) => {
1848
+ const promise = new Promise((resolve8, reject) => {
1844
1849
  promiseResolve = (value) => {
1845
1850
  promiseRef.status = "resolved";
1846
- resolve7(value);
1851
+ resolve8(value);
1847
1852
  };
1848
1853
  promiseReject = (reason) => {
1849
1854
  promiseRef.status = "rejected";
@@ -1865,7 +1870,7 @@ import { Context as Context16, Effect as Effect21, Layer as Layer18, Option as O
1865
1870
 
1866
1871
  // src/server/core/claude-code/functions/parseUserMessage.ts
1867
1872
  import { z as z21 } from "zod";
1868
- var regExp = /<(?<tag>[^>]+)>(?<content>\s*[^<]*?\s*)<\/\k<tag>>/g;
1873
+ var regExp = /<(?<tag>[^>]+)>(?<content>[\s\S]*?)<\/\k<tag>>/g;
1869
1874
  var matchSchema = z21.object({
1870
1875
  tag: z21.string(),
1871
1876
  content: z21.string()
@@ -2252,6 +2257,11 @@ var ignoreCommands = [
2252
2257
  "/mcp",
2253
2258
  "/memory"
2254
2259
  ];
2260
+ var localCommandCaveatText = "Caveat: The messages below were generated by the user while running local commands. DO NOT respond to these messages or otherwise consider them in your response unless the user explicitly asks you to.";
2261
+ var localCommandCaveatPattern = /<local-command-caveat>[\s\S]*?<\/local-command-caveat>/;
2262
+ var isLocalCommandCaveat = (text) => {
2263
+ return text === localCommandCaveatText || localCommandCaveatPattern.test(text);
2264
+ };
2255
2265
  var extractFirstUserMessage = (conversation) => {
2256
2266
  if (conversation.type !== "user") {
2257
2267
  return void 0;
@@ -2263,16 +2273,13 @@ var extractFirstUserMessage = (conversation) => {
2263
2273
  if (firstUserText === null) {
2264
2274
  return void 0;
2265
2275
  }
2266
- if (firstUserText === "Caveat: The messages below were generated by the user while running local commands. DO NOT respond to these messages or otherwise consider them in your response unless the user explicitly asks you to.") {
2276
+ if (isLocalCommandCaveat(firstUserText)) {
2267
2277
  return void 0;
2268
2278
  }
2269
2279
  if (firstUserText === "Warmup") {
2270
2280
  return void 0;
2271
2281
  }
2272
2282
  const command = parseUserMessage(firstUserText);
2273
- if (command.kind === "local-command") {
2274
- return void 0;
2275
- }
2276
2283
  if (command.kind === "command" && ignoreCommands.includes(command.commandName)) {
2277
2284
  return void 0;
2278
2285
  }
@@ -2295,7 +2302,10 @@ var SessionMetaService = class extends Context15.Tag("SessionMetaService")() {
2295
2302
  const getFirstUserMessage = (jsonlFilePath, lines) => Effect20.gen(function* () {
2296
2303
  const cached = yield* firstUserMessageCache.get(jsonlFilePath);
2297
2304
  if (cached !== void 0) {
2298
- return cached;
2305
+ if (cached !== null && cached.kind === "text" && isLocalCommandCaveat(cached.content)) {
2306
+ } else {
2307
+ return cached;
2308
+ }
2299
2309
  }
2300
2310
  let firstUserMessage = null;
2301
2311
  for (const line of lines) {
@@ -2411,7 +2421,7 @@ var LayerImpl13 = Effect21.gen(function* () {
2411
2421
  const content = yield* fs.readFileString(sessionPath);
2412
2422
  const allLines = content.split("\n").filter((line) => line.trim());
2413
2423
  const conversations = parseJsonl(allLines.join("\n"));
2414
- const stat = yield* fs.stat(sessionPath);
2424
+ const stat2 = yield* fs.stat(sessionPath);
2415
2425
  const meta = yield* sessionMetaService.getSessionMeta(
2416
2426
  projectId,
2417
2427
  sessionId
@@ -2440,7 +2450,7 @@ var LayerImpl13 = Effect21.gen(function* () {
2440
2450
  jsonlFilePath: sessionPath,
2441
2451
  meta,
2442
2452
  conversations: isBroken ? conversations : mergedConversations,
2443
- lastModifiedAt: Option3.getOrElse(stat.mtime, () => /* @__PURE__ */ new Date())
2453
+ lastModifiedAt: Option3.getOrElse(stat2.mtime, () => /* @__PURE__ */ new Date())
2444
2454
  };
2445
2455
  return sessionDetail2;
2446
2456
  }) : (() => {
@@ -2504,16 +2514,16 @@ var LayerImpl13 = Effect21.gen(function* () {
2504
2514
  (entry) => Effect21.gen(function* () {
2505
2515
  const fullPath = path.resolve(claudeProjectPath, entry);
2506
2516
  const sessionId = encodeSessionId(fullPath);
2507
- const stat = yield* Effect21.tryPromise(
2517
+ const stat2 = yield* Effect21.tryPromise(
2508
2518
  () => fs.stat(fullPath).pipe(Effect21.runPromise)
2509
2519
  ).pipe(Effect21.catchAll(() => Effect21.succeed(null)));
2510
- if (!stat) {
2520
+ if (!stat2) {
2511
2521
  return null;
2512
2522
  }
2513
2523
  return {
2514
2524
  id: sessionId,
2515
2525
  jsonlFilePath: fullPath,
2516
- lastModifiedAt: Option3.getOrElse(stat.mtime, () => /* @__PURE__ */ new Date())
2526
+ lastModifiedAt: Option3.getOrElse(stat2.mtime, () => /* @__PURE__ */ new Date())
2517
2527
  };
2518
2528
  })
2519
2529
  );
@@ -2698,42 +2708,6 @@ var createMessageGenerator = () => {
2698
2708
  };
2699
2709
  };
2700
2710
 
2701
- // src/server/core/claude-code/functions/fallbackSdkMessage.ts
2702
- var fallbackSdkMessage = (message) => {
2703
- if (message.type === "system") {
2704
- if (message.subtype === "init") {
2705
- return {
2706
- ...message,
2707
- plugins: []
2708
- };
2709
- }
2710
- if (message.subtype === "hook_response") {
2711
- return {
2712
- ...message
2713
- };
2714
- }
2715
- return message;
2716
- }
2717
- if (message.type === "result") {
2718
- if (message.subtype === "success") {
2719
- return {
2720
- ...message,
2721
- modelUsage: {
2722
- ...message.modelUsage
2723
- }
2724
- };
2725
- }
2726
- return {
2727
- ...message,
2728
- errors: [],
2729
- modelUsage: {
2730
- ...message.modelUsage
2731
- }
2732
- };
2733
- }
2734
- return message;
2735
- };
2736
-
2737
2711
  // src/server/core/claude-code/models/CCSessionProcess.ts
2738
2712
  import { Effect as Effect22 } from "effect";
2739
2713
  var isPublic = (process2) => {
@@ -2791,10 +2765,10 @@ var LayerImpl14 = Effect23.gen(function* () {
2791
2765
  const processesRef = yield* Ref8.make([]);
2792
2766
  const eventBus = yield* EventBus;
2793
2767
  const startSessionProcess = (options) => {
2794
- const { sessionDef, taskDef } = options;
2768
+ const { sessionDef, turnDef } = options;
2795
2769
  return Effect23.gen(function* () {
2796
2770
  const task = {
2797
- def: taskDef,
2771
+ def: turnDef,
2798
2772
  status: "pending"
2799
2773
  };
2800
2774
  const newProcess = {
@@ -2829,13 +2803,13 @@ var LayerImpl14 = Effect23.gen(function* () {
2829
2803
  return yield* Effect23.fail(
2830
2804
  new SessionProcessAlreadyAliveError({
2831
2805
  sessionProcessId,
2832
- aliveTaskId: firstAliveTask.def.taskId,
2806
+ aliveTaskId: firstAliveTask.def.turnId,
2833
2807
  aliveTaskSessionId: firstAliveTask.def.sessionId ?? firstAliveTask.sessionId
2834
2808
  })
2835
2809
  );
2836
2810
  }
2837
2811
  const newTask = {
2838
- def: options.taskDef,
2812
+ def: options.turnDef,
2839
2813
  status: "pending"
2840
2814
  };
2841
2815
  const newProcess = {
@@ -2875,11 +2849,11 @@ var LayerImpl14 = Effect23.gen(function* () {
2875
2849
  return processes;
2876
2850
  });
2877
2851
  };
2878
- const getTask = (taskId) => {
2852
+ const getTask = (turnId) => {
2879
2853
  return Effect23.gen(function* () {
2880
2854
  const processes = yield* Ref8.get(processesRef);
2881
2855
  const result = processes.flatMap((p) => {
2882
- const found = p.tasks.find((t) => t.def.taskId === taskId);
2856
+ const found = p.tasks.find((t) => t.def.turnId === turnId);
2883
2857
  if (found === void 0) {
2884
2858
  return [];
2885
2859
  }
@@ -2891,7 +2865,7 @@ var LayerImpl14 = Effect23.gen(function* () {
2891
2865
  ];
2892
2866
  }).at(0);
2893
2867
  if (result === void 0) {
2894
- return yield* Effect23.fail(new TaskNotFoundError({ taskId }));
2868
+ return yield* Effect23.fail(new TaskNotFoundError({ turnId }));
2895
2869
  }
2896
2870
  return result;
2897
2871
  });
@@ -2925,21 +2899,21 @@ var LayerImpl14 = Effect23.gen(function* () {
2925
2899
  return nextState;
2926
2900
  });
2927
2901
  };
2928
- const changeTaskState = (options) => {
2929
- const { sessionProcessId, taskId, nextTask } = options;
2902
+ const changeTurnState = (options) => {
2903
+ const { sessionProcessId, turnId, nextTask } = options;
2930
2904
  return Effect23.gen(function* () {
2931
- const { task } = yield* getTask(taskId);
2905
+ const { task } = yield* getTask(turnId);
2932
2906
  yield* Ref8.update(processesRef, (processes) => {
2933
2907
  return processes.map(
2934
2908
  (p) => p.def.sessionProcessId === sessionProcessId ? {
2935
2909
  ...p,
2936
2910
  tasks: p.tasks.map(
2937
- (t) => t.def.taskId === task.def.taskId ? { ...nextTask } : t
2911
+ (t) => t.def.turnId === task.def.turnId ? { ...nextTask } : t
2938
2912
  )
2939
2913
  } : p
2940
2914
  );
2941
2915
  });
2942
- const updated = yield* getTask(taskId);
2916
+ const updated = yield* getTask(turnId);
2943
2917
  if (updated === void 0) {
2944
2918
  throw new Error("Unreachable: updatedProcess is undefined");
2945
2919
  }
@@ -2958,9 +2932,9 @@ var LayerImpl14 = Effect23.gen(function* () {
2958
2932
  })
2959
2933
  );
2960
2934
  }
2961
- const newTask = yield* changeTaskState({
2935
+ const newTask = yield* changeTurnState({
2962
2936
  sessionProcessId,
2963
- taskId: currentProcess.currentTask.def.taskId,
2937
+ turnId: currentProcess.currentTask.def.turnId,
2964
2938
  nextTask: {
2965
2939
  status: "running",
2966
2940
  def: currentProcess.currentTask.def
@@ -3044,7 +3018,7 @@ var LayerImpl14 = Effect23.gen(function* () {
3044
3018
  const { sessionProcessId, resultMessage } = options;
3045
3019
  return Effect23.gen(function* () {
3046
3020
  const currentProcess = yield* getSessionProcess(sessionProcessId);
3047
- if (currentProcess.type !== "file_created") {
3021
+ if (currentProcess.type !== "file_created" && currentProcess.type !== "initialized") {
3048
3022
  return yield* Effect23.fail(
3049
3023
  new IllegalStateChangeError({
3050
3024
  from: currentProcess.type,
@@ -3052,9 +3026,9 @@ var LayerImpl14 = Effect23.gen(function* () {
3052
3026
  })
3053
3027
  );
3054
3028
  }
3055
- const newTask = yield* changeTaskState({
3029
+ const newTask = yield* changeTurnState({
3056
3030
  sessionProcessId,
3057
- taskId: currentProcess.currentTask.def.taskId,
3031
+ turnId: currentProcess.currentTask.def.turnId,
3058
3032
  nextTask: {
3059
3033
  status: "completed",
3060
3034
  def: currentProcess.currentTask.def,
@@ -3067,7 +3041,7 @@ var LayerImpl14 = Effect23.gen(function* () {
3067
3041
  type: "paused",
3068
3042
  def: currentProcess.def,
3069
3043
  tasks: currentProcess.tasks.map(
3070
- (t) => t.def.taskId === newTask.def.taskId ? newTask : t
3044
+ (t) => t.def.turnId === newTask.def.turnId ? newTask : t
3071
3045
  ),
3072
3046
  sessionId: currentProcess.sessionId
3073
3047
  }
@@ -3092,9 +3066,9 @@ var LayerImpl14 = Effect23.gen(function* () {
3092
3066
  sessionId: currentProcess.sessionId
3093
3067
  } : void 0;
3094
3068
  if (newTask !== void 0) {
3095
- yield* changeTaskState({
3069
+ yield* changeTurnState({
3096
3070
  sessionProcessId,
3097
- taskId: newTask.def.taskId,
3071
+ turnId: newTask.def.turnId,
3098
3072
  nextTask: newTask
3099
3073
  });
3100
3074
  }
@@ -3104,7 +3078,7 @@ var LayerImpl14 = Effect23.gen(function* () {
3104
3078
  type: "completed",
3105
3079
  def: currentProcess.def,
3106
3080
  tasks: newTask !== void 0 ? currentProcess.tasks.map(
3107
- (t) => t.def.taskId === newTask.def.taskId ? newTask : t
3081
+ (t) => t.def.turnId === newTask.def.turnId ? newTask : t
3108
3082
  ) : currentProcess.tasks,
3109
3083
  sessionId: currentProcess.sessionId
3110
3084
  }
@@ -3129,7 +3103,7 @@ var LayerImpl14 = Effect23.gen(function* () {
3129
3103
  getSessionProcess,
3130
3104
  // task
3131
3105
  getTask,
3132
- changeTaskState
3106
+ changeTurnState
3133
3107
  };
3134
3108
  });
3135
3109
  var ClaudeCodeSessionProcessService = class extends Context17.Tag(
@@ -3148,16 +3122,16 @@ var LayerImpl15 = Effect24.gen(function* () {
3148
3122
  const virtualConversationDatabase = yield* VirtualConversationDatabase;
3149
3123
  const permissionService = yield* ClaudeCodePermissionService;
3150
3124
  const runtime = yield* Effect24.runtime();
3151
- const continueTask = (options) => {
3125
+ const continueSessionProcess = (options) => {
3152
3126
  const { sessionProcessId, baseSessionId, input } = options;
3153
3127
  return Effect24.gen(function* () {
3154
3128
  const { sessionProcess, task } = yield* sessionProcessService.continueSessionProcess({
3155
3129
  sessionProcessId,
3156
- taskDef: {
3130
+ turnDef: {
3157
3131
  type: "continue",
3158
3132
  sessionId: baseSessionId,
3159
3133
  baseSessionId,
3160
- taskId: ulid2()
3134
+ turnId: ulid2()
3161
3135
  }
3162
3136
  });
3163
3137
  const virtualConversation = yield* createVirtualConversation(sessionProcess, {
@@ -3180,8 +3154,8 @@ var LayerImpl15 = Effect24.gen(function* () {
3180
3154
  };
3181
3155
  });
3182
3156
  };
3183
- const startTask = (options) => {
3184
- const { baseSession, input, userConfig } = options;
3157
+ const startSessionProcess = (options) => {
3158
+ const { projectId, cwd, input, userConfig, baseSession, ccOptions } = options;
3185
3159
  return Effect24.gen(function* () {
3186
3160
  const {
3187
3161
  generateMessages,
@@ -3190,20 +3164,28 @@ var LayerImpl15 = Effect24.gen(function* () {
3190
3164
  } = createMessageGenerator();
3191
3165
  const { sessionProcess, task } = yield* sessionProcessService.startSessionProcess({
3192
3166
  sessionDef: {
3193
- projectId: baseSession.projectId,
3194
- cwd: baseSession.cwd,
3167
+ projectId,
3168
+ cwd,
3195
3169
  abortController: new AbortController(),
3196
3170
  setNextMessage,
3197
3171
  sessionProcessId: ulid2()
3198
3172
  },
3199
- taskDef: baseSession.sessionId === void 0 ? {
3173
+ turnDef: baseSession === void 0 ? {
3200
3174
  type: "new",
3201
- taskId: ulid2()
3175
+ turnId: ulid2(),
3176
+ ccOptions
3177
+ } : baseSession.type === "fork" ? {
3178
+ type: "fork",
3179
+ turnId: ulid2(),
3180
+ sessionId: baseSession.sessionId,
3181
+ baseSessionId: baseSession.sessionId,
3182
+ ccOptions
3202
3183
  } : {
3203
3184
  type: "resume",
3204
- taskId: ulid2(),
3185
+ turnId: ulid2(),
3205
3186
  sessionId: void 0,
3206
- baseSessionId: baseSession.sessionId
3187
+ baseSessionId: baseSession.sessionId,
3188
+ ccOptions
3207
3189
  }
3208
3190
  });
3209
3191
  const sessionInitializedPromise = controllablePromise();
@@ -3222,6 +3204,9 @@ var LayerImpl15 = Effect24.gen(function* () {
3222
3204
  const processState = yield* sessionProcessService.getSessionProcess(
3223
3205
  sessionProcess.def.sessionProcessId
3224
3206
  );
3207
+ if (sessionProcess.def.abortController.signal.aborted) {
3208
+ return "break";
3209
+ }
3225
3210
  if (processState.type === "completed") {
3226
3211
  return "break";
3227
3212
  }
@@ -3243,7 +3228,7 @@ var LayerImpl15 = Effect24.gen(function* () {
3243
3228
  });
3244
3229
  if (processState.currentTask.def.type === "new") {
3245
3230
  yield* virtualConversationDatabase.createVirtualConversation(
3246
- baseSession.projectId,
3231
+ projectId,
3247
3232
  message.session_id,
3248
3233
  [virtualConversation]
3249
3234
  );
@@ -3287,15 +3272,17 @@ var LayerImpl15 = Effect24.gen(function* () {
3287
3272
  message.session_id
3288
3273
  );
3289
3274
  }
3290
- if (message.type === "result" && processState.type === "file_created") {
3291
- yield* sessionProcessService.toPausedState({
3292
- sessionProcessId: processState.def.sessionProcessId,
3293
- resultMessage: message
3294
- });
3295
- yield* eventBusService.emit("sessionChanged", {
3296
- projectId: processState.def.projectId,
3297
- sessionId: message.session_id
3298
- });
3275
+ if (message.type === "result") {
3276
+ if (processState.type === "file_created" || processState.type === "initialized") {
3277
+ yield* sessionProcessService.toPausedState({
3278
+ sessionProcessId: processState.def.sessionProcessId,
3279
+ resultMessage: message
3280
+ });
3281
+ yield* eventBusService.emit("sessionChanged", {
3282
+ projectId: processState.def.projectId,
3283
+ sessionId: message.session_id
3284
+ });
3285
+ }
3299
3286
  return "continue";
3300
3287
  }
3301
3288
  return "continue";
@@ -3304,32 +3291,29 @@ var LayerImpl15 = Effect24.gen(function* () {
3304
3291
  const messageIter = await Runtime2.runPromise(runtime)(
3305
3292
  Effect24.gen(function* () {
3306
3293
  const permissionOptions = yield* permissionService.createCanUseToolRelatedOptions({
3307
- taskId: task.def.taskId,
3294
+ turnId: task.def.turnId,
3308
3295
  userConfig,
3309
3296
  sessionId: task.def.baseSessionId
3310
3297
  });
3311
- return yield* query3(generateMessages(), {
3298
+ return yield* query2(generateMessages(), {
3299
+ ...task.def.type === "continue" ? {} : task.def.ccOptions,
3300
+ ...permissionOptions,
3312
3301
  resume: task.def.baseSessionId,
3313
3302
  cwd: sessionProcess.def.cwd,
3314
- abortController: sessionProcess.def.abortController,
3315
- ...permissionOptions
3303
+ abortController: sessionProcess.def.abortController
3316
3304
  });
3317
3305
  })
3318
3306
  );
3319
3307
  setNextMessage(input);
3320
3308
  try {
3321
3309
  for await (const message of messageIter) {
3322
- if (sessionProcess.def.abortController.signal.aborted) {
3323
- break;
3324
- }
3325
- const fallbackMessage = fallbackSdkMessage(message);
3326
3310
  const result = await Runtime2.runPromise(runtime)(
3327
- handleMessage(fallbackMessage)
3311
+ handleMessage(message)
3328
3312
  ).catch((error) => {
3329
3313
  Effect24.runFork(
3330
- sessionProcessService.changeTaskState({
3314
+ sessionProcessService.changeTurnState({
3331
3315
  sessionProcessId: sessionProcess.def.sessionProcessId,
3332
- taskId: task.def.taskId,
3316
+ turnId: task.def.turnId,
3333
3317
  nextTask: {
3334
3318
  status: "failed",
3335
3319
  def: task.def,
@@ -3358,9 +3342,9 @@ var LayerImpl15 = Effect24.gen(function* () {
3358
3342
  sessionFileCreatedPromise.reject(error);
3359
3343
  }
3360
3344
  await Effect24.runPromise(
3361
- sessionProcessService.changeTaskState({
3345
+ sessionProcessService.changeTurnState({
3362
3346
  sessionProcessId: sessionProcess.def.sessionProcessId,
3363
- taskId: task.def.taskId,
3347
+ turnId: task.def.turnId,
3364
3348
  nextTask: {
3365
3349
  status: "failed",
3366
3350
  def: task.def,
@@ -3424,8 +3408,8 @@ var LayerImpl15 = Effect24.gen(function* () {
3424
3408
  }
3425
3409
  });
3426
3410
  return {
3427
- continueTask,
3428
- startTask,
3411
+ continueSessionProcess,
3412
+ startSessionProcess,
3429
3413
  abortTask,
3430
3414
  abortAllTasks,
3431
3415
  getPublicSessionProcesses
@@ -3461,7 +3445,7 @@ var LayerImpl16 = Effect25.gen(function* () {
3461
3445
  };
3462
3446
  });
3463
3447
  const createSessionProcess = (options) => Effect25.gen(function* () {
3464
- const { projectId, input, baseSessionId } = options;
3448
+ const { projectId, input, baseSession, ccOptions } = options;
3465
3449
  const { project } = yield* projectRepository.getProject(projectId);
3466
3450
  const userConfig = yield* userConfigService.getUserConfig();
3467
3451
  if (project.meta.projectPath === null) {
@@ -3470,14 +3454,13 @@ var LayerImpl16 = Effect25.gen(function* () {
3470
3454
  status: 400
3471
3455
  };
3472
3456
  }
3473
- const result = yield* claudeCodeLifeCycleService.startTask({
3474
- baseSession: {
3475
- cwd: project.meta.projectPath,
3476
- projectId,
3477
- sessionId: baseSessionId
3478
- },
3457
+ const result = yield* claudeCodeLifeCycleService.startSessionProcess({
3458
+ projectId,
3459
+ cwd: project.meta.projectPath,
3460
+ baseSession,
3479
3461
  userConfig,
3480
- input
3462
+ input,
3463
+ ccOptions
3481
3464
  });
3482
3465
  const { sessionId } = yield* result.yieldSessionInitialized();
3483
3466
  return {
@@ -3500,7 +3483,7 @@ var LayerImpl16 = Effect25.gen(function* () {
3500
3483
  status: 400
3501
3484
  };
3502
3485
  }
3503
- const result = yield* claudeCodeLifeCycleService.continueTask({
3486
+ const result = yield* claudeCodeLifeCycleService.continueSessionProcess({
3504
3487
  sessionProcessId,
3505
3488
  input,
3506
3489
  baseSessionId
@@ -3538,8 +3521,8 @@ var adaptInternalEventToSSE = (rawStream, options) => {
3538
3521
  const { timeout = 60 * 1e3, cleanUp } = options ?? {};
3539
3522
  const abortController = new AbortController();
3540
3523
  let connectionResolve;
3541
- const connectionPromise = new Promise((resolve7) => {
3542
- connectionResolve = resolve7;
3524
+ const connectionPromise = new Promise((resolve8) => {
3525
+ connectionResolve = resolve8;
3543
3526
  });
3544
3527
  const closeConnection = () => {
3545
3528
  connectionResolve?.();
@@ -4030,6 +4013,272 @@ var getFileCompletion = async (projectPath, basePath = "/") => {
4030
4013
  }
4031
4014
  };
4032
4015
 
4016
+ // src/server/core/file-system/functions/getFileContent.ts
4017
+ import { readFile, stat } from "node:fs/promises";
4018
+ import { extname as extname2, normalize, resolve as resolve5 } from "node:path";
4019
+ var DEFAULT_MAX_FILE_SIZE = 1024 * 1024;
4020
+ var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
4021
+ // Images
4022
+ "png",
4023
+ "jpg",
4024
+ "jpeg",
4025
+ "gif",
4026
+ "bmp",
4027
+ "ico",
4028
+ "webp",
4029
+ "svg",
4030
+ "tiff",
4031
+ "tif",
4032
+ // Archives
4033
+ "zip",
4034
+ "tar",
4035
+ "gz",
4036
+ "bz2",
4037
+ "7z",
4038
+ "rar",
4039
+ "xz",
4040
+ // Executables
4041
+ "exe",
4042
+ "dll",
4043
+ "so",
4044
+ "dylib",
4045
+ "bin",
4046
+ // Documents
4047
+ "pdf",
4048
+ "doc",
4049
+ "docx",
4050
+ "xls",
4051
+ "xlsx",
4052
+ "ppt",
4053
+ "pptx",
4054
+ // Media
4055
+ "mp3",
4056
+ "mp4",
4057
+ "avi",
4058
+ "mov",
4059
+ "mkv",
4060
+ "wav",
4061
+ "flac",
4062
+ // Database
4063
+ "db",
4064
+ "sqlite",
4065
+ "sqlite3",
4066
+ // Other
4067
+ "wasm",
4068
+ "ttf",
4069
+ "otf",
4070
+ "woff",
4071
+ "woff2",
4072
+ "eot"
4073
+ ]);
4074
+ var EXTENSION_TO_LANGUAGE = {
4075
+ ts: "typescript",
4076
+ tsx: "tsx",
4077
+ js: "javascript",
4078
+ jsx: "jsx",
4079
+ mjs: "javascript",
4080
+ cjs: "javascript",
4081
+ json: "json",
4082
+ jsonc: "json",
4083
+ md: "markdown",
4084
+ mdx: "markdown",
4085
+ py: "python",
4086
+ rs: "rust",
4087
+ go: "go",
4088
+ html: "html",
4089
+ htm: "html",
4090
+ css: "css",
4091
+ scss: "scss",
4092
+ sass: "sass",
4093
+ less: "less",
4094
+ yml: "yaml",
4095
+ yaml: "yaml",
4096
+ toml: "toml",
4097
+ xml: "xml",
4098
+ sh: "bash",
4099
+ bash: "bash",
4100
+ zsh: "bash",
4101
+ fish: "fish",
4102
+ ps1: "powershell",
4103
+ sql: "sql",
4104
+ graphql: "graphql",
4105
+ gql: "graphql",
4106
+ java: "java",
4107
+ kt: "kotlin",
4108
+ kts: "kotlin",
4109
+ scala: "scala",
4110
+ rb: "ruby",
4111
+ php: "php",
4112
+ c: "c",
4113
+ h: "c",
4114
+ cpp: "cpp",
4115
+ cc: "cpp",
4116
+ cxx: "cpp",
4117
+ hpp: "cpp",
4118
+ cs: "csharp",
4119
+ swift: "swift",
4120
+ m: "objectivec",
4121
+ mm: "objectivec",
4122
+ r: "r",
4123
+ lua: "lua",
4124
+ vim: "vim",
4125
+ dockerfile: "dockerfile",
4126
+ makefile: "makefile",
4127
+ cmake: "cmake",
4128
+ tf: "hcl",
4129
+ hcl: "hcl",
4130
+ proto: "protobuf",
4131
+ prisma: "prisma",
4132
+ vue: "vue",
4133
+ svelte: "svelte",
4134
+ astro: "astro",
4135
+ zig: "zig",
4136
+ elm: "elm",
4137
+ ex: "elixir",
4138
+ exs: "elixir",
4139
+ erl: "erlang",
4140
+ hrl: "erlang",
4141
+ clj: "clojure",
4142
+ cljs: "clojure",
4143
+ cljc: "clojure",
4144
+ hs: "haskell",
4145
+ lhs: "haskell",
4146
+ ml: "ocaml",
4147
+ mli: "ocaml",
4148
+ fs: "fsharp",
4149
+ fsx: "fsharp",
4150
+ fsi: "fsharp",
4151
+ pl: "perl",
4152
+ pm: "perl",
4153
+ nim: "nim",
4154
+ d: "d",
4155
+ dart: "dart",
4156
+ v: "v",
4157
+ sol: "solidity",
4158
+ ini: "ini",
4159
+ cfg: "ini",
4160
+ conf: "ini",
4161
+ env: "shell",
4162
+ gitignore: "gitignore",
4163
+ editorconfig: "editorconfig",
4164
+ txt: "text"
4165
+ };
4166
+ var detectLanguage = (filePath) => {
4167
+ const ext = extname2(filePath).toLowerCase().slice(1);
4168
+ const basename2 = filePath.split("/").pop() ?? "";
4169
+ const lowerBasename = basename2.toLowerCase();
4170
+ if (lowerBasename === "dockerfile") return "dockerfile";
4171
+ if (lowerBasename === "makefile") return "makefile";
4172
+ if (lowerBasename.startsWith(".env")) return "shell";
4173
+ return EXTENSION_TO_LANGUAGE[ext] ?? "text";
4174
+ };
4175
+ var isBinaryExtension = (filePath) => {
4176
+ const ext = extname2(filePath).toLowerCase().slice(1);
4177
+ return BINARY_EXTENSIONS.has(ext);
4178
+ };
4179
+ var isBinaryContent = (buffer) => {
4180
+ const checkLength = Math.min(buffer.length, 8192);
4181
+ for (let i = 0; i < checkLength; i++) {
4182
+ if (buffer[i] === 0) {
4183
+ return true;
4184
+ }
4185
+ }
4186
+ return false;
4187
+ };
4188
+ var validateFilePath = (projectRoot, filePath) => {
4189
+ if (!filePath || filePath.trim() === "") {
4190
+ return { valid: false, message: "File path cannot be empty" };
4191
+ }
4192
+ if (filePath.includes("\0")) {
4193
+ return { valid: false, message: "File path contains invalid characters" };
4194
+ }
4195
+ if (filePath.includes("..")) {
4196
+ return { valid: false, message: "Path traversal (..) is not allowed" };
4197
+ }
4198
+ const resolvedRoot = resolve5(projectRoot);
4199
+ let resolvedPath;
4200
+ if (filePath.startsWith("/")) {
4201
+ resolvedPath = normalize(filePath);
4202
+ } else {
4203
+ const normalizedPath = normalize(filePath);
4204
+ resolvedPath = resolve5(projectRoot, normalizedPath);
4205
+ }
4206
+ if (!resolvedPath.startsWith(`${resolvedRoot}/`) && resolvedPath !== resolvedRoot) {
4207
+ return { valid: false, message: "Path is outside the project root" };
4208
+ }
4209
+ return { valid: true, resolvedPath };
4210
+ };
4211
+ var getFileContent = async (projectRoot, filePath, options = {}) => {
4212
+ const { maxFileSize = DEFAULT_MAX_FILE_SIZE } = options;
4213
+ const validation = validateFilePath(projectRoot, filePath);
4214
+ if (!validation.valid) {
4215
+ return {
4216
+ success: false,
4217
+ error: "INVALID_PATH",
4218
+ message: validation.message,
4219
+ filePath
4220
+ };
4221
+ }
4222
+ const { resolvedPath } = validation;
4223
+ if (isBinaryExtension(filePath)) {
4224
+ return {
4225
+ success: false,
4226
+ error: "BINARY_FILE",
4227
+ message: "Binary file cannot be displayed",
4228
+ filePath
4229
+ };
4230
+ }
4231
+ try {
4232
+ const fileStat = await stat(resolvedPath);
4233
+ if (!fileStat.isFile()) {
4234
+ return {
4235
+ success: false,
4236
+ error: "NOT_FOUND",
4237
+ message: "File not found or is a directory",
4238
+ filePath
4239
+ };
4240
+ }
4241
+ const buffer = await readFile(resolvedPath);
4242
+ if (isBinaryContent(buffer)) {
4243
+ return {
4244
+ success: false,
4245
+ error: "BINARY_FILE",
4246
+ message: "Binary file cannot be displayed",
4247
+ filePath
4248
+ };
4249
+ }
4250
+ let content = buffer.toString("utf-8");
4251
+ const truncated = buffer.length > maxFileSize;
4252
+ if (truncated) {
4253
+ const truncatedBuffer = buffer.subarray(0, maxFileSize);
4254
+ content = truncatedBuffer.toString("utf-8");
4255
+ }
4256
+ const language = detectLanguage(filePath);
4257
+ return {
4258
+ success: true,
4259
+ content,
4260
+ filePath,
4261
+ truncated,
4262
+ language
4263
+ };
4264
+ } catch (error) {
4265
+ if (error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
4266
+ return {
4267
+ success: false,
4268
+ error: "NOT_FOUND",
4269
+ message: "File not found",
4270
+ filePath
4271
+ };
4272
+ }
4273
+ return {
4274
+ success: false,
4275
+ error: "READ_ERROR",
4276
+ message: error instanceof Error ? error.message : "Failed to read file",
4277
+ filePath
4278
+ };
4279
+ }
4280
+ };
4281
+
4033
4282
  // src/server/core/file-system/presentation/FileSystemController.ts
4034
4283
  var LayerImpl19 = Effect30.gen(function* () {
4035
4284
  const projectRepository = yield* ProjectRepository;
@@ -4083,9 +4332,40 @@ var LayerImpl19 = Effect30.gen(function* () {
4083
4332
  };
4084
4333
  }
4085
4334
  });
4335
+ const getFileContentRoute = (options) => Effect30.gen(function* () {
4336
+ const { projectId, filePath } = options;
4337
+ const { project } = yield* projectRepository.getProject(projectId);
4338
+ if (project.meta.projectPath === null) {
4339
+ return {
4340
+ response: {
4341
+ success: false,
4342
+ error: "PROJECT_PATH_NOT_SET",
4343
+ message: "Project path is not configured. Cannot read files without a project root.",
4344
+ filePath
4345
+ },
4346
+ status: 400
4347
+ };
4348
+ }
4349
+ const projectPath = project.meta.projectPath;
4350
+ const result = yield* Effect30.promise(
4351
+ () => getFileContent(projectPath, filePath)
4352
+ );
4353
+ if (!result.success) {
4354
+ const statusCode = result.error === "NOT_FOUND" ? 404 : 400;
4355
+ return {
4356
+ response: result,
4357
+ status: statusCode
4358
+ };
4359
+ }
4360
+ return {
4361
+ response: result,
4362
+ status: 200
4363
+ };
4364
+ });
4086
4365
  return {
4087
4366
  getFileCompletionRoute,
4088
- getDirectoryListingRoute
4367
+ getDirectoryListingRoute,
4368
+ getFileContentRoute
4089
4369
  };
4090
4370
  });
4091
4371
  var FileSystemController = class extends Context24.Tag("FileSystemController")() {
@@ -4098,8 +4378,8 @@ var FileSystemController = class extends Context24.Tag("FileSystemController")()
4098
4378
  import { Context as Context27, Effect as Effect33, Either as Either2, Layer as Layer29 } from "effect";
4099
4379
 
4100
4380
  // src/server/core/git/functions/getDiff.ts
4101
- import { readFile } from "node:fs/promises";
4102
- import { resolve as resolve5 } from "node:path";
4381
+ import { readFile as readFile2 } from "node:fs/promises";
4382
+ import { resolve as resolve6 } from "node:path";
4103
4383
  import parseGitDiff from "parse-git-diff";
4104
4384
 
4105
4385
  // src/server/core/git/functions/utils.ts
@@ -4296,8 +4576,8 @@ async function getUntrackedFiles(cwd) {
4296
4576
  }
4297
4577
  async function createUntrackedFileDiff(cwd, filePath) {
4298
4578
  try {
4299
- const fullPath = resolve5(cwd, filePath);
4300
- const content = await readFile(fullPath, "utf8");
4579
+ const fullPath = resolve6(cwd, filePath);
4580
+ const content = await readFile2(fullPath, "utf8");
4301
4581
  const lines = content.split("\n");
4302
4582
  const diffLines = lines.map((line, index) => ({
4303
4583
  type: "added",
@@ -4444,9 +4724,13 @@ import { Context as Context25, Effect as Effect31, Layer as Layer27, Ref as Ref1
4444
4724
  import { z as z23 } from "zod";
4445
4725
  var envSchema = z23.object({
4446
4726
  // Frameworks
4447
- NODE_ENV: z23.enum(["development", "production", "test"]).optional().default("development"),
4727
+ CCV_ENV: z23.enum(["development", "production", "test"]).optional().default("development"),
4448
4728
  NEXT_PHASE: z23.string().optional(),
4449
- PATH: z23.string().optional()
4729
+ PATH: z23.string().optional(),
4730
+ SHELL: z23.string().optional(),
4731
+ CCV_TERMINAL_SHELL: z23.string().optional(),
4732
+ CCV_TERMINAL_UNRESTRICTED: z23.string().optional(),
4733
+ CCV_TERMINAL_DISABLED: z23.string().optional()
4450
4734
  });
4451
4735
 
4452
4736
  // src/server/core/platform/services/EnvService.ts
@@ -4477,8 +4761,20 @@ var LayerImpl20 = Effect31.gen(function* () {
4477
4761
  return env[key];
4478
4762
  });
4479
4763
  };
4764
+ const getAllEnv = () => {
4765
+ return Effect31.sync(() => {
4766
+ const entries = [];
4767
+ for (const [key, value] of Object.entries(process.env)) {
4768
+ if (typeof value === "string") {
4769
+ entries.push([key, value]);
4770
+ }
4771
+ }
4772
+ return Object.fromEntries(entries);
4773
+ });
4774
+ };
4480
4775
  return {
4481
- getEnv
4776
+ getEnv,
4777
+ getAllEnv
4482
4778
  };
4483
4779
  });
4484
4780
  var EnvService = class extends Context25.Tag("EnvService")() {
@@ -4832,6 +5128,10 @@ var LayerImpl21 = Effect32.gen(function* () {
4832
5128
  );
4833
5129
  return parseGitCommitsOutput(result);
4834
5130
  });
5131
+ const checkout = (cwd, branchName) => Effect32.gen(function* () {
5132
+ yield* execGitCommand(["checkout", branchName], cwd);
5133
+ return { success: true, branch: branchName };
5134
+ });
4835
5135
  return {
4836
5136
  getBranches,
4837
5137
  getCurrentBranch,
@@ -4845,7 +5145,8 @@ var LayerImpl21 = Effect32.gen(function* () {
4845
5145
  compareCommitHash,
4846
5146
  getCommitsWithParent,
4847
5147
  findBaseBranch,
4848
- getCommitsBetweenBranches
5148
+ getCommitsBetweenBranches,
5149
+ checkout
4849
5150
  };
4850
5151
  });
4851
5152
  var GitService = class extends Context26.Tag("GitService")() {
@@ -5126,12 +5427,75 @@ var LayerImpl22 = Effect33.gen(function* () {
5126
5427
  status: 200
5127
5428
  };
5128
5429
  });
5430
+ const getBranches = (options) => Effect33.gen(function* () {
5431
+ const { projectId } = options;
5432
+ const { project } = yield* projectRepository.getProject(projectId);
5433
+ if (project.meta.projectPath === null) {
5434
+ return {
5435
+ response: { success: false, error: "Project path not found" },
5436
+ status: 400
5437
+ };
5438
+ }
5439
+ const projectPath = project.meta.projectPath;
5440
+ const branchesResult = yield* Effect33.either(
5441
+ gitService.getBranches(projectPath)
5442
+ );
5443
+ if (Either2.isLeft(branchesResult)) {
5444
+ return {
5445
+ response: { success: false, error: "Failed to get branches" },
5446
+ status: 500
5447
+ };
5448
+ }
5449
+ const currentBranchResult = yield* Effect33.either(
5450
+ gitService.getCurrentBranch(projectPath)
5451
+ );
5452
+ const currentBranch = Either2.isRight(currentBranchResult) ? currentBranchResult.right : null;
5453
+ return {
5454
+ response: {
5455
+ success: true,
5456
+ data: {
5457
+ branches: branchesResult.right.data,
5458
+ currentBranch
5459
+ }
5460
+ },
5461
+ status: 200
5462
+ };
5463
+ });
5464
+ const checkoutBranch = (options) => Effect33.gen(function* () {
5465
+ const { projectId, branchName } = options;
5466
+ const { project } = yield* projectRepository.getProject(projectId);
5467
+ if (project.meta.projectPath === null) {
5468
+ return {
5469
+ response: { success: false, error: "Project path not found" },
5470
+ status: 400
5471
+ };
5472
+ }
5473
+ const projectPath = project.meta.projectPath;
5474
+ const checkoutResult = yield* Effect33.either(
5475
+ gitService.checkout(projectPath, branchName)
5476
+ );
5477
+ if (Either2.isLeft(checkoutResult)) {
5478
+ return {
5479
+ response: { success: false, error: "Failed to checkout branch" },
5480
+ status: 500
5481
+ };
5482
+ }
5483
+ return {
5484
+ response: {
5485
+ success: true,
5486
+ branch: checkoutResult.right.branch
5487
+ },
5488
+ status: 200
5489
+ };
5490
+ });
5129
5491
  return {
5130
5492
  getGitDiff,
5131
5493
  commitFiles,
5132
5494
  pushCommits,
5133
5495
  commitAndPush,
5134
- getCurrentRevisions
5496
+ getCurrentRevisions,
5497
+ getBranches,
5498
+ checkoutBranch
5135
5499
  };
5136
5500
  });
5137
5501
  function parsePushError(stderr) {
@@ -5171,6 +5535,9 @@ var GitController = class extends Context27.Tag("GitController")() {
5171
5535
  }
5172
5536
  };
5173
5537
 
5538
+ // src/server/core/platform/ccvEnv.ts
5539
+ var isDevelopmentEnv = (ccvEnv) => ccvEnv === "development";
5540
+
5174
5541
  // src/server/core/project/presentation/ProjectController.ts
5175
5542
  import { FileSystem as FileSystem11, Path as Path15 } from "@effect/platform";
5176
5543
  import { Context as Context28, Effect as Effect35, Layer as Layer30 } from "effect";
@@ -5279,12 +5646,10 @@ var LayerImpl23 = Effect35.gen(function* () {
5279
5646
  const userConfig = yield* userConfigService.getUserConfig();
5280
5647
  const claudeMdPath = path.join(projectPath, "CLAUDE.md");
5281
5648
  const claudeMdExists = yield* fileSystem.exists(claudeMdPath);
5282
- const result = yield* claudeCodeLifeCycleService.startTask({
5283
- baseSession: {
5284
- cwd: projectPath,
5285
- projectId,
5286
- sessionId: void 0
5287
- },
5649
+ const result = yield* claudeCodeLifeCycleService.startSessionProcess({
5650
+ projectId,
5651
+ cwd: projectPath,
5652
+ baseSession: void 0,
5288
5653
  userConfig,
5289
5654
  input: {
5290
5655
  text: claudeMdExists ? "describe this project" : "/init"
@@ -5340,7 +5705,17 @@ var scheduleSchema = z24.discriminatedUnion("type", [
5340
5705
  var messageConfigSchema = z24.object({
5341
5706
  content: z24.string(),
5342
5707
  projectId: z24.string(),
5343
- baseSessionId: z24.string().nullable()
5708
+ baseSession: z24.union([
5709
+ z24.null(),
5710
+ z24.object({
5711
+ type: z24.literal("fork"),
5712
+ sessionId: z24.string()
5713
+ }),
5714
+ z24.object({
5715
+ type: z24.literal("resume"),
5716
+ sessionId: z24.string()
5717
+ })
5718
+ ])
5344
5719
  });
5345
5720
  var jobStatusSchema = z24.enum(["success", "failed"]);
5346
5721
  var schedulerJobSchema = z24.object({
@@ -5475,12 +5850,10 @@ var executeJob = (job) => Effect37.gen(function* () {
5475
5850
  new Error(`Project path not found for projectId: ${message.projectId}`)
5476
5851
  );
5477
5852
  }
5478
- yield* lifeCycleService.startTask({
5479
- baseSession: {
5480
- cwd: project.meta.projectPath,
5481
- projectId: message.projectId,
5482
- sessionId: message.baseSessionId ?? void 0
5483
- },
5853
+ yield* lifeCycleService.startSessionProcess({
5854
+ projectId: message.projectId,
5855
+ cwd: project.meta.projectPath,
5856
+ baseSession: message.baseSession ?? void 0,
5484
5857
  userConfig,
5485
5858
  input: {
5486
5859
  text: message.content
@@ -5938,7 +6311,7 @@ var LayerImpl25 = Effect40.gen(function* () {
5938
6311
  const hasExistingReservedJobForSession = (sessionId) => Effect40.gen(function* () {
5939
6312
  const jobs = yield* schedulerService.getJobs().pipe(Effect40.catchAll(() => Effect40.succeed([])));
5940
6313
  return jobs.some(
5941
- (job) => job.schedule.type === "reserved" && job.message.baseSessionId === sessionId && job.lastRunStatus === null
6314
+ (job) => job.schedule.type === "reserved" && job.message.baseSession?.sessionId === sessionId && job.lastRunStatus === null
5942
6315
  // Not yet executed
5943
6316
  );
5944
6317
  });
@@ -5981,7 +6354,10 @@ var LayerImpl25 = Effect40.gen(function* () {
5981
6354
  message: {
5982
6355
  content: "continue",
5983
6356
  projectId: processProjectId,
5984
- baseSessionId: sessionId
6357
+ baseSession: {
6358
+ type: "resume",
6359
+ sessionId
6360
+ }
5985
6361
  },
5986
6362
  enabled: true
5987
6363
  }).pipe(
@@ -6178,8 +6554,8 @@ var LayerImpl27 = Effect42.gen(function* () {
6178
6554
  const documentEffects = projectEntries.map(
6179
6555
  (projectEntry) => Effect42.gen(function* () {
6180
6556
  const projectPath = path.resolve(claudeProjectsDirPath, projectEntry);
6181
- const stat = yield* fs.stat(projectPath).pipe(Effect42.catchAll(() => Effect42.succeed(null)));
6182
- if (stat?.type !== "Directory") {
6557
+ const stat2 = yield* fs.stat(projectPath).pipe(Effect42.catchAll(() => Effect42.succeed(null)));
6558
+ if (stat2?.type !== "Directory") {
6183
6559
  return [];
6184
6560
  }
6185
6561
  const projectId = encodeProjectId(projectPath);
@@ -6247,14 +6623,14 @@ var LayerImpl27 = Effect42.gen(function* () {
6247
6623
  yield* Ref13.set(indexCacheRef, { index, documents, builtAt: now });
6248
6624
  return { index, documents };
6249
6625
  });
6250
- const search = (query4, limit = 20, projectId) => Effect42.gen(function* () {
6626
+ const search = (query3, limit = 20, projectId) => Effect42.gen(function* () {
6251
6627
  const { claudeProjectsDirPath } = yield* context.claudeCodePaths;
6252
6628
  const dirExists = yield* fs.exists(claudeProjectsDirPath);
6253
6629
  if (!dirExists) {
6254
6630
  return { results: [] };
6255
6631
  }
6256
6632
  const { index: miniSearch, documents } = yield* getIndex();
6257
- const searchResults = miniSearch.search(query4).slice(0, limit * 2);
6633
+ const searchResults = miniSearch.search(query3).slice(0, limit * 2);
6258
6634
  const results = [];
6259
6635
  for (const result of searchResults) {
6260
6636
  if (results.length >= limit) break;
@@ -6264,7 +6640,7 @@ var LayerImpl27 = Effect42.gen(function* () {
6264
6640
  const score = doc.type === "user" ? result.score * 1.2 : result.score;
6265
6641
  const snippetLength = 150;
6266
6642
  const text = doc.text;
6267
- const queryLower = query4.toLowerCase();
6643
+ const queryLower = query3.toLowerCase();
6268
6644
  const textLower = text.toLowerCase();
6269
6645
  const matchIndex = textLower.indexOf(queryLower);
6270
6646
  let snippet;
@@ -6304,8 +6680,8 @@ var SearchService = class extends Context33.Tag("SearchService")() {
6304
6680
  var LayerImpl28 = Effect43.gen(function* () {
6305
6681
  const searchService = yield* SearchService;
6306
6682
  const search = (options) => Effect43.gen(function* () {
6307
- const { query: query4, limit, projectId } = options;
6308
- if (query4.trim().length < 2) {
6683
+ const { query: query3, limit, projectId } = options;
6684
+ if (query3.trim().length < 2) {
6309
6685
  return {
6310
6686
  status: 400,
6311
6687
  response: {
@@ -6314,7 +6690,7 @@ var LayerImpl28 = Effect43.gen(function* () {
6314
6690
  };
6315
6691
  }
6316
6692
  const { results } = yield* searchService.search(
6317
- query4.trim(),
6693
+ query3.trim(),
6318
6694
  limit,
6319
6695
  projectId
6320
6696
  );
@@ -8143,7 +8519,7 @@ var TasksService = class extends Context36.Tag("TasksService")() {
8143
8519
  candidates.map(
8144
8520
  (file) => Effect46.gen(function* () {
8145
8521
  const fullPath = path.join(projectMetaDir, file);
8146
- const stat = yield* fs.stat(fullPath);
8522
+ const stat2 = yield* fs.stat(fullPath);
8147
8523
  const match = file.match(uuidPattern);
8148
8524
  const uuid = match ? match[0] : file;
8149
8525
  const tasksPath = path.join(claudeDir, TASKS_DIR_NAME, uuid);
@@ -8151,7 +8527,7 @@ var TasksService = class extends Context36.Tag("TasksService")() {
8151
8527
  return {
8152
8528
  file,
8153
8529
  uuid,
8154
- mtime: Option4.getOrElse(stat.mtime, () => /* @__PURE__ */ new Date(0)),
8530
+ mtime: Option4.getOrElse(stat2.mtime, () => /* @__PURE__ */ new Date(0)),
8155
8531
  hasTasks
8156
8532
  };
8157
8533
  })
@@ -8266,21 +8642,21 @@ var TasksService = class extends Context36.Tag("TasksService")() {
8266
8642
  }
8267
8643
  return tasks.sort((a, b) => parseInt(a.id, 10) - parseInt(b.id, 10));
8268
8644
  });
8269
- const getTask = (projectPath, taskId, specificSessionId) => Effect46.gen(function* () {
8645
+ const getTask = (projectPath, turnId, specificSessionId) => Effect46.gen(function* () {
8270
8646
  const tasksDir = yield* getTasksDirOrFail(
8271
8647
  projectPath,
8272
8648
  specificSessionId
8273
8649
  );
8274
- const taskFile = path.join(tasksDir, `${taskId}.json`);
8650
+ const taskFile = path.join(tasksDir, `${turnId}.json`);
8275
8651
  const exists = yield* fs.exists(taskFile);
8276
8652
  if (!exists) {
8277
- return yield* Effect46.fail(new Error(`Task ${taskId} not found`));
8653
+ return yield* Effect46.fail(new Error(`Task ${turnId} not found`));
8278
8654
  }
8279
8655
  const content = yield* fs.readFileString(taskFile);
8280
8656
  const task = JSON.parse(content);
8281
8657
  return yield* Effect46.try(() => TaskSchema.parse(task));
8282
8658
  });
8283
- const createTask = (projectPath, taskDef, specificSessionId) => Effect46.gen(function* () {
8659
+ const createTask = (projectPath, turnDef, specificSessionId) => Effect46.gen(function* () {
8284
8660
  const tasksDir = yield* getTasksDirOrFail(
8285
8661
  projectPath,
8286
8662
  specificSessionId
@@ -8306,7 +8682,7 @@ var TasksService = class extends Context36.Tag("TasksService")() {
8306
8682
  status: "pending",
8307
8683
  blocks: [],
8308
8684
  blockedBy: [],
8309
- ...taskDef
8685
+ ...turnDef
8310
8686
  };
8311
8687
  const filePath = path.join(tasksDir, `${newId}.json`);
8312
8688
  yield* fs.writeFileString(filePath, JSON.stringify(newTask, null, 2));
@@ -8380,87 +8756,398 @@ var TasksController = class extends Context37.Tag("TasksController")() {
8380
8756
  }
8381
8757
  };
8382
8758
 
8383
- // src/server/hono/app.ts
8384
- import { Hono } from "hono";
8385
- var honoApp = new Hono();
8759
+ // src/server/core/terminal/TerminalService.ts
8760
+ import { Context as Context38, Effect as Effect48, Layer as Layer40 } from "effect";
8761
+ import { ulid as ulid5 } from "ulid";
8386
8762
 
8387
- // src/server/hono/initialize.ts
8388
- import { Context as Context38, Effect as Effect48, Layer as Layer40, Ref as Ref14, Schedule as Schedule2 } from "effect";
8389
- var InitializeService = class extends Context38.Tag("InitializeService")() {
8390
- static {
8391
- this.Live = Layer40.effect(
8392
- this,
8393
- Effect48.gen(function* () {
8394
- const eventBus = yield* EventBus;
8395
- const fileWatcher = yield* FileWatcherService;
8396
- const projectRepository = yield* ProjectRepository;
8397
- const sessionRepository = yield* SessionRepository;
8398
- const projectMetaService = yield* ProjectMetaService;
8399
- const sessionMetaService = yield* SessionMetaService;
8400
- const virtualConversationDatabase = yield* VirtualConversationDatabase;
8401
- const rateLimitAutoScheduleService = yield* RateLimitAutoScheduleService;
8402
- const listenersRef = yield* Ref14.make({});
8403
- const startInitialization = () => {
8404
- return Effect48.gen(function* () {
8405
- yield* fileWatcher.startWatching();
8406
- yield* rateLimitAutoScheduleService.start();
8407
- const daemon = Effect48.repeat(
8408
- eventBus.emit("heartbeat", {}),
8409
- Schedule2.fixed("10 seconds")
8410
- );
8411
- console.log("start heartbeat");
8412
- yield* Effect48.forkDaemon(daemon);
8413
- console.log("after starting heartbeat fork");
8414
- const onSessionChanged = (event) => {
8415
- Effect48.runFork(
8416
- projectMetaService.invalidateProject(event.projectId)
8417
- );
8418
- Effect48.runFork(
8419
- sessionMetaService.invalidateSession(
8420
- event.projectId,
8421
- event.sessionId
8422
- )
8423
- );
8424
- };
8425
- const onSessionProcessChanged = (event) => {
8426
- if ((event.changed.type === "completed" || event.changed.type === "paused") && event.changed.sessionId !== void 0) {
8427
- Effect48.runFork(
8428
- virtualConversationDatabase.deleteVirtualConversations(
8429
- event.changed.sessionId
8430
- )
8431
- );
8432
- return;
8433
- }
8434
- };
8435
- yield* Ref14.set(listenersRef, {
8436
- sessionChanged: onSessionChanged,
8437
- sessionProcessChanged: onSessionProcessChanged
8438
- });
8439
- yield* eventBus.on("sessionChanged", onSessionChanged);
8440
- yield* eventBus.on("sessionProcessChanged", onSessionProcessChanged);
8441
- yield* Effect48.gen(function* () {
8442
- console.log("Initializing projects cache");
8443
- const { projects } = yield* projectRepository.getProjects();
8444
- console.log(`${projects.length} projects cache initialized`);
8445
- console.log("Initializing sessions cache");
8446
- const results = yield* Effect48.all(
8447
- projects.map(
8448
- (project) => sessionRepository.getSessions(project.id)
8449
- ),
8450
- { concurrency: "unbounded" }
8451
- );
8452
- const totalSessions = results.reduce(
8453
- (s, { sessions }) => s + sessions.length,
8454
- 0
8455
- );
8763
+ // src/server/core/terminal/normalizePtyChunk.ts
8764
+ var normalizePtyChunk = (chunk) => {
8765
+ if (typeof chunk === "string") {
8766
+ return chunk;
8767
+ }
8768
+ if (chunk instanceof Uint8Array) {
8769
+ return Buffer.from(chunk).toString("utf8");
8770
+ }
8771
+ if (chunk instanceof ArrayBuffer) {
8772
+ return Buffer.from(chunk).toString("utf8");
8773
+ }
8774
+ return null;
8775
+ };
8776
+
8777
+ // src/server/core/terminal/rusptyAdapter.ts
8778
+ var createRusptySession = (ruspty, options) => {
8779
+ const pty = new ruspty.Pty(options);
8780
+ return {
8781
+ process: {
8782
+ write: (data) => {
8783
+ pty.write.write(data);
8784
+ },
8785
+ resize: (cols, rows) => {
8786
+ pty.resize({ cols, rows });
8787
+ },
8788
+ kill: () => {
8789
+ pty.close();
8790
+ }
8791
+ },
8792
+ read: pty.read
8793
+ };
8794
+ };
8795
+
8796
+ // src/server/core/terminal/TerminalService.ts
8797
+ var DEFAULT_COLS = 80;
8798
+ var DEFAULT_ROWS = 24;
8799
+ var MAX_BUFFER_BYTES = 1024 * 1024 * 2;
8800
+ var SESSION_TTL_MS = 30 * 60 * 1e3;
8801
+ var MAX_SESSIONS = 10;
8802
+ var selectShell = (shellEnv, fallbackShell, unrestrictedFlag) => {
8803
+ if (process.platform === "win32") {
8804
+ return { command: "powershell.exe", args: ["-NoLogo"] };
8805
+ }
8806
+ const command = shellEnv ?? fallbackShell ?? "bash";
8807
+ const args = [];
8808
+ if (!unrestrictedFlag && command.toLowerCase().includes("bash")) {
8809
+ args.push("--noprofile", "--norc", "--restricted");
8810
+ }
8811
+ return { command, args };
8812
+ };
8813
+ var isFlagEnabled2 = (value) => {
8814
+ if (!value) return false;
8815
+ return value === "1" || value.toLowerCase() === "true";
8816
+ };
8817
+ var LayerImpl30 = Effect48.gen(function* () {
8818
+ const envService = yield* EnvService;
8819
+ const ccvOptionsService = yield* CcvOptionsService;
8820
+ const sessions = /* @__PURE__ */ new Map();
8821
+ const disabledService = (reason) => {
8822
+ const getOrCreateSession2 = (_sessionId, _cwdOverride) => Effect48.fail(new Error(`Terminal support is unavailable (${reason}).`));
8823
+ return {
8824
+ getOrCreateSession: getOrCreateSession2,
8825
+ registerClient: () => Effect48.void,
8826
+ unregisterClient: () => Effect48.void,
8827
+ writeInput: () => Effect48.void,
8828
+ resize: () => Effect48.void,
8829
+ signal: () => Effect48.void,
8830
+ snapshotSince: () => Effect48.succeed(void 0)
8831
+ };
8832
+ };
8833
+ const ruspty = yield* Effect48.tryPromise({
8834
+ try: () => import("@replit/ruspty"),
8835
+ catch: (error) => new Error(`Failed to load @replit/ruspty: ${String(error)}`)
8836
+ }).pipe(
8837
+ Effect48.catchAll(
8838
+ (error) => Effect48.sync(() => {
8839
+ console.warn(error.message);
8840
+ return null;
8841
+ })
8842
+ )
8843
+ );
8844
+ if (!ruspty) {
8845
+ return disabledService("@replit/ruspty failed to load");
8846
+ }
8847
+ const trimBuffer = (session) => {
8848
+ while (session.bufferBytes > MAX_BUFFER_BYTES && session.buffer.length > 0) {
8849
+ const removed = session.buffer.shift();
8850
+ if (removed) {
8851
+ session.bufferBytes -= Buffer.byteLength(removed.data, "utf8");
8852
+ }
8853
+ }
8854
+ };
8855
+ const sendJson2 = (client, payload) => {
8856
+ if (client.readyState !== 1) return;
8857
+ client.send(JSON.stringify(payload));
8858
+ };
8859
+ const broadcast = (session, payload) => {
8860
+ for (const client of session.clients) {
8861
+ sendJson2(client, payload);
8862
+ }
8863
+ };
8864
+ const destroySession = (sessionId) => {
8865
+ const session = sessions.get(sessionId);
8866
+ if (!session) return;
8867
+ sessions.delete(sessionId);
8868
+ session.exited = true;
8869
+ for (const client of session.clients) {
8870
+ if (client.readyState === 1) {
8871
+ client.close(1e3, "Session closed");
8872
+ }
8873
+ }
8874
+ session.clients.clear();
8875
+ session.pty.kill();
8876
+ };
8877
+ const pruneSessions = () => {
8878
+ const now = Date.now();
8879
+ for (const [sessionId, session] of sessions.entries()) {
8880
+ if (session.clients.size > 0) {
8881
+ continue;
8882
+ }
8883
+ if (now - session.lastActivity > SESSION_TTL_MS) {
8884
+ destroySession(sessionId);
8885
+ }
8886
+ }
8887
+ if (sessions.size <= MAX_SESSIONS) return;
8888
+ const sorted = Array.from(sessions.values()).sort(
8889
+ (left, right) => left.lastActivity - right.lastActivity
8890
+ );
8891
+ for (const session of sorted.slice(0, sessions.size - MAX_SESSIONS)) {
8892
+ destroySession(session.id);
8893
+ }
8894
+ };
8895
+ const createSession = (requestedId, options) => {
8896
+ const id = requestedId ?? ulid5();
8897
+ const shell = selectShell(
8898
+ options.shell,
8899
+ options.fallbackShell,
8900
+ options.unrestrictedFlag
8901
+ );
8902
+ const { process: ptyProcess, read } = createRusptySession(ruspty, {
8903
+ command: shell.command,
8904
+ args: shell.args,
8905
+ envs: options.env,
8906
+ dir: options.cwd,
8907
+ size: { cols: DEFAULT_COLS, rows: DEFAULT_ROWS },
8908
+ onExit: (_error, exitCode) => {
8909
+ const current = sessions.get(id);
8910
+ if (!current) return;
8911
+ current.exited = true;
8912
+ current.lastActivity = Date.now();
8913
+ broadcast(current, { type: "exit", code: exitCode });
8914
+ if (current.clients.size === 0) {
8915
+ destroySession(current.id);
8916
+ }
8917
+ }
8918
+ });
8919
+ const session = {
8920
+ id,
8921
+ pty: ptyProcess,
8922
+ seq: 0,
8923
+ buffer: [],
8924
+ bufferBytes: 0,
8925
+ lastActivity: Date.now(),
8926
+ clients: /* @__PURE__ */ new Set(),
8927
+ exited: false,
8928
+ inputBuffer: ""
8929
+ };
8930
+ read.on("data", (chunk) => {
8931
+ const data = normalizePtyChunk(chunk);
8932
+ if (data === null) return;
8933
+ const current = sessions.get(session.id);
8934
+ if (!current || current.exited) return;
8935
+ current.lastActivity = Date.now();
8936
+ current.seq += 1;
8937
+ current.buffer.push({ seq: current.seq, data });
8938
+ current.bufferBytes += Buffer.byteLength(data, "utf8");
8939
+ trimBuffer(current);
8940
+ broadcast(current, {
8941
+ type: "output",
8942
+ seq: current.seq,
8943
+ data
8944
+ });
8945
+ });
8946
+ sessions.set(id, session);
8947
+ return session;
8948
+ };
8949
+ const getSession = (sessionId) => {
8950
+ if (!sessionId) return void 0;
8951
+ return sessions.get(sessionId);
8952
+ };
8953
+ const getOrCreateSession = (sessionId, cwdOverride) => Effect48.gen(function* () {
8954
+ const terminalDisabledEnv = yield* envService.getEnv(
8955
+ "CCV_TERMINAL_DISABLED"
8956
+ );
8957
+ const terminalDisabledOption = yield* ccvOptionsService.getCcvOptions("terminalDisabled");
8958
+ const terminalDisabled = terminalDisabledOption ?? isFlagEnabled2(terminalDisabledEnv);
8959
+ if (terminalDisabled) {
8960
+ return yield* Effect48.fail(
8961
+ new Error(
8962
+ "Terminal support is unavailable (CCV_TERMINAL_DISABLED is enabled)."
8963
+ )
8964
+ );
8965
+ }
8966
+ const cwd = cwdOverride ?? process.cwd();
8967
+ const terminalShellOption = yield* ccvOptionsService.getCcvOptions("terminalShell");
8968
+ const shell = terminalShellOption ?? (yield* envService.getEnv("CCV_TERMINAL_SHELL"));
8969
+ const fallbackShell = yield* envService.getEnv("SHELL");
8970
+ const terminalUnrestrictedOption = yield* ccvOptionsService.getCcvOptions(
8971
+ "terminalUnrestricted"
8972
+ );
8973
+ const terminalUnrestrictedEnv = yield* envService.getEnv(
8974
+ "CCV_TERMINAL_UNRESTRICTED"
8975
+ );
8976
+ const unrestrictedFlag = terminalUnrestrictedOption ?? isFlagEnabled2(terminalUnrestrictedEnv);
8977
+ const env = yield* envService.getAllEnv();
8978
+ const existing = getSession(sessionId);
8979
+ if (existing) {
8980
+ existing.lastActivity = Date.now();
8981
+ return existing;
8982
+ }
8983
+ return createSession(sessionId, {
8984
+ cwd,
8985
+ shell,
8986
+ fallbackShell,
8987
+ unrestrictedFlag,
8988
+ env
8989
+ });
8990
+ });
8991
+ const registerClient = (sessionId, client) => Effect48.sync(() => {
8992
+ const session = getSession(sessionId);
8993
+ if (!session) {
8994
+ return;
8995
+ }
8996
+ session.clients.add(client);
8997
+ session.lastActivity = Date.now();
8998
+ });
8999
+ const unregisterClient = (sessionId, client) => Effect48.sync(() => {
9000
+ const session = getSession(sessionId);
9001
+ if (!session) {
9002
+ return;
9003
+ }
9004
+ session.clients.delete(client);
9005
+ session.lastActivity = Date.now();
9006
+ if (session.exited && session.clients.size === 0) {
9007
+ destroySession(session.id);
9008
+ }
9009
+ });
9010
+ const writeInput = (sessionId, data) => Effect48.sync(() => {
9011
+ const session = getSession(sessionId);
9012
+ if (!session || session.exited) return;
9013
+ session.lastActivity = Date.now();
9014
+ const combined = session.inputBuffer + data;
9015
+ const lines = combined.split(/\r\n|\n|\r/);
9016
+ session.inputBuffer = lines.pop() ?? "";
9017
+ for (const line of lines) {
9018
+ const trimmed = line.trim();
9019
+ if (trimmed.length > 0) {
9020
+ console.log(`[terminal] session ${session.id} command: ${trimmed}`);
9021
+ }
9022
+ }
9023
+ session.pty.write(data);
9024
+ });
9025
+ const resize = (sessionId, cols, rows) => Effect48.sync(() => {
9026
+ const session = getSession(sessionId);
9027
+ if (!session || session.exited) return;
9028
+ session.lastActivity = Date.now();
9029
+ session.pty.resize(cols, rows);
9030
+ });
9031
+ const signal = (sessionId, name) => Effect48.sync(() => {
9032
+ const session = getSession(sessionId);
9033
+ if (!session || session.exited) return;
9034
+ session.lastActivity = Date.now();
9035
+ if (name === "SIGINT") {
9036
+ session.pty.write("");
9037
+ }
9038
+ });
9039
+ const snapshotSince = (sessionId, lastSeq) => Effect48.sync(() => {
9040
+ const session = getSession(sessionId);
9041
+ if (!session) return void 0;
9042
+ const chunks = session.buffer.filter((chunk) => chunk.seq > lastSeq);
9043
+ if (chunks.length === 0) return void 0;
9044
+ const data = chunks.map((chunk) => chunk.data).join("");
9045
+ return { seq: session.seq, data };
9046
+ });
9047
+ const cleanupLoop = Effect48.forever(
9048
+ Effect48.gen(function* () {
9049
+ yield* Effect48.sleep("1 minute");
9050
+ pruneSessions();
9051
+ })
9052
+ );
9053
+ yield* Effect48.forkScoped(cleanupLoop);
9054
+ return {
9055
+ getOrCreateSession,
9056
+ registerClient,
9057
+ unregisterClient,
9058
+ writeInput,
9059
+ resize,
9060
+ signal,
9061
+ snapshotSince
9062
+ };
9063
+ });
9064
+ var TerminalService = class extends Context38.Tag("TerminalService")() {
9065
+ static {
9066
+ this.Live = Layer40.effect(this, LayerImpl30);
9067
+ }
9068
+ };
9069
+
9070
+ // src/server/hono/app.ts
9071
+ import { Hono } from "hono";
9072
+ var honoApp = new Hono();
9073
+
9074
+ // src/server/hono/initialize.ts
9075
+ import { Context as Context39, Effect as Effect49, Layer as Layer41, Ref as Ref14, Schedule as Schedule2 } from "effect";
9076
+ var InitializeService = class extends Context39.Tag("InitializeService")() {
9077
+ static {
9078
+ this.Live = Layer41.effect(
9079
+ this,
9080
+ Effect49.gen(function* () {
9081
+ const eventBus = yield* EventBus;
9082
+ const fileWatcher = yield* FileWatcherService;
9083
+ const projectRepository = yield* ProjectRepository;
9084
+ const sessionRepository = yield* SessionRepository;
9085
+ const projectMetaService = yield* ProjectMetaService;
9086
+ const sessionMetaService = yield* SessionMetaService;
9087
+ const virtualConversationDatabase = yield* VirtualConversationDatabase;
9088
+ const rateLimitAutoScheduleService = yield* RateLimitAutoScheduleService;
9089
+ const listenersRef = yield* Ref14.make({});
9090
+ const startInitialization = () => {
9091
+ return Effect49.gen(function* () {
9092
+ yield* fileWatcher.startWatching();
9093
+ yield* rateLimitAutoScheduleService.start();
9094
+ const daemon = Effect49.repeat(
9095
+ eventBus.emit("heartbeat", {}),
9096
+ Schedule2.fixed("10 seconds")
9097
+ );
9098
+ console.log("start heartbeat");
9099
+ yield* Effect49.forkDaemon(daemon);
9100
+ console.log("after starting heartbeat fork");
9101
+ const onSessionChanged = (event) => {
9102
+ Effect49.runFork(
9103
+ projectMetaService.invalidateProject(event.projectId)
9104
+ );
9105
+ Effect49.runFork(
9106
+ sessionMetaService.invalidateSession(
9107
+ event.projectId,
9108
+ event.sessionId
9109
+ )
9110
+ );
9111
+ };
9112
+ const onSessionProcessChanged = (event) => {
9113
+ if ((event.changed.type === "completed" || event.changed.type === "paused") && event.changed.sessionId !== void 0) {
9114
+ Effect49.runFork(
9115
+ virtualConversationDatabase.deleteVirtualConversations(
9116
+ event.changed.sessionId
9117
+ )
9118
+ );
9119
+ return;
9120
+ }
9121
+ };
9122
+ yield* Ref14.set(listenersRef, {
9123
+ sessionChanged: onSessionChanged,
9124
+ sessionProcessChanged: onSessionProcessChanged
9125
+ });
9126
+ yield* eventBus.on("sessionChanged", onSessionChanged);
9127
+ yield* eventBus.on("sessionProcessChanged", onSessionProcessChanged);
9128
+ yield* Effect49.gen(function* () {
9129
+ console.log("Initializing projects cache");
9130
+ const { projects } = yield* projectRepository.getProjects();
9131
+ console.log(`${projects.length} projects cache initialized`);
9132
+ console.log("Initializing sessions cache");
9133
+ const results = yield* Effect49.all(
9134
+ projects.map(
9135
+ (project) => sessionRepository.getSessions(project.id)
9136
+ ),
9137
+ { concurrency: "unbounded" }
9138
+ );
9139
+ const totalSessions = results.reduce(
9140
+ (s, { sessions }) => s + sessions.length,
9141
+ 0
9142
+ );
8456
9143
  console.log(`${totalSessions} sessions cache initialized`);
8457
9144
  }).pipe(
8458
- Effect48.catchAll(() => Effect48.void),
8459
- Effect48.withSpan("initialize-cache")
9145
+ Effect49.catchAll(() => Effect49.void),
9146
+ Effect49.withSpan("initialize-cache")
8460
9147
  );
8461
- }).pipe(Effect48.withSpan("start-initialization"));
9148
+ }).pipe(Effect49.withSpan("start-initialization"));
8462
9149
  };
8463
- const stopCleanup = () => Effect48.gen(function* () {
9150
+ const stopCleanup = () => Effect49.gen(function* () {
8464
9151
  const listeners = yield* Ref14.get(listenersRef);
8465
9152
  if (listeners.sessionChanged) {
8466
9153
  yield* eventBus.off("sessionChanged", listeners.sessionChanged);
@@ -8485,221 +9172,101 @@ var InitializeService = class extends Context38.Tag("InitializeService")() {
8485
9172
  };
8486
9173
 
8487
9174
  // src/server/hono/middleware/auth.middleware.ts
8488
- import { Context as Context39, Effect as Effect49, Layer as Layer41 } from "effect";
9175
+ import { Context as Context40, Effect as Effect50, Layer as Layer42, Runtime as Runtime3 } from "effect";
8489
9176
  import { getCookie } from "hono/cookie";
8490
9177
  import { createMiddleware } from "hono/factory";
8491
9178
  var generateSessionToken = (password) => {
8492
9179
  if (!password) return "";
8493
9180
  return Buffer.from(`ccv-session:${password}`).toString("base64");
8494
9181
  };
8495
- var PUBLIC_API_ROUTES = [
8496
- "/api/auth/login",
8497
- "/api/auth/check",
8498
- "/api/auth/logout",
8499
- "/api/config",
8500
- // Allow config access for theme/locale loading
8501
- "/api/version"
8502
- ];
8503
- var LayerImpl30 = Effect49.gen(function* () {
9182
+ var getBearerToken = (authorization) => {
9183
+ if (!authorization) return void 0;
9184
+ const [scheme, token] = authorization.split(" ");
9185
+ if (!scheme || !token) return void 0;
9186
+ if (scheme.toLowerCase() !== "bearer") return void 0;
9187
+ const trimmedToken = token.trim();
9188
+ return trimmedToken.length > 0 ? trimmedToken : void 0;
9189
+ };
9190
+ var createAuthRequiredMiddleware = (authEnabled, validSessionToken, authPassword) => {
9191
+ return createMiddleware(async (c, next) => {
9192
+ if (!c.req.path.startsWith("/api")) {
9193
+ return next();
9194
+ }
9195
+ if (!authEnabled) {
9196
+ return next();
9197
+ }
9198
+ const sessionToken = getCookie(c, "ccv-session");
9199
+ const bearerToken = getBearerToken(c.req.header("Authorization"));
9200
+ const cookieAuthorized = sessionToken === validSessionToken;
9201
+ const bearerAuthorized = authPassword !== void 0 && bearerToken === authPassword;
9202
+ if (!cookieAuthorized && !bearerAuthorized) {
9203
+ return c.json({ error: "Unauthorized" }, 401);
9204
+ }
9205
+ await next();
9206
+ });
9207
+ };
9208
+ var LayerImpl31 = Effect50.gen(function* () {
8504
9209
  const ccvOptionsService = yield* CcvOptionsService;
8505
- return Effect49.gen(function* () {
8506
- const anthPassword = yield* ccvOptionsService.getCcvOptions("password");
8507
- const authEnabled = anthPassword !== void 0;
8508
- const validSessionToken = generateSessionToken(anthPassword);
8509
- const authMiddleware = createMiddleware(async (c, next) => {
8510
- if (PUBLIC_API_ROUTES.includes(c.req.path)) {
8511
- return next();
8512
- }
8513
- if (!c.req.path.startsWith("/api")) {
8514
- return next();
8515
- }
8516
- if (!authEnabled) {
8517
- return next();
8518
- }
8519
- const sessionToken = getCookie(c, "ccv-session");
8520
- if (!sessionToken || sessionToken !== validSessionToken) {
8521
- return c.json({ error: "Unauthorized" }, 401);
8522
- }
8523
- await next();
9210
+ const runtime = yield* Effect50.runtime();
9211
+ const runPromise = Runtime3.runPromise(runtime);
9212
+ return yield* Effect50.gen(function* () {
9213
+ const getAuthState = Effect50.gen(function* () {
9214
+ const authPassword = yield* ccvOptionsService.getCcvOptions("password");
9215
+ const authEnabled = authPassword !== void 0;
9216
+ const validSessionToken = generateSessionToken(authPassword);
9217
+ return { authEnabled, authPassword, validSessionToken };
8524
9218
  });
9219
+ const authRequiredMiddleware = createMiddleware(
9220
+ async (c, next) => {
9221
+ if (!c.req.path.startsWith("/api")) {
9222
+ return next();
9223
+ }
9224
+ const { authEnabled, validSessionToken, authPassword } = await runPromise(getAuthState);
9225
+ return createAuthRequiredMiddleware(
9226
+ authEnabled,
9227
+ validSessionToken,
9228
+ authPassword
9229
+ )(c, next);
9230
+ }
9231
+ );
8525
9232
  return {
8526
- authEnabled,
8527
- anthPassword,
8528
- validSessionToken,
8529
- authMiddleware
9233
+ getAuthState,
9234
+ authRequiredMiddleware
8530
9235
  };
8531
9236
  });
8532
9237
  });
8533
- var AuthMiddleware = class extends Context39.Tag("AuthMiddleware")() {
9238
+ var AuthMiddleware = class extends Context40.Tag("AuthMiddleware")() {
8534
9239
  static {
8535
- this.Live = Layer41.effect(this, LayerImpl30);
9240
+ this.Live = Layer42.effect(this, LayerImpl31);
8536
9241
  }
8537
9242
  };
8538
9243
 
8539
- // src/server/hono/route.ts
8540
- import { zValidator } from "@hono/zod-validator";
8541
- import { Effect as Effect51, Runtime as Runtime3 } from "effect";
8542
- import { deleteCookie, getCookie as getCookie3, setCookie as setCookie2 } from "hono/cookie";
8543
- import { streamSSE } from "hono/streaming";
9244
+ // src/server/hono/routes/index.ts
9245
+ import { zValidator as zValidator8 } from "@hono/zod-validator";
9246
+ import { Effect as Effect62, Runtime as Runtime5 } from "effect";
9247
+ import { setCookie as setCookie3 } from "hono/cookie";
8544
9248
  import prexit from "prexit";
8545
- import { z as z31 } from "zod";
8546
-
8547
- // src/server/core/claude-code/schema.ts
8548
- import { z as z27 } from "zod";
8549
- var mediaTypeSchema = z27.enum([
8550
- "image/png",
8551
- "image/jpeg",
8552
- "image/gif",
8553
- "image/webp"
8554
- ]);
8555
- var imageBlockSchema = z27.object({
8556
- type: z27.literal("image"),
8557
- source: z27.object({
8558
- type: z27.literal("base64"),
8559
- media_type: mediaTypeSchema,
8560
- data: z27.string()
8561
- })
8562
- });
8563
- var documentBlockSchema = z27.object({
8564
- type: z27.literal("document"),
8565
- source: z27.union([
8566
- z27.object({
8567
- type: z27.literal("text"),
8568
- media_type: z27.enum(["text/plain"]),
8569
- data: z27.string()
8570
- }),
8571
- z27.object({
8572
- type: z27.literal("base64"),
8573
- media_type: z27.enum(["application/pdf"]),
8574
- data: z27.string()
8575
- })
8576
- ])
8577
- });
8578
- var userMessageInputSchema = z27.object({
8579
- text: z27.string().min(1),
8580
- images: z27.array(imageBlockSchema).optional(),
8581
- documents: z27.array(documentBlockSchema).optional()
8582
- });
8583
-
8584
- // src/server/core/git/schema.ts
8585
- import { z as z28 } from "zod";
8586
- var CommitRequestSchema = z28.object({
8587
- projectId: z28.string().min(1),
8588
- files: z28.array(z28.string().min(1)).min(1),
8589
- message: z28.string().trim().min(1)
8590
- });
8591
- var PushRequestSchema = z28.object({
8592
- projectId: z28.string().min(1)
8593
- });
8594
- var CommitResultSuccessSchema = z28.object({
8595
- success: z28.literal(true),
8596
- commitSha: z28.string().length(40),
8597
- filesCommitted: z28.number().int().positive(),
8598
- message: z28.string()
8599
- });
8600
- var CommitResultErrorSchema = z28.object({
8601
- success: z28.literal(false),
8602
- error: z28.string(),
8603
- errorCode: z28.enum([
8604
- "EMPTY_MESSAGE",
8605
- "NO_FILES",
8606
- "PROJECT_NOT_FOUND",
8607
- "NOT_A_REPOSITORY",
8608
- "HOOK_FAILED",
8609
- "GIT_COMMAND_ERROR"
8610
- ]),
8611
- details: z28.string().optional()
8612
- });
8613
- var CommitResultSchema = z28.discriminatedUnion("success", [
8614
- CommitResultSuccessSchema,
8615
- CommitResultErrorSchema
8616
- ]);
8617
- var PushResultSuccessSchema = z28.object({
8618
- success: z28.literal(true),
8619
- remote: z28.string(),
8620
- branch: z28.string(),
8621
- objectsPushed: z28.number().int().optional()
8622
- });
8623
- var PushResultErrorSchema = z28.object({
8624
- success: z28.literal(false),
8625
- error: z28.string(),
8626
- errorCode: z28.enum([
8627
- "PROJECT_NOT_FOUND",
8628
- "NOT_A_REPOSITORY",
8629
- "NO_UPSTREAM",
8630
- "NON_FAST_FORWARD",
8631
- "AUTH_FAILED",
8632
- "NETWORK_ERROR",
8633
- "TIMEOUT",
8634
- "GIT_COMMAND_ERROR"
8635
- ]),
8636
- details: z28.string().optional()
8637
- });
8638
- var PushResultSchema = z28.discriminatedUnion("success", [
8639
- PushResultSuccessSchema,
8640
- PushResultErrorSchema
8641
- ]);
8642
- var CommitAndPushResultSuccessSchema = z28.object({
8643
- success: z28.literal(true),
8644
- commitSha: z28.string().length(40),
8645
- filesCommitted: z28.number().int().positive(),
8646
- message: z28.string(),
8647
- remote: z28.string(),
8648
- branch: z28.string()
8649
- });
8650
- var CommitAndPushResultErrorSchema = z28.object({
8651
- success: z28.literal(false),
8652
- commitSucceeded: z28.boolean(),
8653
- commitSha: z28.string().length(40).optional(),
8654
- error: z28.string(),
8655
- errorCode: z28.enum([
8656
- "EMPTY_MESSAGE",
8657
- "NO_FILES",
8658
- "PROJECT_NOT_FOUND",
8659
- "NOT_A_REPOSITORY",
8660
- "HOOK_FAILED",
8661
- "GIT_COMMAND_ERROR",
8662
- "NO_UPSTREAM",
8663
- "NON_FAST_FORWARD",
8664
- "AUTH_FAILED",
8665
- "NETWORK_ERROR",
8666
- "TIMEOUT"
8667
- ]),
8668
- details: z28.string().optional()
8669
- });
8670
- var CommitAndPushResultSchema = z28.discriminatedUnion("success", [
8671
- CommitAndPushResultSuccessSchema,
8672
- CommitAndPushResultErrorSchema
8673
- ]);
8674
9249
 
8675
9250
  // src/server/lib/config/config.ts
8676
- import z30 from "zod";
9251
+ import z28 from "zod";
8677
9252
 
8678
9253
  // src/lib/i18n/schema.ts
8679
- import z29 from "zod";
8680
- var localeSchema = z29.enum(["ja", "en", "zh_CN"]);
9254
+ import z27 from "zod";
9255
+ var localeSchema = z27.enum(["ja", "en", "zh_CN"]);
8681
9256
 
8682
9257
  // src/server/lib/config/config.ts
8683
- var userConfigSchema = z30.object({
8684
- hideNoUserMessageSession: z30.boolean().optional().default(true),
8685
- unifySameTitleSession: z30.boolean().optional().default(false),
8686
- enterKeyBehavior: z30.enum(["shift-enter-send", "enter-send", "command-enter-send"]).optional().default("shift-enter-send"),
8687
- permissionMode: z30.enum(["acceptEdits", "bypassPermissions", "default", "plan"]).optional().default("default"),
9258
+ var userConfigSchema = z28.object({
9259
+ hideNoUserMessageSession: z28.boolean().optional().default(true),
9260
+ unifySameTitleSession: z28.boolean().optional().default(false),
9261
+ enterKeyBehavior: z28.enum(["shift-enter-send", "enter-send", "command-enter-send"]).optional().default("shift-enter-send"),
9262
+ permissionMode: z28.enum(["acceptEdits", "bypassPermissions", "default", "plan"]).optional().default("default"),
8688
9263
  locale: localeSchema.optional().default("en"),
8689
- theme: z30.enum(["light", "dark", "system"]).optional().default("system"),
8690
- searchHotkey: z30.enum(["ctrl-k", "command-k"]).optional().default("command-k"),
8691
- autoScheduleContinueOnRateLimit: z30.boolean().optional().default(false)
9264
+ theme: z28.enum(["light", "dark", "system"]).optional().default("system"),
9265
+ searchHotkey: z28.enum(["ctrl-k", "command-k"]).optional().default("command-k"),
9266
+ autoScheduleContinueOnRateLimit: z28.boolean().optional().default(false)
8692
9267
  });
8693
9268
  var defaultUserConfig = userConfigSchema.parse({});
8694
9269
 
8695
- // src/server/lib/effect/toEffectResponse.ts
8696
- import { Effect as Effect50 } from "effect";
8697
- var effectToResponse = async (ctx, effect) => {
8698
- const result = await Effect50.runPromise(effect);
8699
- const result2 = ctx.json(result.response, result.status);
8700
- return result2;
8701
- };
8702
-
8703
9270
  // src/server/hono/middleware/config.middleware.ts
8704
9271
  import { getCookie as getCookie2, setCookie } from "hono/cookie";
8705
9272
  import { createMiddleware as createMiddleware2 } from "hono/factory";
@@ -8737,46 +9304,22 @@ var configMiddleware = createMiddleware2(
8737
9304
  }
8738
9305
  );
8739
9306
 
8740
- // src/server/hono/route.ts
8741
- var routes = (app, options) => Effect51.gen(function* () {
8742
- const ccvOptionsService = yield* CcvOptionsService;
8743
- yield* ccvOptionsService.loadCliOptions(options);
8744
- const envService = yield* EnvService;
8745
- const userConfigService = yield* UserConfigService;
8746
- const claudeCodeLifeCycleService = yield* ClaudeCodeLifeCycleService;
8747
- const initializeService = yield* InitializeService;
8748
- const projectController = yield* ProjectController;
8749
- const sessionController = yield* SessionController;
8750
- const agentSessionController = yield* AgentSessionController;
8751
- const gitController = yield* GitController;
8752
- const claudeCodeSessionProcessController = yield* ClaudeCodeSessionProcessController;
8753
- const claudeCodePermissionController = yield* ClaudeCodePermissionController;
8754
- const sseController = yield* SSEController;
8755
- const fileSystemController = yield* FileSystemController;
8756
- const claudeCodeController = yield* ClaudeCodeController;
8757
- const schedulerController = yield* SchedulerController;
8758
- const featureFlagController = yield* FeatureFlagController;
8759
- const searchController = yield* SearchController;
8760
- const tasksController = yield* TasksController;
8761
- const authMiddlewareService = yield* AuthMiddleware;
8762
- const { authMiddleware, validSessionToken, authEnabled, anthPassword } = yield* authMiddlewareService;
8763
- const runtime = yield* Effect51.runtime();
8764
- if ((yield* envService.getEnv("NEXT_PHASE")) !== "phase-production-build") {
8765
- yield* initializeService.startInitialization();
8766
- prexit(async () => {
8767
- await Runtime3.runPromise(runtime)(initializeService.stopCleanup());
8768
- });
8769
- }
8770
- return app.use(configMiddleware).use(authMiddleware).use(async (c, next) => {
8771
- await Effect51.runPromise(
8772
- userConfigService.setUserConfig({
8773
- ...c.get("userConfig")
8774
- })
8775
- );
8776
- await next();
8777
- }).post(
8778
- "/api/auth/login",
8779
- zValidator("json", z31.object({ password: z31.string() })),
9307
+ // src/server/hono/runtime.ts
9308
+ import { Effect as Effect51 } from "effect";
9309
+ var getHonoRuntime = Effect51.runtime();
9310
+
9311
+ // src/server/hono/routes/authRoutes.ts
9312
+ import { zValidator } from "@hono/zod-validator";
9313
+ import { Effect as Effect52 } from "effect";
9314
+ import { Hono as Hono2 } from "hono";
9315
+ import { deleteCookie, getCookie as getCookie3, setCookie as setCookie2 } from "hono/cookie";
9316
+ import { z as z29 } from "zod";
9317
+ var authRoutes = Effect52.gen(function* () {
9318
+ const { getAuthState } = yield* AuthMiddleware;
9319
+ const { validSessionToken, authEnabled, authPassword } = yield* getAuthState;
9320
+ return new Hono2().post(
9321
+ "/login",
9322
+ zValidator("json", z29.object({ password: z29.string() })),
8780
9323
  async (c) => {
8781
9324
  const { password } = c.req.valid("json");
8782
9325
  if (!authEnabled) {
@@ -8787,7 +9330,7 @@ var routes = (app, options) => Effect51.gen(function* () {
8787
9330
  500
8788
9331
  );
8789
9332
  }
8790
- if (password !== anthPassword) {
9333
+ if (password !== authPassword) {
8791
9334
  return c.json({ error: "Invalid password" }, 401);
8792
9335
  }
8793
9336
  setCookie2(c, "ccv-session", validSessionToken, {
@@ -8801,459 +9344,785 @@ var routes = (app, options) => Effect51.gen(function* () {
8801
9344
  });
8802
9345
  return c.json({ success: true });
8803
9346
  }
8804
- ).post("/api/auth/logout", async (c) => {
9347
+ ).post("/logout", async (c) => {
8805
9348
  deleteCookie(c, "ccv-session", { path: "/" });
8806
9349
  return c.json({ success: true });
8807
- }).get("/api/auth/check", async (c) => {
9350
+ }).get("/check", async (c) => {
8808
9351
  const sessionToken = getCookie3(c, "ccv-session");
8809
9352
  const isAuthenticated = authEnabled ? sessionToken === validSessionToken : true;
8810
9353
  return c.json({ authenticated: isAuthenticated, authEnabled });
8811
- }).get("/api/config", async (c) => {
8812
- return c.json({
8813
- config: c.get("userConfig")
8814
- });
8815
- }).put("/api/config", zValidator("json", userConfigSchema), async (c) => {
8816
- const { ...config } = c.req.valid("json");
8817
- setCookie2(c, "ccv-config", JSON.stringify(config));
8818
- return c.json({
8819
- config
8820
- });
8821
- }).get("/api/version", async (c) => {
8822
- return c.json({
8823
- version: package_default.version
8824
- });
8825
- }).get("/api/projects", async (c) => {
9354
+ });
9355
+ });
9356
+
9357
+ // src/server/hono/routes/claudeCodeRoutes.ts
9358
+ import { zValidator as zValidator2 } from "@hono/zod-validator";
9359
+ import { Effect as Effect54 } from "effect";
9360
+ import { Hono as Hono3 } from "hono";
9361
+ import { z as z31 } from "zod";
9362
+
9363
+ // src/server/core/claude-code/schema.ts
9364
+ import { z as z30 } from "zod";
9365
+ var mediaTypeSchema = z30.enum([
9366
+ "image/png",
9367
+ "image/jpeg",
9368
+ "image/gif",
9369
+ "image/webp"
9370
+ ]);
9371
+ var imageBlockSchema = z30.object({
9372
+ type: z30.literal("image"),
9373
+ source: z30.object({
9374
+ type: z30.literal("base64"),
9375
+ media_type: mediaTypeSchema,
9376
+ data: z30.string()
9377
+ })
9378
+ });
9379
+ var documentBlockSchema = z30.object({
9380
+ type: z30.literal("document"),
9381
+ source: z30.union([
9382
+ z30.object({
9383
+ type: z30.literal("text"),
9384
+ media_type: z30.enum(["text/plain"]),
9385
+ data: z30.string()
9386
+ }),
9387
+ z30.object({
9388
+ type: z30.literal("base64"),
9389
+ media_type: z30.enum(["application/pdf"]),
9390
+ data: z30.string()
9391
+ })
9392
+ ])
9393
+ });
9394
+ var userMessageInputSchema = z30.object({
9395
+ text: z30.string().min(1),
9396
+ images: z30.array(imageBlockSchema).optional(),
9397
+ documents: z30.array(documentBlockSchema).optional()
9398
+ });
9399
+ var sandboxNetworkConfigSchema = z30.object({
9400
+ allowedDomains: z30.array(z30.string()).optional(),
9401
+ allowUnixSockets: z30.array(z30.string()).optional(),
9402
+ allowAllUnixSockets: z30.boolean().optional(),
9403
+ allowLocalBinding: z30.boolean().optional(),
9404
+ httpProxyPort: z30.number().optional(),
9405
+ socksProxyPort: z30.number().optional()
9406
+ });
9407
+ var sandboxSettingsSchema = z30.object({
9408
+ enabled: z30.boolean().optional(),
9409
+ autoAllowBashIfSandboxed: z30.boolean().optional(),
9410
+ allowUnsandboxedCommands: z30.boolean().optional(),
9411
+ network: sandboxNetworkConfigSchema.optional()
9412
+ });
9413
+ var ccOptionsSchema = z30.object({
9414
+ disallowedTools: z30.array(z30.string()).optional(),
9415
+ settingSources: z30.array(z30.enum(["user", "project", "local"])).optional(),
9416
+ systemPrompt: z30.union([
9417
+ z30.string(),
9418
+ z30.object({
9419
+ type: z30.literal("preset"),
9420
+ preset: z30.literal("claude_code"),
9421
+ append: z30.string().optional()
9422
+ })
9423
+ ]).optional(),
9424
+ model: z30.string().optional(),
9425
+ sandbox: sandboxSettingsSchema.optional(),
9426
+ maxTurns: z30.number().optional(),
9427
+ maxThinkingTokens: z30.number().optional(),
9428
+ env: z30.record(z30.string(), z30.string().optional()).optional(),
9429
+ maxBudgetUsd: z30.number().optional()
9430
+ });
9431
+
9432
+ // src/server/lib/effect/toEffectResponse.ts
9433
+ import { Effect as Effect53 } from "effect";
9434
+ var effectToResponse = async (ctx, effect) => {
9435
+ const result = await Effect53.runPromise(effect);
9436
+ const result2 = ctx.json(result.response, result.status);
9437
+ return result2;
9438
+ };
9439
+
9440
+ // src/server/hono/routes/claudeCodeRoutes.ts
9441
+ var normalizeUserMessageInput = (input) => {
9442
+ const images = input.images?.map((image) => ({
9443
+ type: image.type,
9444
+ source: image.source
9445
+ }));
9446
+ const documents = input.documents?.map((document) => {
9447
+ if (!document.source) {
9448
+ throw new Error("Document source is required");
9449
+ }
9450
+ return {
9451
+ type: document.type,
9452
+ source: document.source
9453
+ };
9454
+ });
9455
+ return {
9456
+ text: input.text,
9457
+ images,
9458
+ documents
9459
+ };
9460
+ };
9461
+ var claudeCodeRoutes = Effect54.gen(function* () {
9462
+ const claudeCodeController = yield* ClaudeCodeController;
9463
+ const claudeCodeSessionProcessController = yield* ClaudeCodeSessionProcessController;
9464
+ const claudeCodePermissionController = yield* ClaudeCodePermissionController;
9465
+ const claudeCodeLifeCycleService = yield* ClaudeCodeLifeCycleService;
9466
+ const runtime = yield* getHonoRuntime;
9467
+ return new Hono3().get("/meta", async (c) => {
8826
9468
  const response = await effectToResponse(
8827
9469
  c,
8828
- projectController.getProjects()
9470
+ claudeCodeController.getClaudeCodeMeta().pipe(Effect54.provide(runtime))
8829
9471
  );
8830
9472
  return response;
8831
- }).get(
8832
- "/api/projects/:projectId",
8833
- zValidator("query", z31.object({ cursor: z31.string().optional() })),
9473
+ }).get("/features", async (c) => {
9474
+ const response = await effectToResponse(
9475
+ c,
9476
+ claudeCodeController.getAvailableFeatures().pipe(Effect54.provide(runtime))
9477
+ );
9478
+ return response;
9479
+ }).get("/session-processes", async (c) => {
9480
+ const response = await effectToResponse(
9481
+ c,
9482
+ claudeCodeSessionProcessController.getSessionProcesses()
9483
+ );
9484
+ return response;
9485
+ }).post(
9486
+ "/session-processes",
9487
+ zValidator2(
9488
+ "json",
9489
+ z31.object({
9490
+ projectId: z31.string(),
9491
+ input: userMessageInputSchema,
9492
+ baseSession: z31.union([
9493
+ z31.undefined(),
9494
+ z31.object({
9495
+ type: z31.literal("fork"),
9496
+ sessionId: z31.string()
9497
+ }),
9498
+ z31.object({
9499
+ type: z31.literal("resume"),
9500
+ sessionId: z31.string()
9501
+ })
9502
+ ]),
9503
+ ccOptions: ccOptionsSchema.optional()
9504
+ })
9505
+ ),
8834
9506
  async (c) => {
9507
+ const body = c.req.valid("json");
9508
+ const input = normalizeUserMessageInput(body.input);
9509
+ const { baseSession, ...rest } = body;
8835
9510
  const response = await effectToResponse(
8836
9511
  c,
8837
- projectController.getProject({
8838
- ...c.req.param(),
8839
- ...c.req.valid("query")
8840
- }).pipe(Effect51.provide(runtime))
9512
+ claudeCodeSessionProcessController.createSessionProcess({
9513
+ ...rest,
9514
+ input,
9515
+ baseSession: baseSession ?? void 0
9516
+ })
8841
9517
  );
8842
9518
  return response;
8843
9519
  }
8844
9520
  ).post(
8845
- "/api/projects",
8846
- zValidator(
9521
+ "/session-processes/:sessionProcessId/continue",
9522
+ zValidator2(
8847
9523
  "json",
8848
9524
  z31.object({
8849
- projectPath: z31.string().min(1, "Project path is required")
9525
+ projectId: z31.string(),
9526
+ input: userMessageInputSchema,
9527
+ baseSessionId: z31.string()
8850
9528
  })
8851
9529
  ),
8852
9530
  async (c) => {
9531
+ const body = c.req.valid("json");
9532
+ const input = normalizeUserMessageInput(body.input);
8853
9533
  const response = await effectToResponse(
8854
9534
  c,
8855
- projectController.createProject({
8856
- ...c.req.valid("json")
8857
- }).pipe(Effect51.provide(runtime))
9535
+ claudeCodeSessionProcessController.continueSessionProcess({
9536
+ ...c.req.param(),
9537
+ ...body,
9538
+ input
9539
+ }).pipe(Effect54.provide(runtime))
8858
9540
  );
8859
9541
  return response;
8860
9542
  }
8861
- ).get("/api/projects/:projectId/latest-session", async (c) => {
8862
- const response = await effectToResponse(
8863
- c,
8864
- projectController.getProjectLatestSession({
8865
- ...c.req.param()
8866
- }).pipe(Effect51.provide(runtime))
8867
- );
8868
- return response;
8869
- }).get("/api/projects/:projectId/sessions/:sessionId", async (c) => {
8870
- const response = await effectToResponse(
8871
- c,
8872
- sessionController.getSession({ ...c.req.param() }).pipe(Effect51.provide(runtime))
8873
- );
8874
- return response;
8875
- }).get(
8876
- "/api/projects/:projectId/sessions/:sessionId/export",
9543
+ ).post(
9544
+ "/session-processes/:sessionProcessId/abort",
9545
+ zValidator2("json", z31.object({ projectId: z31.string() })),
8877
9546
  async (c) => {
8878
- const response = await effectToResponse(
8879
- c,
8880
- sessionController.exportSessionHtml({ ...c.req.param() }).pipe(Effect51.provide(runtime))
9547
+ const { sessionProcessId } = c.req.param();
9548
+ void Effect54.runFork(
9549
+ claudeCodeLifeCycleService.abortTask(sessionProcessId)
8881
9550
  );
8882
- return response;
9551
+ return c.json({ message: "Task aborted" });
8883
9552
  }
8884
- ).delete("/api/projects/:projectId/sessions/:sessionId", async (c) => {
8885
- const response = await effectToResponse(
8886
- c,
8887
- sessionController.deleteSession({ ...c.req.param() }).pipe(Effect51.provide(runtime))
8888
- );
8889
- return response;
8890
- }).get(
8891
- "/api/projects/:projectId/agent-sessions/:agentId",
8892
- zValidator("query", z31.object({ sessionId: z31.string().optional() })),
9553
+ ).post(
9554
+ "/permission-response",
9555
+ zValidator2(
9556
+ "json",
9557
+ z31.object({
9558
+ permissionRequestId: z31.string(),
9559
+ decision: z31.enum(["allow", "deny"])
9560
+ })
9561
+ ),
8893
9562
  async (c) => {
8894
- const { projectId, agentId } = c.req.param();
8895
- const { sessionId } = c.req.valid("query");
8896
9563
  const response = await effectToResponse(
8897
9564
  c,
8898
- agentSessionController.getAgentSession({
8899
- projectId,
8900
- agentId,
8901
- sessionId
8902
- }).pipe(Effect51.provide(runtime))
9565
+ claudeCodePermissionController.permissionResponse({
9566
+ permissionResponse: c.req.valid("json")
9567
+ })
8903
9568
  );
8904
9569
  return response;
8905
9570
  }
8906
- ).get("/api/projects/:projectId/git/current-revisions", async (c) => {
9571
+ );
9572
+ });
9573
+
9574
+ // src/server/hono/routes/featureFlagRoutes.ts
9575
+ import { Effect as Effect55 } from "effect";
9576
+ import { Hono as Hono4 } from "hono";
9577
+ var featureFlagRoutes = Effect55.gen(function* () {
9578
+ const featureFlagController = yield* FeatureFlagController;
9579
+ const runtime = yield* getHonoRuntime;
9580
+ return new Hono4().get("/", async (c) => {
8907
9581
  const response = await effectToResponse(
8908
9582
  c,
8909
- gitController.getCurrentRevisions({
8910
- ...c.req.param()
8911
- }).pipe(Effect51.provide(runtime))
9583
+ featureFlagController.getFlags().pipe(Effect55.provide(runtime))
8912
9584
  );
8913
9585
  return response;
8914
- }).post(
8915
- "/api/projects/:projectId/git/diff",
8916
- zValidator(
8917
- "json",
8918
- z31.object({
8919
- fromRef: z31.string().min(1, "fromRef is required"),
8920
- toRef: z31.string().min(1, "toRef is required")
9586
+ });
9587
+ });
9588
+
9589
+ // src/server/hono/routes/fileSystemRoutes.ts
9590
+ import { zValidator as zValidator3 } from "@hono/zod-validator";
9591
+ import { Effect as Effect56 } from "effect";
9592
+ import { Hono as Hono5 } from "hono";
9593
+ import { z as z32 } from "zod";
9594
+ var fileSystemRoutes = Effect56.gen(function* () {
9595
+ const fileSystemController = yield* FileSystemController;
9596
+ return new Hono5().get(
9597
+ "/file-completion",
9598
+ zValidator3(
9599
+ "query",
9600
+ z32.object({
9601
+ projectId: z32.string(),
9602
+ basePath: z32.string().optional().default("/api/")
8921
9603
  })
8922
9604
  ),
8923
9605
  async (c) => {
8924
9606
  const response = await effectToResponse(
8925
9607
  c,
8926
- gitController.getGitDiff({
8927
- ...c.req.param(),
8928
- ...c.req.valid("json")
8929
- }).pipe(Effect51.provide(runtime))
9608
+ fileSystemController.getFileCompletionRoute({
9609
+ ...c.req.valid("query")
9610
+ })
8930
9611
  );
8931
9612
  return response;
8932
9613
  }
8933
- ).post(
8934
- "/api/projects/:projectId/git/commit",
8935
- zValidator("json", CommitRequestSchema),
9614
+ ).get(
9615
+ "/directory-browser",
9616
+ zValidator3(
9617
+ "query",
9618
+ z32.object({
9619
+ currentPath: z32.string().optional(),
9620
+ showHidden: z32.string().optional().transform((val) => val === "true")
9621
+ })
9622
+ ),
8936
9623
  async (c) => {
8937
9624
  const response = await effectToResponse(
8938
9625
  c,
8939
- gitController.commitFiles({
8940
- ...c.req.param(),
8941
- ...c.req.valid("json")
8942
- }).pipe(Effect51.provide(runtime))
9626
+ fileSystemController.getDirectoryListingRoute({
9627
+ ...c.req.valid("query")
9628
+ })
8943
9629
  );
8944
9630
  return response;
8945
9631
  }
8946
- ).post(
8947
- "/api/projects/:projectId/git/push",
8948
- zValidator("json", PushRequestSchema),
9632
+ );
9633
+ });
9634
+
9635
+ // src/server/hono/routes/projectRoutes.ts
9636
+ import { zValidator as zValidator4 } from "@hono/zod-validator";
9637
+ import { Effect as Effect57 } from "effect";
9638
+ import { Hono as Hono6 } from "hono";
9639
+ import { z as z34 } from "zod";
9640
+
9641
+ // src/server/core/git/schema.ts
9642
+ import { z as z33 } from "zod";
9643
+ var CommitRequestSchema = z33.object({
9644
+ files: z33.array(z33.string().min(1)).min(1),
9645
+ message: z33.string().trim().min(1)
9646
+ });
9647
+ var CommitResultSuccessSchema = z33.object({
9648
+ success: z33.literal(true),
9649
+ commitSha: z33.string().length(40),
9650
+ filesCommitted: z33.number().int().positive(),
9651
+ message: z33.string()
9652
+ });
9653
+ var CommitResultErrorSchema = z33.object({
9654
+ success: z33.literal(false),
9655
+ error: z33.string(),
9656
+ errorCode: z33.enum([
9657
+ "EMPTY_MESSAGE",
9658
+ "NO_FILES",
9659
+ "PROJECT_NOT_FOUND",
9660
+ "NOT_A_REPOSITORY",
9661
+ "HOOK_FAILED",
9662
+ "GIT_COMMAND_ERROR"
9663
+ ]),
9664
+ details: z33.string().optional()
9665
+ });
9666
+ var CommitResultSchema = z33.discriminatedUnion("success", [
9667
+ CommitResultSuccessSchema,
9668
+ CommitResultErrorSchema
9669
+ ]);
9670
+ var PushResultSuccessSchema = z33.object({
9671
+ success: z33.literal(true),
9672
+ remote: z33.string(),
9673
+ branch: z33.string(),
9674
+ objectsPushed: z33.number().int().optional()
9675
+ });
9676
+ var PushResultErrorSchema = z33.object({
9677
+ success: z33.literal(false),
9678
+ error: z33.string(),
9679
+ errorCode: z33.enum([
9680
+ "PROJECT_NOT_FOUND",
9681
+ "NOT_A_REPOSITORY",
9682
+ "NO_UPSTREAM",
9683
+ "NON_FAST_FORWARD",
9684
+ "AUTH_FAILED",
9685
+ "NETWORK_ERROR",
9686
+ "TIMEOUT",
9687
+ "GIT_COMMAND_ERROR"
9688
+ ]),
9689
+ details: z33.string().optional()
9690
+ });
9691
+ var PushResultSchema = z33.discriminatedUnion("success", [
9692
+ PushResultSuccessSchema,
9693
+ PushResultErrorSchema
9694
+ ]);
9695
+ var CommitAndPushResultSuccessSchema = z33.object({
9696
+ success: z33.literal(true),
9697
+ commitSha: z33.string().length(40),
9698
+ filesCommitted: z33.number().int().positive(),
9699
+ message: z33.string(),
9700
+ remote: z33.string(),
9701
+ branch: z33.string()
9702
+ });
9703
+ var CommitAndPushResultErrorSchema = z33.object({
9704
+ success: z33.literal(false),
9705
+ commitSucceeded: z33.boolean(),
9706
+ commitSha: z33.string().length(40).optional(),
9707
+ error: z33.string(),
9708
+ errorCode: z33.enum([
9709
+ "EMPTY_MESSAGE",
9710
+ "NO_FILES",
9711
+ "PROJECT_NOT_FOUND",
9712
+ "NOT_A_REPOSITORY",
9713
+ "HOOK_FAILED",
9714
+ "GIT_COMMAND_ERROR",
9715
+ "NO_UPSTREAM",
9716
+ "NON_FAST_FORWARD",
9717
+ "AUTH_FAILED",
9718
+ "NETWORK_ERROR",
9719
+ "TIMEOUT"
9720
+ ]),
9721
+ details: z33.string().optional()
9722
+ });
9723
+ var CommitAndPushResultSchema = z33.discriminatedUnion("success", [
9724
+ CommitAndPushResultSuccessSchema,
9725
+ CommitAndPushResultErrorSchema
9726
+ ]);
9727
+
9728
+ // src/server/hono/routes/projectRoutes.ts
9729
+ var projectRoutes = Effect57.gen(function* () {
9730
+ const projectController = yield* ProjectController;
9731
+ const sessionController = yield* SessionController;
9732
+ const agentSessionController = yield* AgentSessionController;
9733
+ const claudeCodeController = yield* ClaudeCodeController;
9734
+ const fileSystemController = yield* FileSystemController;
9735
+ const gitController = yield* GitController;
9736
+ const runtime = yield* getHonoRuntime;
9737
+ return new Hono6().get("/", async (c) => {
9738
+ const response = await effectToResponse(
9739
+ c,
9740
+ projectController.getProjects()
9741
+ );
9742
+ return response;
9743
+ }).get(
9744
+ "/:projectId",
9745
+ zValidator4("query", z34.object({ cursor: z34.string().optional() })),
8949
9746
  async (c) => {
8950
9747
  const response = await effectToResponse(
8951
9748
  c,
8952
- gitController.pushCommits({
9749
+ projectController.getProject({
8953
9750
  ...c.req.param(),
8954
- ...c.req.valid("json")
8955
- }).pipe(Effect51.provide(runtime))
9751
+ ...c.req.valid("query")
9752
+ }).pipe(Effect57.provide(runtime))
8956
9753
  );
8957
9754
  return response;
8958
9755
  }
8959
9756
  ).post(
8960
- "/api/projects/:projectId/git/commit-and-push",
8961
- zValidator("json", CommitRequestSchema),
9757
+ "/",
9758
+ zValidator4(
9759
+ "json",
9760
+ z34.object({
9761
+ projectPath: z34.string().min(1, "Project path is required")
9762
+ })
9763
+ ),
8962
9764
  async (c) => {
8963
9765
  const response = await effectToResponse(
8964
9766
  c,
8965
- gitController.commitAndPush({
8966
- ...c.req.param(),
9767
+ projectController.createProject({
8967
9768
  ...c.req.valid("json")
8968
- }).pipe(Effect51.provide(runtime))
9769
+ }).pipe(Effect57.provide(runtime))
8969
9770
  );
8970
9771
  return response;
8971
9772
  }
8972
- ).get("/api/projects/:projectId/claude-commands", async (c) => {
9773
+ ).get("/:projectId/latest-session", async (c) => {
8973
9774
  const response = await effectToResponse(
8974
9775
  c,
8975
- claudeCodeController.getClaudeCommands({
9776
+ projectController.getProjectLatestSession({
8976
9777
  ...c.req.param()
8977
- }).pipe(Effect51.provide(runtime))
9778
+ }).pipe(Effect57.provide(runtime))
8978
9779
  );
8979
9780
  return response;
8980
- }).get("/api/projects/:projectId/mcp/list", async (c) => {
9781
+ }).get("/:projectId/sessions/:sessionId", async (c) => {
9782
+ const projectId = c.req.param("projectId");
9783
+ const sessionId = c.req.param("sessionId");
8981
9784
  const response = await effectToResponse(
8982
9785
  c,
8983
- claudeCodeController.getMcpListRoute({
8984
- ...c.req.param()
8985
- }).pipe(Effect51.provide(runtime))
9786
+ sessionController.getSession({ projectId, sessionId }).pipe(Effect57.provide(runtime))
8986
9787
  );
8987
9788
  return response;
8988
- }).get("/api/cc/meta", async (c) => {
9789
+ }).get("/:projectId/sessions/:sessionId/export", async (c) => {
9790
+ const projectId = c.req.param("projectId");
9791
+ const sessionId = c.req.param("sessionId");
8989
9792
  const response = await effectToResponse(
8990
9793
  c,
8991
- claudeCodeController.getClaudeCodeMeta().pipe(Effect51.provide(runtime))
9794
+ sessionController.exportSessionHtml({ projectId, sessionId }).pipe(Effect57.provide(runtime))
8992
9795
  );
8993
9796
  return response;
8994
- }).get("/api/cc/features", async (c) => {
9797
+ }).delete("/:projectId/sessions/:sessionId", async (c) => {
9798
+ const projectId = c.req.param("projectId");
9799
+ const sessionId = c.req.param("sessionId");
8995
9800
  const response = await effectToResponse(
8996
9801
  c,
8997
- claudeCodeController.getAvailableFeatures().pipe(Effect51.provide(runtime))
9802
+ sessionController.deleteSession({ projectId, sessionId }).pipe(Effect57.provide(runtime))
8998
9803
  );
8999
9804
  return response;
9000
- }).get("/api/cc/session-processes", async (c) => {
9805
+ }).get("/:projectId/agent-sessions/:agentId", async (c) => {
9806
+ const projectId = c.req.param("projectId");
9807
+ const agentId = c.req.param("agentId");
9001
9808
  const response = await effectToResponse(
9002
9809
  c,
9003
- claudeCodeSessionProcessController.getSessionProcesses()
9810
+ agentSessionController.getAgentSession({ projectId, agentId })
9004
9811
  );
9005
9812
  return response;
9006
- }).post(
9007
- "/api/cc/session-processes",
9008
- zValidator(
9009
- "json",
9010
- z31.object({
9011
- projectId: z31.string(),
9012
- input: userMessageInputSchema,
9013
- baseSessionId: z31.string().optional()
9813
+ }).get("/:projectId/claude-commands", async (c) => {
9814
+ const response = await effectToResponse(
9815
+ c,
9816
+ claudeCodeController.getClaudeCommands({
9817
+ ...c.req.param()
9818
+ }).pipe(Effect57.provide(runtime))
9819
+ );
9820
+ return response;
9821
+ }).get("/:projectId/mcp/list", async (c) => {
9822
+ const response = await effectToResponse(
9823
+ c,
9824
+ claudeCodeController.getMcpListRoute({
9825
+ ...c.req.param()
9826
+ }).pipe(Effect57.provide(runtime))
9827
+ );
9828
+ return response;
9829
+ }).get(
9830
+ "/:projectId/files",
9831
+ zValidator4(
9832
+ "query",
9833
+ z34.object({
9834
+ filePath: z34.string().min(1, "filePath is required")
9014
9835
  })
9015
9836
  ),
9016
9837
  async (c) => {
9838
+ const { projectId } = c.req.param();
9839
+ const { filePath } = c.req.valid("query");
9017
9840
  const response = await effectToResponse(
9018
9841
  c,
9019
- claudeCodeSessionProcessController.createSessionProcess(
9020
- c.req.valid("json")
9021
- )
9842
+ fileSystemController.getFileContentRoute({
9843
+ projectId,
9844
+ filePath
9845
+ }).pipe(Effect57.provide(runtime))
9022
9846
  );
9023
9847
  return response;
9024
9848
  }
9025
- ).post(
9026
- "/api/cc/session-processes/:sessionProcessId/continue",
9027
- zValidator(
9849
+ ).get("/:projectId/git/current-revisions", async (c) => {
9850
+ const projectId = c.req.param("projectId");
9851
+ const response = await effectToResponse(
9852
+ c,
9853
+ gitController.getCurrentRevisions({
9854
+ projectId
9855
+ }).pipe(Effect57.provide(runtime))
9856
+ );
9857
+ return response;
9858
+ }).post(
9859
+ "/:projectId/git/diff",
9860
+ zValidator4(
9028
9861
  "json",
9029
- z31.object({
9030
- projectId: z31.string(),
9031
- input: userMessageInputSchema,
9032
- baseSessionId: z31.string()
9862
+ z34.object({
9863
+ fromRef: z34.string().min(1, "fromRef is required"),
9864
+ toRef: z34.string().min(1, "toRef is required")
9033
9865
  })
9034
9866
  ),
9035
9867
  async (c) => {
9868
+ const projectId = c.req.param("projectId");
9036
9869
  const response = await effectToResponse(
9037
9870
  c,
9038
- claudeCodeSessionProcessController.continueSessionProcess({
9039
- ...c.req.param(),
9871
+ gitController.getGitDiff({
9872
+ projectId,
9040
9873
  ...c.req.valid("json")
9041
- }).pipe(Effect51.provide(runtime))
9874
+ }).pipe(Effect57.provide(runtime))
9042
9875
  );
9043
9876
  return response;
9044
9877
  }
9045
9878
  ).post(
9046
- "/api/cc/session-processes/:sessionProcessId/abort",
9047
- zValidator("json", z31.object({ projectId: z31.string() })),
9048
- async (c) => {
9049
- const { sessionProcessId } = c.req.param();
9050
- void Effect51.runFork(
9051
- claudeCodeLifeCycleService.abortTask(sessionProcessId)
9052
- );
9053
- return c.json({ message: "Task aborted" });
9054
- }
9055
- ).post(
9056
- "/api/cc/permission-response",
9057
- zValidator(
9058
- "json",
9059
- z31.object({
9060
- permissionRequestId: z31.string(),
9061
- decision: z31.enum(["allow", "deny"])
9062
- })
9063
- ),
9879
+ "/:projectId/git/commit",
9880
+ zValidator4("json", CommitRequestSchema),
9064
9881
  async (c) => {
9882
+ const projectId = c.req.param("projectId");
9065
9883
  const response = await effectToResponse(
9066
9884
  c,
9067
- claudeCodePermissionController.permissionResponse({
9068
- permissionResponse: c.req.valid("json")
9069
- })
9885
+ gitController.commitFiles({
9886
+ projectId,
9887
+ ...c.req.valid("json")
9888
+ }).pipe(Effect57.provide(runtime))
9070
9889
  );
9071
9890
  return response;
9072
9891
  }
9073
- ).get("/api/sse", async (c) => {
9074
- return streamSSE(
9075
- c,
9076
- async (rawStream) => {
9077
- await Runtime3.runPromise(runtime)(
9078
- sseController.handleSSE(rawStream).pipe(Effect51.provide(TypeSafeSSE.make(rawStream)))
9079
- );
9080
- },
9081
- async (err) => {
9082
- console.error("Streaming error:", err);
9083
- }
9084
- );
9085
- }).get("/api/scheduler/jobs", async (c) => {
9892
+ ).post("/:projectId/git/push", async (c) => {
9893
+ const projectId = c.req.param("projectId");
9086
9894
  const response = await effectToResponse(
9087
9895
  c,
9088
- schedulerController.getJobs().pipe(Effect51.provide(runtime))
9896
+ gitController.pushCommits({
9897
+ projectId
9898
+ }).pipe(Effect57.provide(runtime))
9089
9899
  );
9090
9900
  return response;
9091
9901
  }).post(
9092
- "/api/scheduler/jobs",
9093
- zValidator("json", newSchedulerJobSchema),
9094
- async (c) => {
9095
- const response = await effectToResponse(
9096
- c,
9097
- schedulerController.addJob({
9098
- job: c.req.valid("json")
9099
- }).pipe(Effect51.provide(runtime))
9100
- );
9101
- return response;
9102
- }
9103
- ).patch(
9104
- "/api/scheduler/jobs/:id",
9105
- zValidator("json", updateSchedulerJobSchema),
9902
+ "/:projectId/git/commit-and-push",
9903
+ zValidator4("json", CommitRequestSchema),
9106
9904
  async (c) => {
9905
+ const projectId = c.req.param("projectId");
9107
9906
  const response = await effectToResponse(
9108
9907
  c,
9109
- schedulerController.updateJob({
9110
- id: c.req.param("id"),
9111
- job: c.req.valid("json")
9112
- }).pipe(Effect51.provide(runtime))
9908
+ gitController.commitAndPush({
9909
+ projectId,
9910
+ ...c.req.valid("json")
9911
+ }).pipe(Effect57.provide(runtime))
9113
9912
  );
9114
9913
  return response;
9115
9914
  }
9116
- ).delete("/api/scheduler/jobs/:id", async (c) => {
9915
+ ).get("/:projectId/git/branches", async (c) => {
9916
+ const projectId = c.req.param("projectId");
9117
9917
  const response = await effectToResponse(
9118
9918
  c,
9119
- schedulerController.deleteJob({
9120
- id: c.req.param("id")
9121
- }).pipe(Effect51.provide(runtime))
9919
+ gitController.getBranches({
9920
+ projectId
9921
+ }).pipe(Effect57.provide(runtime))
9122
9922
  );
9123
9923
  return response;
9124
- }).get(
9125
- "/api/fs/file-completion",
9126
- zValidator(
9127
- "query",
9128
- z31.object({
9129
- projectId: z31.string(),
9130
- basePath: z31.string().optional().default("/api/")
9924
+ }).post(
9925
+ "/:projectId/git/checkout",
9926
+ zValidator4(
9927
+ "json",
9928
+ z34.object({
9929
+ branchName: z34.string().min(1, "branchName is required")
9131
9930
  })
9132
9931
  ),
9133
9932
  async (c) => {
9933
+ const projectId = c.req.param("projectId");
9134
9934
  const response = await effectToResponse(
9135
9935
  c,
9136
- fileSystemController.getFileCompletionRoute({
9137
- ...c.req.valid("query")
9138
- })
9936
+ gitController.checkoutBranch({
9937
+ projectId,
9938
+ ...c.req.valid("json")
9939
+ }).pipe(Effect57.provide(runtime))
9139
9940
  );
9140
9941
  return response;
9141
9942
  }
9142
- ).get(
9143
- "/api/fs/directory-browser",
9144
- zValidator(
9145
- "query",
9146
- z31.object({
9147
- currentPath: z31.string().optional(),
9148
- showHidden: z31.string().optional().transform((val) => val === "true")
9149
- })
9150
- ),
9943
+ );
9944
+ });
9945
+
9946
+ // src/server/hono/routes/schedulerRoutes.ts
9947
+ import { zValidator as zValidator5 } from "@hono/zod-validator";
9948
+ import { Effect as Effect58 } from "effect";
9949
+ import { Hono as Hono7 } from "hono";
9950
+ var schedulerRoutes = Effect58.gen(function* () {
9951
+ const schedulerController = yield* SchedulerController;
9952
+ const runtime = yield* getHonoRuntime;
9953
+ return new Hono7().get("/jobs", async (c) => {
9954
+ const response = await effectToResponse(
9955
+ c,
9956
+ schedulerController.getJobs().pipe(Effect58.provide(runtime))
9957
+ );
9958
+ return response;
9959
+ }).post("/jobs", zValidator5("json", newSchedulerJobSchema), async (c) => {
9960
+ const response = await effectToResponse(
9961
+ c,
9962
+ schedulerController.addJob({
9963
+ job: c.req.valid("json")
9964
+ }).pipe(Effect58.provide(runtime))
9965
+ );
9966
+ return response;
9967
+ }).patch(
9968
+ "/jobs/:id",
9969
+ zValidator5("json", updateSchedulerJobSchema),
9151
9970
  async (c) => {
9152
9971
  const response = await effectToResponse(
9153
9972
  c,
9154
- fileSystemController.getDirectoryListingRoute({
9155
- ...c.req.valid("query")
9156
- })
9973
+ schedulerController.updateJob({
9974
+ id: c.req.param("id"),
9975
+ job: c.req.valid("json")
9976
+ }).pipe(Effect58.provide(runtime))
9157
9977
  );
9158
9978
  return response;
9159
9979
  }
9160
- ).get(
9161
- "/api/search",
9162
- zValidator(
9980
+ ).delete("/jobs/:id", async (c) => {
9981
+ const response = await effectToResponse(
9982
+ c,
9983
+ schedulerController.deleteJob({
9984
+ id: c.req.param("id")
9985
+ }).pipe(Effect58.provide(runtime))
9986
+ );
9987
+ return response;
9988
+ });
9989
+ });
9990
+
9991
+ // src/server/hono/routes/searchRoutes.ts
9992
+ import { zValidator as zValidator6 } from "@hono/zod-validator";
9993
+ import { Effect as Effect59 } from "effect";
9994
+ import { Hono as Hono8 } from "hono";
9995
+ import { z as z35 } from "zod";
9996
+ var searchRoutes = Effect59.gen(function* () {
9997
+ const searchController = yield* SearchController;
9998
+ const runtime = yield* getHonoRuntime;
9999
+ return new Hono8().get(
10000
+ "/",
10001
+ zValidator6(
9163
10002
  "query",
9164
- z31.object({
9165
- q: z31.string().min(2),
9166
- limit: z31.string().optional().transform((val) => val ? parseInt(val, 10) : void 0),
9167
- projectId: z31.string().optional()
10003
+ z35.object({
10004
+ q: z35.string().min(2),
10005
+ limit: z35.string().optional().transform((val) => val ? parseInt(val, 10) : void 0),
10006
+ projectId: z35.string().optional()
9168
10007
  })
9169
10008
  ),
9170
10009
  async (c) => {
9171
10010
  const { q, limit, projectId } = c.req.valid("query");
9172
10011
  const response = await effectToResponse(
9173
10012
  c,
9174
- searchController.search({ query: q, limit, projectId }).pipe(Effect51.provide(runtime))
10013
+ searchController.search({ query: q, limit, projectId }).pipe(Effect59.provide(runtime))
9175
10014
  );
9176
10015
  return response;
9177
10016
  }
9178
- ).get("/api/flags", async (c) => {
9179
- const response = await effectToResponse(
10017
+ );
10018
+ });
10019
+
10020
+ // src/server/hono/routes/sseRoutes.ts
10021
+ import { Effect as Effect60, Runtime as Runtime4 } from "effect";
10022
+ import { Hono as Hono9 } from "hono";
10023
+ import { streamSSE } from "hono/streaming";
10024
+ var sseRoutes = Effect60.gen(function* () {
10025
+ const sseController = yield* SSEController;
10026
+ const runtime = yield* getHonoRuntime;
10027
+ return new Hono9().get("/sse", async (c) => {
10028
+ return streamSSE(
9180
10029
  c,
9181
- featureFlagController.getFlags().pipe(Effect51.provide(runtime))
10030
+ async (rawStream) => {
10031
+ await Runtime4.runPromise(runtime)(
10032
+ sseController.handleSSE(rawStream).pipe(Effect60.provide(TypeSafeSSE.make(rawStream)))
10033
+ );
10034
+ },
10035
+ async (err) => {
10036
+ console.error("Streaming error:", err);
10037
+ }
9182
10038
  );
9183
- return response;
9184
- }).get(
9185
- "/api/tasks",
9186
- zValidator(
10039
+ });
10040
+ });
10041
+
10042
+ // src/server/hono/routes/tasksRoutes.ts
10043
+ import { zValidator as zValidator7 } from "@hono/zod-validator";
10044
+ import { Effect as Effect61 } from "effect";
10045
+ import { Hono as Hono10 } from "hono";
10046
+ import { z as z36 } from "zod";
10047
+ var tasksRoutes = Effect61.gen(function* () {
10048
+ const tasksController = yield* TasksController;
10049
+ const runtime = yield* getHonoRuntime;
10050
+ return new Hono10().get(
10051
+ "/",
10052
+ zValidator7(
9187
10053
  "query",
9188
- z31.object({
9189
- projectId: z31.string(),
9190
- sessionId: z31.string().optional()
10054
+ z36.object({
10055
+ projectId: z36.string(),
10056
+ sessionId: z36.string().optional()
9191
10057
  })
9192
10058
  ),
9193
10059
  async (c) => {
9194
10060
  const { projectId, sessionId } = c.req.valid("query");
9195
10061
  const projectPath = decodeProjectId(projectId);
10062
+ const status = 200;
9196
10063
  const response = await effectToResponse(
9197
10064
  c,
9198
10065
  tasksController.listTasks(projectPath, sessionId).pipe(
9199
- Effect51.map((tasks) => ({
9200
- status: 200,
10066
+ Effect61.map((tasks) => ({
10067
+ status,
9201
10068
  response: tasks
9202
10069
  })),
9203
- Effect51.provide(runtime)
10070
+ Effect61.provide(runtime)
9204
10071
  )
9205
10072
  );
9206
10073
  return response;
9207
10074
  }
9208
10075
  ).post(
9209
- "/api/tasks",
9210
- zValidator(
10076
+ "/",
10077
+ zValidator7(
9211
10078
  "query",
9212
- z31.object({
9213
- projectId: z31.string(),
9214
- sessionId: z31.string().optional()
10079
+ z36.object({
10080
+ projectId: z36.string(),
10081
+ sessionId: z36.string().optional()
9215
10082
  })
9216
10083
  ),
9217
- zValidator("json", TaskCreateSchema),
10084
+ zValidator7("json", TaskCreateSchema),
9218
10085
  async (c) => {
9219
10086
  const { projectId, sessionId } = c.req.valid("query");
9220
10087
  const body = c.req.valid("json");
9221
10088
  const projectPath = decodeProjectId(projectId);
10089
+ const status = 200;
9222
10090
  const response = await effectToResponse(
9223
10091
  c,
9224
10092
  tasksController.createTask(projectPath, body, sessionId).pipe(
9225
- Effect51.map((task) => ({
9226
- status: 200,
10093
+ Effect61.map((task) => ({
10094
+ status,
9227
10095
  response: task
9228
10096
  })),
9229
- Effect51.provide(runtime)
10097
+ Effect61.provide(runtime)
9230
10098
  )
9231
10099
  );
9232
10100
  return response;
9233
10101
  }
9234
10102
  ).patch(
9235
- "/api/tasks/:id",
9236
- zValidator(
10103
+ "/:taskId",
10104
+ zValidator7(
9237
10105
  "query",
9238
- z31.object({
9239
- projectId: z31.string(),
9240
- sessionId: z31.string().optional()
10106
+ z36.object({
10107
+ projectId: z36.string(),
10108
+ sessionId: z36.string().optional()
9241
10109
  })
9242
10110
  ),
9243
- zValidator("json", TaskUpdateSchema.omit({ taskId: true })),
10111
+ zValidator7("json", TaskUpdateSchema.omit({ taskId: true })),
9244
10112
  async (c) => {
9245
- const { id } = c.req.param();
10113
+ const { taskId } = c.req.param();
9246
10114
  const { projectId, sessionId } = c.req.valid("query");
9247
10115
  const body = c.req.valid("json");
9248
10116
  const projectPath = decodeProjectId(projectId);
10117
+ const status = 200;
9249
10118
  const response = await effectToResponse(
9250
10119
  c,
9251
- tasksController.updateTask(projectPath, { ...body, taskId: id }, sessionId).pipe(
9252
- Effect51.map((task) => ({
9253
- status: 200,
10120
+ tasksController.updateTask(projectPath, { ...body, taskId }, sessionId).pipe(
10121
+ Effect61.map((task) => ({
10122
+ status,
9254
10123
  response: task
9255
10124
  })),
9256
- Effect51.provide(runtime)
10125
+ Effect61.provide(runtime)
9257
10126
  )
9258
10127
  );
9259
10128
  return response;
@@ -9261,26 +10130,197 @@ var routes = (app, options) => Effect51.gen(function* () {
9261
10130
  );
9262
10131
  });
9263
10132
 
10133
+ // src/server/hono/routes/index.ts
10134
+ var routes = (app, options) => Effect62.gen(function* () {
10135
+ const ccvOptionsService = yield* CcvOptionsService;
10136
+ yield* ccvOptionsService.loadCliOptions(options);
10137
+ const envService = yield* EnvService;
10138
+ const userConfigService = yield* UserConfigService;
10139
+ const initializeService = yield* InitializeService;
10140
+ const { authRequiredMiddleware } = yield* AuthMiddleware;
10141
+ const runtime = yield* getHonoRuntime;
10142
+ if ((yield* envService.getEnv("NEXT_PHASE")) !== "phase-production-build") {
10143
+ yield* initializeService.startInitialization();
10144
+ prexit(async () => {
10145
+ await Runtime5.runPromise(runtime)(initializeService.stopCleanup());
10146
+ });
10147
+ }
10148
+ return app.use(configMiddleware).use(async (c, next) => {
10149
+ await Runtime5.runPromise(
10150
+ runtime,
10151
+ userConfigService.setUserConfig({
10152
+ ...c.get("userConfig")
10153
+ })
10154
+ );
10155
+ await next();
10156
+ }).get("/api/version", async (c) => {
10157
+ return c.json({
10158
+ version: package_default.version
10159
+ });
10160
+ }).route("/api/auth", yield* authRoutes).use(authRequiredMiddleware).get("/api/config", async (c) => {
10161
+ return c.json({
10162
+ config: c.get("userConfig")
10163
+ });
10164
+ }).put("/api/config", zValidator8("json", userConfigSchema), async (c) => {
10165
+ const { ...config } = c.req.valid("json");
10166
+ setCookie3(c, "ccv-config", JSON.stringify(config));
10167
+ return c.json({
10168
+ config
10169
+ });
10170
+ }).route("/api/projects", yield* projectRoutes).route("/api/claude-code", yield* claudeCodeRoutes).route("/api/scheduler", yield* schedulerRoutes).route("/api/file-system", yield* fileSystemRoutes).route("/api/search", yield* searchRoutes).route("/api/feature-flags", yield* featureFlagRoutes).route("/api/tasks", yield* tasksRoutes).route("/api/sse", yield* sseRoutes);
10171
+ });
10172
+
9264
10173
  // src/server/lib/effect/layers.ts
9265
10174
  import { NodeContext } from "@effect/platform-node";
9266
- import { Layer as Layer42 } from "effect";
9267
- var platformLayer = Layer42.mergeAll(
10175
+ import { Layer as Layer43 } from "effect";
10176
+ var platformLayer = Layer43.mergeAll(
9268
10177
  ApplicationContext.Live,
9269
10178
  UserConfigService.Live,
9270
10179
  EventBus.Live,
9271
10180
  EnvService.Live,
9272
10181
  CcvOptionsService.Live
9273
10182
  ).pipe(
9274
- Layer42.provide(EnvService.Live),
9275
- Layer42.provide(CcvOptionsService.Live),
9276
- Layer42.provide(NodeContext.layer)
10183
+ Layer43.provide(EnvService.Live),
10184
+ Layer43.provide(CcvOptionsService.Live),
10185
+ Layer43.provide(NodeContext.layer)
9277
10186
  );
9278
10187
 
10188
+ // src/server/terminal/terminalWebSocket.ts
10189
+ import { Effect as Effect63, Runtime as Runtime6 } from "effect";
10190
+ import WebSocket, { WebSocketServer } from "ws";
10191
+ var parseCookies = (cookieHeader) => {
10192
+ const result = {};
10193
+ if (!cookieHeader) return result;
10194
+ const parts = cookieHeader.split(";");
10195
+ for (const part of parts) {
10196
+ const [rawKey, ...rest] = part.trim().split("=");
10197
+ if (!rawKey) continue;
10198
+ result[rawKey] = rest.join("=");
10199
+ }
10200
+ return result;
10201
+ };
10202
+ var parseClientMessage = (payload) => {
10203
+ try {
10204
+ const parsed = JSON.parse(payload);
10205
+ if (!parsed || typeof parsed !== "object") return void 0;
10206
+ if (parsed.type === "input" && typeof parsed.data === "string") {
10207
+ return { type: "input", data: parsed.data };
10208
+ }
10209
+ if (parsed.type === "resize" && typeof parsed.cols === "number" && typeof parsed.rows === "number") {
10210
+ return { type: "resize", cols: parsed.cols, rows: parsed.rows };
10211
+ }
10212
+ if (parsed.type === "signal" && typeof parsed.name === "string") {
10213
+ return { type: "signal", name: parsed.name };
10214
+ }
10215
+ if (parsed.type === "sync" && typeof parsed.lastSeq === "number") {
10216
+ return { type: "sync", lastSeq: parsed.lastSeq };
10217
+ }
10218
+ if (parsed.type === "ping") {
10219
+ return { type: "ping" };
10220
+ }
10221
+ } catch {
10222
+ return void 0;
10223
+ }
10224
+ return void 0;
10225
+ };
10226
+ var sendJson = (client, payload) => {
10227
+ if (client.readyState !== WebSocket.OPEN) return;
10228
+ client.send(JSON.stringify(payload));
10229
+ };
10230
+ var baseUrlForRequest = (req) => {
10231
+ const host = req.headers.host ?? "localhost";
10232
+ return `http://${host}`;
10233
+ };
10234
+ var setupTerminalWebSocket = (server) => Effect63.gen(function* () {
10235
+ const terminalService = yield* TerminalService;
10236
+ const { getAuthState } = yield* AuthMiddleware;
10237
+ const { authEnabled, validSessionToken } = yield* getAuthState;
10238
+ const runtime = yield* Effect63.runtime();
10239
+ const runPromise = Runtime6.runPromise(runtime);
10240
+ const wss = new WebSocketServer({ noServer: true });
10241
+ server.on("upgrade", (req, socket, head) => {
10242
+ const url = new URL(req.url ?? "/", baseUrlForRequest(req));
10243
+ if (url.pathname !== "/ws/terminal") return;
10244
+ if (authEnabled) {
10245
+ const cookies = parseCookies(req.headers.cookie);
10246
+ if (cookies["ccv-session"] !== validSessionToken) {
10247
+ socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
10248
+ socket.destroy();
10249
+ return;
10250
+ }
10251
+ }
10252
+ wss.handleUpgrade(req, socket, head, (ws) => {
10253
+ wss.emit("connection", ws, req);
10254
+ });
10255
+ });
10256
+ wss.on("connection", (ws, req) => {
10257
+ const url = new URL(req.url ?? "/", baseUrlForRequest(req));
10258
+ const sessionIdParam = url.searchParams.get("sessionId");
10259
+ const requestedSessionId = sessionIdParam && sessionIdParam.length > 0 ? sessionIdParam : void 0;
10260
+ const cwdParam = url.searchParams.get("cwd");
10261
+ const cwd = cwdParam && cwdParam.length > 0 ? cwdParam : void 0;
10262
+ runPromise(terminalService.getOrCreateSession(requestedSessionId, cwd)).then((session) => {
10263
+ sendJson(ws, {
10264
+ type: "hello",
10265
+ sessionId: session.id,
10266
+ seq: session.seq
10267
+ });
10268
+ return runPromise(
10269
+ terminalService.registerClient(session.id, ws)
10270
+ ).then(() => session);
10271
+ }).then((session) => {
10272
+ ws.on("message", (data) => {
10273
+ const text = typeof data === "string" ? data : data instanceof Buffer ? data.toString("utf8") : void 0;
10274
+ if (!text) return;
10275
+ const message = parseClientMessage(text);
10276
+ if (!message) return;
10277
+ if (message.type === "input") {
10278
+ void runPromise(
10279
+ terminalService.writeInput(session.id, message.data)
10280
+ );
10281
+ return;
10282
+ }
10283
+ if (message.type === "resize") {
10284
+ void runPromise(
10285
+ terminalService.resize(session.id, message.cols, message.rows)
10286
+ );
10287
+ return;
10288
+ }
10289
+ if (message.type === "signal") {
10290
+ void runPromise(terminalService.signal(session.id, message.name));
10291
+ return;
10292
+ }
10293
+ if (message.type === "sync") {
10294
+ void runPromise(
10295
+ terminalService.snapshotSince(session.id, message.lastSeq)
10296
+ ).then((snapshot) => {
10297
+ if (!snapshot) return;
10298
+ sendJson(ws, {
10299
+ type: "snapshot",
10300
+ seq: snapshot.seq,
10301
+ data: snapshot.data
10302
+ });
10303
+ });
10304
+ return;
10305
+ }
10306
+ if (message.type === "ping") {
10307
+ sendJson(ws, { type: "pong" });
10308
+ }
10309
+ });
10310
+ ws.on("close", () => {
10311
+ void runPromise(terminalService.unregisterClient(session.id, ws));
10312
+ });
10313
+ }).catch(() => {
10314
+ ws.close(1011, "Session initialization failed");
10315
+ });
10316
+ });
10317
+ });
10318
+
9279
10319
  // src/server/startServer.ts
9280
10320
  var startServer = async (options) => {
9281
- const isDevelopment = process.env.NODE_ENV === "development";
10321
+ const isDevelopment = isDevelopmentEnv(process.env.CCV_ENV);
9282
10322
  if (!isDevelopment) {
9283
- const staticPath = resolve6(import.meta.dirname, "static");
10323
+ const staticPath = resolve7(import.meta.dirname, "static");
9284
10324
  console.log("Serving static files from ", staticPath);
9285
10325
  honoApp.use(
9286
10326
  "/assets/*",
@@ -9292,12 +10332,18 @@ var startServer = async (options) => {
9292
10332
  if (c.req.path.startsWith("/api")) {
9293
10333
  return next();
9294
10334
  }
9295
- const html = await readFile2(resolve6(staticPath, "index.html"), "utf-8");
10335
+ const html = await readFile3(resolve7(staticPath, "index.html"), "utf-8");
9296
10336
  return c.html(html);
9297
10337
  });
9298
10338
  }
9299
- const program2 = routes(honoApp, options).pipe(Effect52.provide(MainLayer));
9300
- await Effect52.runPromise(program2);
10339
+ const server = createAdaptorServer({
10340
+ fetch: honoApp.fetch
10341
+ });
10342
+ const program2 = Effect64.gen(function* () {
10343
+ yield* routes(honoApp, options);
10344
+ yield* setupTerminalWebSocket(server);
10345
+ }).pipe(Effect64.provide(MainLayer), Effect64.scoped);
10346
+ await Effect64.runPromise(program2);
9301
10347
  const port = isDevelopment ? (
9302
10348
  // biome-ignore lint/style/noProcessEnv: allow only here
9303
10349
  process.env.DEV_BE_PORT ?? "3401"
@@ -9306,29 +10352,24 @@ var startServer = async (options) => {
9306
10352
  options.port ?? process.env.PORT ?? "3000"
9307
10353
  );
9308
10354
  const hostname = options.hostname ?? process.env.HOSTNAME ?? "localhost";
9309
- serve(
9310
- {
9311
- fetch: honoApp.fetch,
9312
- port: parseInt(port, 10),
9313
- hostname
9314
- },
9315
- (info) => {
9316
- console.log(`Server is running on http://${hostname}:${info.port}`);
9317
- }
9318
- );
10355
+ server.listen(parseInt(port, 10), hostname, () => {
10356
+ const info = server.address();
10357
+ const serverPort = typeof info === "object" && info !== null ? info.port : port;
10358
+ console.log(`Server is running on http://${hostname}:${serverPort}`);
10359
+ });
9319
10360
  };
9320
- var PlatformLayer = Layer43.mergeAll(platformLayer, NodeContext2.layer);
9321
- var InfraBasics = Layer43.mergeAll(
10361
+ var PlatformLayer = Layer44.mergeAll(platformLayer, NodeContext2.layer);
10362
+ var InfraBasics = Layer44.mergeAll(
9322
10363
  VirtualConversationDatabase.Live,
9323
10364
  ProjectMetaService.Live,
9324
10365
  SessionMetaService.Live
9325
10366
  );
9326
- var InfraRepos = Layer43.mergeAll(
10367
+ var InfraRepos = Layer44.mergeAll(
9327
10368
  ProjectRepository.Live,
9328
10369
  SessionRepository.Live
9329
- ).pipe(Layer43.provideMerge(InfraBasics));
9330
- var InfraLayer = AgentSessionLayer.pipe(Layer43.provideMerge(InfraRepos));
9331
- var DomainBase = Layer43.mergeAll(
10370
+ ).pipe(Layer44.provideMerge(InfraBasics));
10371
+ var InfraLayer = AgentSessionLayer.pipe(Layer44.provideMerge(InfraRepos));
10372
+ var DomainBase = Layer44.mergeAll(
9332
10373
  ClaudeCodePermissionService.Live,
9333
10374
  ClaudeCodeSessionProcessService.Live,
9334
10375
  ClaudeCodeService.Live,
@@ -9339,17 +10380,18 @@ var DomainBase = Layer43.mergeAll(
9339
10380
  TasksService.Live
9340
10381
  );
9341
10382
  var DomainLayer = ClaudeCodeLifeCycleService.Live.pipe(
9342
- Layer43.provideMerge(DomainBase)
10383
+ Layer44.provideMerge(DomainBase)
9343
10384
  );
9344
- var AppServices = Layer43.mergeAll(
10385
+ var AppServices = Layer44.mergeAll(
9345
10386
  FileWatcherService.Live,
9346
10387
  RateLimitAutoScheduleService.Live,
9347
- AuthMiddleware.Live
10388
+ AuthMiddleware.Live,
10389
+ TerminalService.Live
9348
10390
  );
9349
10391
  var ApplicationLayer = InitializeService.Live.pipe(
9350
- Layer43.provideMerge(AppServices)
10392
+ Layer44.provideMerge(AppServices)
9351
10393
  );
9352
- var PresentationLayer = Layer43.mergeAll(
10394
+ var PresentationLayer = Layer44.mergeAll(
9353
10395
  ProjectController.Live,
9354
10396
  SessionController.Live,
9355
10397
  AgentSessionController.Live,
@@ -9365,17 +10407,23 @@ var PresentationLayer = Layer43.mergeAll(
9365
10407
  TasksController.Live
9366
10408
  );
9367
10409
  var MainLayer = PresentationLayer.pipe(
9368
- Layer43.provideMerge(ApplicationLayer),
9369
- Layer43.provideMerge(DomainLayer),
9370
- Layer43.provideMerge(InfraLayer),
9371
- Layer43.provideMerge(PlatformLayer)
10410
+ Layer44.provideMerge(ApplicationLayer),
10411
+ Layer44.provideMerge(DomainLayer),
10412
+ Layer44.provideMerge(InfraLayer),
10413
+ Layer44.provideMerge(PlatformLayer)
9372
10414
  );
9373
10415
 
9374
10416
  // src/server/main.ts
9375
10417
  var program = new Command3();
9376
10418
  program.name(package_default.name).version(package_default.version).description(package_default.description);
9377
- program.option("-p, --port <port>", "port to listen on").option("-h, --hostname <hostname>", "hostname to listen on").option("-P, --password <password>", "password to authenticate").option("-e, --executable <executable>", "path to claude code executable").option("--claude-dir <claude-dir>", "path to claude directory").action(async (options) => {
9378
- await Effect53.runPromise(checkDeprecatedEnvs);
10419
+ program.option("-p, --port <port>", "port to listen on").option("-h, --hostname <hostname>", "hostname to listen on").option("-P, --password <password>", "password to authenticate").option("-e, --executable <executable>", "path to claude code executable").option("--claude-dir <claude-dir>", "path to claude directory").option(
10420
+ "--terminal-disabled",
10421
+ "disable the in-app terminal panel when enabled"
10422
+ ).option("--terminal-shell <path>", "shell executable for terminal sessions").option(
10423
+ "--terminal-unrestricted",
10424
+ "disable restricted shell flags for bash sessions"
10425
+ ).action(async (options) => {
10426
+ await Effect65.runPromise(checkDeprecatedEnvs);
9379
10427
  await startServer(options);
9380
10428
  });
9381
10429
  var main = async () => {