@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/README.md +17 -1
- package/dist/main.js +1859 -811
- package/dist/main.js.map +4 -4
- package/dist/static/assets/ProtectedRoute-kldRjzy3.js +1 -0
- package/dist/static/assets/{eye-BGsXRq_8.js → eye-BEkfdnRO.js} +1 -1
- package/dist/static/assets/index-C9P7O98i.js +1 -0
- package/dist/static/assets/{index-sXgy7Ajd.js → index-CHZixXYx.js} +1 -1
- package/dist/static/assets/index-CTq99aOX.css +1 -0
- package/dist/static/assets/index-nTFAt0o5.js +101 -0
- package/dist/static/assets/{label-gygRdIVO.js → label-DigNu3m4.js} +1 -1
- package/dist/static/assets/{login-DRDs0jaq.js → login-CotBrr4P.js} +1 -1
- package/dist/static/assets/messages-D_2afeYc.js +1 -0
- package/dist/static/assets/messages-lrmcxVGh.js +1 -0
- package/dist/static/assets/messages-m0e3n_U5.js +1 -0
- package/dist/static/assets/session-B4VFb0DB.js +51 -0
- package/dist/static/assets/{session-B5BSJDkJ.js → session-BfEVLScy.js} +1 -1
- package/dist/static/assets/session-DFuMZ0ql.css +1 -0
- package/dist/static/index.html +2 -2
- package/package.json +14 -4
- package/dist/static/assets/ProtectedRoute-BZvfrfXZ.js +0 -1
- package/dist/static/assets/index-D9d-SW2q.js +0 -1
- package/dist/static/assets/index-hJ5yiXvq.js +0 -101
- package/dist/static/assets/index-kl3XSvPM.css +0 -1
- package/dist/static/assets/messages-5zx59L1k.js +0 -1
- package/dist/static/assets/messages-Diwakl7a.js +0 -1
- package/dist/static/assets/messages-Pr7vABlX.js +0 -1
- package/dist/static/assets/session-D2lB0w81.js +0 -40
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
|
|
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.
|
|
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": "
|
|
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: "
|
|
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.
|
|
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
|
|
218
|
-
import { resolve as
|
|
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 {
|
|
230
|
+
import { createAdaptorServer } from "@hono/node-server";
|
|
221
231
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
222
|
-
import { Effect as
|
|
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
|
|
944
|
-
const mtime = Option.getOrElse(
|
|
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
|
|
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(
|
|
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
|
|
1056
|
+
const stat2 = yield* Effect9.tryPromise(
|
|
1040
1057
|
() => fs.stat(fullPath).pipe(Effect9.runPromise)
|
|
1041
1058
|
).pipe(Effect9.catchAll(() => Effect9.succeed(null)));
|
|
1042
|
-
if (!
|
|
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(
|
|
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
|
|
1361
|
-
const {
|
|
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
|
-
|
|
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
|
|
1376
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1403
|
-
|
|
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 {
|
|
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
|
-
|
|
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((
|
|
1848
|
+
const promise = new Promise((resolve8, reject) => {
|
|
1844
1849
|
promiseResolve = (value) => {
|
|
1845
1850
|
promiseRef.status = "resolved";
|
|
1846
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
|
2517
|
+
const stat2 = yield* Effect21.tryPromise(
|
|
2508
2518
|
() => fs.stat(fullPath).pipe(Effect21.runPromise)
|
|
2509
2519
|
).pipe(Effect21.catchAll(() => Effect21.succeed(null)));
|
|
2510
|
-
if (!
|
|
2520
|
+
if (!stat2) {
|
|
2511
2521
|
return null;
|
|
2512
2522
|
}
|
|
2513
2523
|
return {
|
|
2514
2524
|
id: sessionId,
|
|
2515
2525
|
jsonlFilePath: fullPath,
|
|
2516
|
-
lastModifiedAt: Option3.getOrElse(
|
|
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,
|
|
2768
|
+
const { sessionDef, turnDef } = options;
|
|
2795
2769
|
return Effect23.gen(function* () {
|
|
2796
2770
|
const task = {
|
|
2797
|
-
def:
|
|
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.
|
|
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.
|
|
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 = (
|
|
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.
|
|
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({
|
|
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
|
|
2929
|
-
const { sessionProcessId,
|
|
2902
|
+
const changeTurnState = (options) => {
|
|
2903
|
+
const { sessionProcessId, turnId, nextTask } = options;
|
|
2930
2904
|
return Effect23.gen(function* () {
|
|
2931
|
-
const { task } = yield* getTask(
|
|
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.
|
|
2911
|
+
(t) => t.def.turnId === task.def.turnId ? { ...nextTask } : t
|
|
2938
2912
|
)
|
|
2939
2913
|
} : p
|
|
2940
2914
|
);
|
|
2941
2915
|
});
|
|
2942
|
-
const updated = yield* getTask(
|
|
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*
|
|
2935
|
+
const newTask = yield* changeTurnState({
|
|
2962
2936
|
sessionProcessId,
|
|
2963
|
-
|
|
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*
|
|
3029
|
+
const newTask = yield* changeTurnState({
|
|
3056
3030
|
sessionProcessId,
|
|
3057
|
-
|
|
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.
|
|
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*
|
|
3069
|
+
yield* changeTurnState({
|
|
3096
3070
|
sessionProcessId,
|
|
3097
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
3130
|
+
turnDef: {
|
|
3157
3131
|
type: "continue",
|
|
3158
3132
|
sessionId: baseSessionId,
|
|
3159
3133
|
baseSessionId,
|
|
3160
|
-
|
|
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
|
|
3184
|
-
const {
|
|
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
|
|
3194
|
-
cwd
|
|
3167
|
+
projectId,
|
|
3168
|
+
cwd,
|
|
3195
3169
|
abortController: new AbortController(),
|
|
3196
3170
|
setNextMessage,
|
|
3197
3171
|
sessionProcessId: ulid2()
|
|
3198
3172
|
},
|
|
3199
|
-
|
|
3173
|
+
turnDef: baseSession === void 0 ? {
|
|
3200
3174
|
type: "new",
|
|
3201
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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"
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
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
|
-
|
|
3294
|
+
turnId: task.def.turnId,
|
|
3308
3295
|
userConfig,
|
|
3309
3296
|
sessionId: task.def.baseSessionId
|
|
3310
3297
|
});
|
|
3311
|
-
return yield*
|
|
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(
|
|
3311
|
+
handleMessage(message)
|
|
3328
3312
|
).catch((error) => {
|
|
3329
3313
|
Effect24.runFork(
|
|
3330
|
-
sessionProcessService.
|
|
3314
|
+
sessionProcessService.changeTurnState({
|
|
3331
3315
|
sessionProcessId: sessionProcess.def.sessionProcessId,
|
|
3332
|
-
|
|
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.
|
|
3345
|
+
sessionProcessService.changeTurnState({
|
|
3362
3346
|
sessionProcessId: sessionProcess.def.sessionProcessId,
|
|
3363
|
-
|
|
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
|
-
|
|
3428
|
-
|
|
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,
|
|
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.
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
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.
|
|
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((
|
|
3542
|
-
connectionResolve =
|
|
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
|
|
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 =
|
|
4300
|
-
const content = await
|
|
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
|
-
|
|
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.
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
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
|
-
|
|
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.
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
6182
|
-
if (
|
|
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 = (
|
|
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(
|
|
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 =
|
|
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:
|
|
6308
|
-
if (
|
|
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
|
-
|
|
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
|
|
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(
|
|
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,
|
|
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, `${
|
|
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 ${
|
|
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,
|
|
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
|
-
...
|
|
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/
|
|
8384
|
-
import {
|
|
8385
|
-
|
|
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/
|
|
8388
|
-
|
|
8389
|
-
|
|
8390
|
-
|
|
8391
|
-
|
|
8392
|
-
|
|
8393
|
-
|
|
8394
|
-
|
|
8395
|
-
|
|
8396
|
-
|
|
8397
|
-
|
|
8398
|
-
|
|
8399
|
-
|
|
8400
|
-
|
|
8401
|
-
|
|
8402
|
-
|
|
8403
|
-
|
|
8404
|
-
|
|
8405
|
-
|
|
8406
|
-
|
|
8407
|
-
|
|
8408
|
-
|
|
8409
|
-
|
|
8410
|
-
|
|
8411
|
-
|
|
8412
|
-
|
|
8413
|
-
|
|
8414
|
-
|
|
8415
|
-
|
|
8416
|
-
|
|
8417
|
-
|
|
8418
|
-
|
|
8419
|
-
|
|
8420
|
-
|
|
8421
|
-
|
|
8422
|
-
|
|
8423
|
-
|
|
8424
|
-
|
|
8425
|
-
|
|
8426
|
-
|
|
8427
|
-
|
|
8428
|
-
|
|
8429
|
-
|
|
8430
|
-
|
|
8431
|
-
|
|
8432
|
-
|
|
8433
|
-
|
|
8434
|
-
|
|
8435
|
-
|
|
8436
|
-
|
|
8437
|
-
|
|
8438
|
-
|
|
8439
|
-
|
|
8440
|
-
|
|
8441
|
-
|
|
8442
|
-
|
|
8443
|
-
|
|
8444
|
-
|
|
8445
|
-
|
|
8446
|
-
|
|
8447
|
-
|
|
8448
|
-
|
|
8449
|
-
|
|
8450
|
-
|
|
8451
|
-
|
|
8452
|
-
|
|
8453
|
-
|
|
8454
|
-
|
|
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
|
-
|
|
8459
|
-
|
|
9145
|
+
Effect49.catchAll(() => Effect49.void),
|
|
9146
|
+
Effect49.withSpan("initialize-cache")
|
|
8460
9147
|
);
|
|
8461
|
-
}).pipe(
|
|
9148
|
+
}).pipe(Effect49.withSpan("start-initialization"));
|
|
8462
9149
|
};
|
|
8463
|
-
const stopCleanup = () =>
|
|
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
|
|
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
|
|
8496
|
-
|
|
8497
|
-
"
|
|
8498
|
-
|
|
8499
|
-
"
|
|
8500
|
-
|
|
8501
|
-
|
|
8502
|
-
|
|
8503
|
-
var
|
|
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
|
-
|
|
8506
|
-
|
|
8507
|
-
|
|
8508
|
-
const
|
|
8509
|
-
|
|
8510
|
-
|
|
8511
|
-
|
|
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
|
-
|
|
8527
|
-
|
|
8528
|
-
validSessionToken,
|
|
8529
|
-
authMiddleware
|
|
9233
|
+
getAuthState,
|
|
9234
|
+
authRequiredMiddleware
|
|
8530
9235
|
};
|
|
8531
9236
|
});
|
|
8532
9237
|
});
|
|
8533
|
-
var AuthMiddleware = class extends
|
|
9238
|
+
var AuthMiddleware = class extends Context40.Tag("AuthMiddleware")() {
|
|
8534
9239
|
static {
|
|
8535
|
-
this.Live =
|
|
9240
|
+
this.Live = Layer42.effect(this, LayerImpl31);
|
|
8536
9241
|
}
|
|
8537
9242
|
};
|
|
8538
9243
|
|
|
8539
|
-
// src/server/hono/
|
|
8540
|
-
import { zValidator } from "@hono/zod-validator";
|
|
8541
|
-
import { Effect as
|
|
8542
|
-
import {
|
|
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
|
|
9251
|
+
import z28 from "zod";
|
|
8677
9252
|
|
|
8678
9253
|
// src/lib/i18n/schema.ts
|
|
8679
|
-
import
|
|
8680
|
-
var localeSchema =
|
|
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 =
|
|
8684
|
-
hideNoUserMessageSession:
|
|
8685
|
-
unifySameTitleSession:
|
|
8686
|
-
enterKeyBehavior:
|
|
8687
|
-
permissionMode:
|
|
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:
|
|
8690
|
-
searchHotkey:
|
|
8691
|
-
autoScheduleContinueOnRateLimit:
|
|
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/
|
|
8741
|
-
|
|
8742
|
-
|
|
8743
|
-
|
|
8744
|
-
|
|
8745
|
-
|
|
8746
|
-
|
|
8747
|
-
|
|
8748
|
-
|
|
8749
|
-
|
|
8750
|
-
|
|
8751
|
-
const
|
|
8752
|
-
const
|
|
8753
|
-
|
|
8754
|
-
|
|
8755
|
-
|
|
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 !==
|
|
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("/
|
|
9347
|
+
).post("/logout", async (c) => {
|
|
8805
9348
|
deleteCookie(c, "ccv-session", { path: "/" });
|
|
8806
9349
|
return c.json({ success: true });
|
|
8807
|
-
}).get("/
|
|
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
|
-
})
|
|
8812
|
-
|
|
8813
|
-
|
|
8814
|
-
|
|
8815
|
-
|
|
8816
|
-
|
|
8817
|
-
|
|
8818
|
-
|
|
8819
|
-
|
|
8820
|
-
|
|
8821
|
-
|
|
8822
|
-
|
|
8823
|
-
|
|
8824
|
-
|
|
8825
|
-
|
|
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
|
-
|
|
9470
|
+
claudeCodeController.getClaudeCodeMeta().pipe(Effect54.provide(runtime))
|
|
8829
9471
|
);
|
|
8830
9472
|
return response;
|
|
8831
|
-
}).get(
|
|
8832
|
-
|
|
8833
|
-
|
|
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
|
-
|
|
8838
|
-
...
|
|
8839
|
-
|
|
8840
|
-
|
|
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
|
-
"/
|
|
8846
|
-
|
|
9521
|
+
"/session-processes/:sessionProcessId/continue",
|
|
9522
|
+
zValidator2(
|
|
8847
9523
|
"json",
|
|
8848
9524
|
z31.object({
|
|
8849
|
-
|
|
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
|
-
|
|
8856
|
-
...c.req.
|
|
8857
|
-
|
|
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
|
-
).
|
|
8862
|
-
|
|
8863
|
-
|
|
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
|
|
8879
|
-
|
|
8880
|
-
|
|
9547
|
+
const { sessionProcessId } = c.req.param();
|
|
9548
|
+
void Effect54.runFork(
|
|
9549
|
+
claudeCodeLifeCycleService.abortTask(sessionProcessId)
|
|
8881
9550
|
);
|
|
8882
|
-
return
|
|
9551
|
+
return c.json({ message: "Task aborted" });
|
|
8883
9552
|
}
|
|
8884
|
-
).
|
|
8885
|
-
|
|
8886
|
-
|
|
8887
|
-
|
|
8888
|
-
|
|
8889
|
-
|
|
8890
|
-
|
|
8891
|
-
|
|
8892
|
-
|
|
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
|
-
|
|
8899
|
-
|
|
8900
|
-
|
|
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
|
-
)
|
|
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
|
-
|
|
8910
|
-
...c.req.param()
|
|
8911
|
-
}).pipe(Effect51.provide(runtime))
|
|
9583
|
+
featureFlagController.getFlags().pipe(Effect55.provide(runtime))
|
|
8912
9584
|
);
|
|
8913
9585
|
return response;
|
|
8914
|
-
})
|
|
8915
|
-
|
|
8916
|
-
|
|
8917
|
-
|
|
8918
|
-
|
|
8919
|
-
|
|
8920
|
-
|
|
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
|
-
|
|
8927
|
-
...c.req.
|
|
8928
|
-
|
|
8929
|
-
}).pipe(Effect51.provide(runtime))
|
|
9608
|
+
fileSystemController.getFileCompletionRoute({
|
|
9609
|
+
...c.req.valid("query")
|
|
9610
|
+
})
|
|
8930
9611
|
);
|
|
8931
9612
|
return response;
|
|
8932
9613
|
}
|
|
8933
|
-
).
|
|
8934
|
-
"/
|
|
8935
|
-
|
|
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
|
-
|
|
8940
|
-
...c.req.
|
|
8941
|
-
|
|
8942
|
-
}).pipe(Effect51.provide(runtime))
|
|
9626
|
+
fileSystemController.getDirectoryListingRoute({
|
|
9627
|
+
...c.req.valid("query")
|
|
9628
|
+
})
|
|
8943
9629
|
);
|
|
8944
9630
|
return response;
|
|
8945
9631
|
}
|
|
8946
|
-
)
|
|
8947
|
-
|
|
8948
|
-
|
|
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
|
-
|
|
9749
|
+
projectController.getProject({
|
|
8953
9750
|
...c.req.param(),
|
|
8954
|
-
...c.req.valid("
|
|
8955
|
-
}).pipe(
|
|
9751
|
+
...c.req.valid("query")
|
|
9752
|
+
}).pipe(Effect57.provide(runtime))
|
|
8956
9753
|
);
|
|
8957
9754
|
return response;
|
|
8958
9755
|
}
|
|
8959
9756
|
).post(
|
|
8960
|
-
"/
|
|
8961
|
-
|
|
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
|
-
|
|
8966
|
-
...c.req.param(),
|
|
9767
|
+
projectController.createProject({
|
|
8967
9768
|
...c.req.valid("json")
|
|
8968
|
-
}).pipe(
|
|
9769
|
+
}).pipe(Effect57.provide(runtime))
|
|
8969
9770
|
);
|
|
8970
9771
|
return response;
|
|
8971
9772
|
}
|
|
8972
|
-
).get("
|
|
9773
|
+
).get("/:projectId/latest-session", async (c) => {
|
|
8973
9774
|
const response = await effectToResponse(
|
|
8974
9775
|
c,
|
|
8975
|
-
|
|
9776
|
+
projectController.getProjectLatestSession({
|
|
8976
9777
|
...c.req.param()
|
|
8977
|
-
}).pipe(
|
|
9778
|
+
}).pipe(Effect57.provide(runtime))
|
|
8978
9779
|
);
|
|
8979
9780
|
return response;
|
|
8980
|
-
}).get("
|
|
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
|
-
|
|
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("/
|
|
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
|
-
|
|
9794
|
+
sessionController.exportSessionHtml({ projectId, sessionId }).pipe(Effect57.provide(runtime))
|
|
8992
9795
|
);
|
|
8993
9796
|
return response;
|
|
8994
|
-
}).
|
|
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
|
-
|
|
9802
|
+
sessionController.deleteSession({ projectId, sessionId }).pipe(Effect57.provide(runtime))
|
|
8998
9803
|
);
|
|
8999
9804
|
return response;
|
|
9000
|
-
}).get("/
|
|
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
|
-
|
|
9810
|
+
agentSessionController.getAgentSession({ projectId, agentId })
|
|
9004
9811
|
);
|
|
9005
9812
|
return response;
|
|
9006
|
-
}).
|
|
9007
|
-
|
|
9008
|
-
|
|
9009
|
-
|
|
9010
|
-
|
|
9011
|
-
|
|
9012
|
-
|
|
9013
|
-
|
|
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
|
-
|
|
9020
|
-
|
|
9021
|
-
|
|
9842
|
+
fileSystemController.getFileContentRoute({
|
|
9843
|
+
projectId,
|
|
9844
|
+
filePath
|
|
9845
|
+
}).pipe(Effect57.provide(runtime))
|
|
9022
9846
|
);
|
|
9023
9847
|
return response;
|
|
9024
9848
|
}
|
|
9025
|
-
).
|
|
9026
|
-
"
|
|
9027
|
-
|
|
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
|
-
|
|
9030
|
-
|
|
9031
|
-
|
|
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
|
-
|
|
9039
|
-
|
|
9871
|
+
gitController.getGitDiff({
|
|
9872
|
+
projectId,
|
|
9040
9873
|
...c.req.valid("json")
|
|
9041
|
-
}).pipe(
|
|
9874
|
+
}).pipe(Effect57.provide(runtime))
|
|
9042
9875
|
);
|
|
9043
9876
|
return response;
|
|
9044
9877
|
}
|
|
9045
9878
|
).post(
|
|
9046
|
-
"/
|
|
9047
|
-
|
|
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
|
-
|
|
9068
|
-
|
|
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
|
-
).
|
|
9074
|
-
|
|
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
|
-
|
|
9896
|
+
gitController.pushCommits({
|
|
9897
|
+
projectId
|
|
9898
|
+
}).pipe(Effect57.provide(runtime))
|
|
9089
9899
|
);
|
|
9090
9900
|
return response;
|
|
9091
9901
|
}).post(
|
|
9092
|
-
"/
|
|
9093
|
-
|
|
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
|
-
|
|
9110
|
-
|
|
9111
|
-
|
|
9112
|
-
}).pipe(
|
|
9908
|
+
gitController.commitAndPush({
|
|
9909
|
+
projectId,
|
|
9910
|
+
...c.req.valid("json")
|
|
9911
|
+
}).pipe(Effect57.provide(runtime))
|
|
9113
9912
|
);
|
|
9114
9913
|
return response;
|
|
9115
9914
|
}
|
|
9116
|
-
).
|
|
9915
|
+
).get("/:projectId/git/branches", async (c) => {
|
|
9916
|
+
const projectId = c.req.param("projectId");
|
|
9117
9917
|
const response = await effectToResponse(
|
|
9118
9918
|
c,
|
|
9119
|
-
|
|
9120
|
-
|
|
9121
|
-
}).pipe(
|
|
9919
|
+
gitController.getBranches({
|
|
9920
|
+
projectId
|
|
9921
|
+
}).pipe(Effect57.provide(runtime))
|
|
9122
9922
|
);
|
|
9123
9923
|
return response;
|
|
9124
|
-
}).
|
|
9125
|
-
"/
|
|
9126
|
-
|
|
9127
|
-
"
|
|
9128
|
-
|
|
9129
|
-
|
|
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
|
-
|
|
9137
|
-
|
|
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
|
-
)
|
|
9143
|
-
|
|
9144
|
-
|
|
9145
|
-
|
|
9146
|
-
|
|
9147
|
-
|
|
9148
|
-
|
|
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
|
-
|
|
9155
|
-
|
|
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
|
-
).
|
|
9161
|
-
|
|
9162
|
-
|
|
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
|
-
|
|
9165
|
-
q:
|
|
9166
|
-
limit:
|
|
9167
|
-
projectId:
|
|
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(
|
|
10013
|
+
searchController.search({ query: q, limit, projectId }).pipe(Effect59.provide(runtime))
|
|
9175
10014
|
);
|
|
9176
10015
|
return response;
|
|
9177
10016
|
}
|
|
9178
|
-
)
|
|
9179
|
-
|
|
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
|
-
|
|
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
|
-
|
|
9184
|
-
|
|
9185
|
-
|
|
9186
|
-
|
|
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
|
-
|
|
9189
|
-
projectId:
|
|
9190
|
-
sessionId:
|
|
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
|
-
|
|
9200
|
-
status
|
|
10066
|
+
Effect61.map((tasks) => ({
|
|
10067
|
+
status,
|
|
9201
10068
|
response: tasks
|
|
9202
10069
|
})),
|
|
9203
|
-
|
|
10070
|
+
Effect61.provide(runtime)
|
|
9204
10071
|
)
|
|
9205
10072
|
);
|
|
9206
10073
|
return response;
|
|
9207
10074
|
}
|
|
9208
10075
|
).post(
|
|
9209
|
-
"/
|
|
9210
|
-
|
|
10076
|
+
"/",
|
|
10077
|
+
zValidator7(
|
|
9211
10078
|
"query",
|
|
9212
|
-
|
|
9213
|
-
projectId:
|
|
9214
|
-
sessionId:
|
|
10079
|
+
z36.object({
|
|
10080
|
+
projectId: z36.string(),
|
|
10081
|
+
sessionId: z36.string().optional()
|
|
9215
10082
|
})
|
|
9216
10083
|
),
|
|
9217
|
-
|
|
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
|
-
|
|
9226
|
-
status
|
|
10093
|
+
Effect61.map((task) => ({
|
|
10094
|
+
status,
|
|
9227
10095
|
response: task
|
|
9228
10096
|
})),
|
|
9229
|
-
|
|
10097
|
+
Effect61.provide(runtime)
|
|
9230
10098
|
)
|
|
9231
10099
|
);
|
|
9232
10100
|
return response;
|
|
9233
10101
|
}
|
|
9234
10102
|
).patch(
|
|
9235
|
-
"
|
|
9236
|
-
|
|
10103
|
+
"/:taskId",
|
|
10104
|
+
zValidator7(
|
|
9237
10105
|
"query",
|
|
9238
|
-
|
|
9239
|
-
projectId:
|
|
9240
|
-
sessionId:
|
|
10106
|
+
z36.object({
|
|
10107
|
+
projectId: z36.string(),
|
|
10108
|
+
sessionId: z36.string().optional()
|
|
9241
10109
|
})
|
|
9242
10110
|
),
|
|
9243
|
-
|
|
10111
|
+
zValidator7("json", TaskUpdateSchema.omit({ taskId: true })),
|
|
9244
10112
|
async (c) => {
|
|
9245
|
-
const {
|
|
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
|
|
9252
|
-
|
|
9253
|
-
status
|
|
10120
|
+
tasksController.updateTask(projectPath, { ...body, taskId }, sessionId).pipe(
|
|
10121
|
+
Effect61.map((task) => ({
|
|
10122
|
+
status,
|
|
9254
10123
|
response: task
|
|
9255
10124
|
})),
|
|
9256
|
-
|
|
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
|
|
9267
|
-
var platformLayer =
|
|
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
|
-
|
|
9275
|
-
|
|
9276
|
-
|
|
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.
|
|
10321
|
+
const isDevelopment = isDevelopmentEnv(process.env.CCV_ENV);
|
|
9282
10322
|
if (!isDevelopment) {
|
|
9283
|
-
const staticPath =
|
|
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
|
|
10335
|
+
const html = await readFile3(resolve7(staticPath, "index.html"), "utf-8");
|
|
9296
10336
|
return c.html(html);
|
|
9297
10337
|
});
|
|
9298
10338
|
}
|
|
9299
|
-
const
|
|
9300
|
-
|
|
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
|
-
|
|
9310
|
-
|
|
9311
|
-
|
|
9312
|
-
|
|
9313
|
-
|
|
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 =
|
|
9321
|
-
var InfraBasics =
|
|
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 =
|
|
10367
|
+
var InfraRepos = Layer44.mergeAll(
|
|
9327
10368
|
ProjectRepository.Live,
|
|
9328
10369
|
SessionRepository.Live
|
|
9329
|
-
).pipe(
|
|
9330
|
-
var InfraLayer = AgentSessionLayer.pipe(
|
|
9331
|
-
var DomainBase =
|
|
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
|
-
|
|
10383
|
+
Layer44.provideMerge(DomainBase)
|
|
9343
10384
|
);
|
|
9344
|
-
var AppServices =
|
|
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
|
-
|
|
10392
|
+
Layer44.provideMerge(AppServices)
|
|
9351
10393
|
);
|
|
9352
|
-
var PresentationLayer =
|
|
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
|
-
|
|
9369
|
-
|
|
9370
|
-
|
|
9371
|
-
|
|
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").
|
|
9378
|
-
|
|
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 () => {
|