@sanqianx/claude-ai-workbench 0.13.2 → 0.13.4
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/LICENSE +201 -201
- package/README.md +156 -156
- package/apps/agent-terminal/app.css +652 -613
- package/apps/agent-terminal/app.js +143 -67
- package/apps/agent-terminal/conversation-composer.js +44 -0
- package/apps/agent-terminal/index.html +172 -170
- package/apps/agent-terminal/panel-host-proof.html +152 -152
- package/apps/agent-terminal/panel-proof-content.html +41 -41
- package/apps/agent-terminal/right-workspace-dock.css +189 -159
- package/apps/agent-terminal/right-workspace-dock.js +652 -521
- package/apps/agent-terminal/user-question-card.js +68 -0
- package/apps/ai-profile-settings-host/index.html +25 -25
- package/apps/ai-profile-settings-host/page.css +1 -1
- package/apps/ai-profile-settings-host/page.js +113 -113
- package/apps/minimal-test-host/README.md +15 -15
- package/apps/minimal-test-host/host.css +2 -2
- package/apps/minimal-test-host/host.js +35 -35
- package/apps/minimal-test-host/index.html +58 -58
- package/apps/minimal-test-host/optional-mock/fault-injection.js +15 -15
- package/docs/sdk/FILE_LAYOUT.md +31 -31
- package/docs/sdk/INTEGRATION.md +126 -126
- package/docs/sdk/README.md +21 -21
- package/docs/sdk/SESSION_EVENTS.md +15 -13
- package/docs/sdk/openapi.yaml +16 -1
- package/package.json +1 -1
- package/packages/client/README.md +29 -29
- package/packages/client/index.d.ts +122 -122
- package/packages/client/index.js +260 -260
- package/packages/client/package.json +2 -2
- package/packages/contracts/README.md +9 -9
- package/packages/contracts/index.d.ts +519 -517
- package/packages/contracts/index.js +312 -312
- package/packages/contracts/package.json +1 -1
- package/packages/core/README.md +8 -8
- package/packages/core/index.d.ts +76 -76
- package/packages/core/index.js +222 -222
- package/packages/core/package.json +2 -2
- package/packages/server/README.md +17 -17
- package/packages/server/bin/agent-terminal-server.js +42 -42
- package/packages/server/bin/claude-workbench.js +225 -225
- package/packages/server/index.d.ts +171 -171
- package/packages/server/index.js +230 -230
- package/packages/server/lib/agent-discovery.js +133 -133
- package/packages/server/lib/agent-registry.js +143 -143
- package/packages/server/lib/agent-spawn-env.js +17 -17
- package/packages/server/lib/attachment-store.js +78 -78
- package/packages/server/lib/bridges/app-server-bridge.js +373 -373
- package/packages/server/lib/bridges/opencode-server-bridge.js +506 -506
- package/packages/server/lib/capture-settings-store.js +35 -35
- package/packages/server/lib/claude-history-recovery.js +87 -87
- package/packages/server/lib/cli-session-recovery.js +28 -28
- package/packages/server/lib/context-resolver.js +55 -55
- package/packages/server/lib/conversation-capture.js +51 -51
- package/packages/server/lib/delegation-phase-store.js +357 -357
- package/packages/server/lib/delegation-store.js +474 -468
- package/packages/server/lib/diagnostics.js +32 -32
- package/packages/server/lib/drivers/cli-json-runner.js +974 -974
- package/packages/server/lib/drivers/codex-driver.js +1461 -1442
- package/packages/server/lib/drivers/opencode-driver.js +1554 -1554
- package/packages/server/lib/drivers/zcode-driver.js +1250 -1250
- package/packages/server/lib/drivers/zcode-profile-runtime.js +71 -71
- package/packages/server/lib/event-stream.js +138 -138
- package/packages/server/lib/folder-picker.js +623 -623
- package/packages/server/lib/http-handler.js +339 -339
- package/packages/server/lib/json-file.js +26 -26
- package/packages/server/lib/knowledge-context.js +33 -33
- package/packages/server/lib/model-detection.js +295 -295
- package/packages/server/lib/parent-context.js +44 -0
- package/packages/server/lib/profile-store.js +283 -283
- package/packages/server/lib/project-store.js +102 -102
- package/packages/server/lib/runtime-loader.js +120 -120
- package/packages/server/lib/session-archive-store.js +53 -53
- package/packages/server/lib/session-history.js +612 -612
- package/packages/server/lib/system-doctor.js +125 -125
- package/packages/server/lib/ui-preferences-store.js +48 -48
- package/packages/server/lib/user-questions.js +67 -0
- package/packages/server/lib/workbench-service.js +1696 -1675
- package/packages/server/package.json +2 -2
- package/packages/ui/README.md +30 -30
- package/packages/ui/ai-profile-settings.d.ts +45 -45
- package/packages/ui/ai-profile-settings.js +283 -283
- package/packages/ui/claude-terminal.d.ts +31 -31
- package/packages/ui/claude-terminal.js +343 -343
- package/packages/ui/index.d.ts +18 -18
- package/packages/ui/index.js +51 -51
- package/packages/ui/package.json +2 -2
- package/packages/ui/theme.css +13 -13
- package/src/assets/claude-prompts.json +21 -21
- package/src/backend/http/ai-profile-routes.js +61 -61
- package/src/backend/http/claude-routes.js +170 -170
- package/src/backend/http/node-helpers.js +45 -45
- package/src/backend/index.js +21 -21
- package/src/backend/lib/ai-adapter.js +144 -144
- package/src/backend/lib/ai-profile-store.js +259 -259
- package/src/backend/lib/ai-vendor-presets.js +83 -83
- package/src/backend/lib/ai-workspace.js +140 -140
- package/src/backend/lib/automation-config.js +253 -253
- package/src/backend/lib/claude-cli-runner.js +2408 -2392
- package/src/backend/lib/data-dir.js +169 -169
- package/src/backend/lib/llm-client.js +176 -176
- package/src/backend/lib/model-context-windows.js +82 -82
- package/src/backend/lib/native-task-control.js +217 -217
- package/src/backend/lib/native-task-history-index.js +475 -475
- package/src/backend/lib/native-task-history-reader.js +526 -526
- package/src/backend/lib/native-task-messages.js +457 -457
- package/src/backend/lib/native-task-registry.js +325 -325
- package/src/backend/lib/native-task-runtime.js +242 -242
- package/src/backend/lib/native-task-trace.js +440 -440
- package/src/backend/lib/native-task-transcript.js +393 -393
- package/src/backend/lib/prompt-registry.js +166 -166
- package/src/backend/lib/runtime-config.js +121 -121
- package/src/backend/lib/sdk-session-stream.js +143 -143
- package/src/backend/lib/sdk-turn-stream.js +16 -16
- package/src/backend/lib/store-scope.js +131 -131
- package/src/backend/lib/task-identity.js +104 -104
- package/src/backend/lib/task-state-projection.js +58 -58
- package/src/index.js +7 -7
- package/src/sdk-wave-a.ts +1 -1
- package/src/sdk-wave-b.ts +1 -1
- package/src/sdk-wave-c.ts +1 -1
- package/src/sdk-wave-d.ts +1 -1
- package/src/sdk-wave-e.ts +1 -1
|
@@ -1,170 +1,170 @@
|
|
|
1
|
-
const { sendJson, readBody, writeSseHeaders, writeSse } = require('./node-helpers');
|
|
2
|
-
|
|
3
|
-
function sessionIdFromPath(pathname, basePath) {
|
|
4
|
-
const rest = pathname.slice(basePath.length).replace(/^\/+/, '');
|
|
5
|
-
const parts = rest.split('/');
|
|
6
|
-
return parts[0] === 'sessions' ? parts[1] || '' : '';
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
function ensureRunner(runner) {
|
|
10
|
-
if (!runner || typeof runner.listSessions !== 'function') {
|
|
11
|
-
throw new Error('Claude runner is not available');
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
async function handleClaudeRoutes(req, res, options = {}) {
|
|
16
|
-
const runner = options.runner;
|
|
17
|
-
const basePath = options.basePath || '/api/claude';
|
|
18
|
-
const url = options.url || new URL(req.url, 'http://127.0.0.1');
|
|
19
|
-
const pathname = url.pathname;
|
|
20
|
-
if (pathname !== basePath && !pathname.startsWith(`${basePath}/`)) return false;
|
|
21
|
-
|
|
22
|
-
try {
|
|
23
|
-
ensureRunner(runner);
|
|
24
|
-
} catch (error) {
|
|
25
|
-
sendJson(res, 503, { ok: false, error: error.message });
|
|
26
|
-
return true;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (req.method === 'GET' && pathname === `${basePath}/sessions`) {
|
|
30
|
-
const projectSlug = url.searchParams.get('slug') || url.searchParams.get('projectSlug') || null;
|
|
31
|
-
sendJson(res, 200, { ok: true, sessions: runner.listSessions({ projectSlug }) });
|
|
32
|
-
return true;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (req.method === 'GET' && pathname === `${basePath}/sessions-stream`) {
|
|
36
|
-
writeSseHeaders(res);
|
|
37
|
-
writeSse(res, 'claude/snapshot', { sessions: runner.listSessions({}) });
|
|
38
|
-
const unsubscribe = runner.subscribeList(event => {
|
|
39
|
-
try { writeSse(res, 'claude/sessions-changed', event); } catch {}
|
|
40
|
-
});
|
|
41
|
-
const heartbeat = setInterval(() => {
|
|
42
|
-
try { res.write(`: keepalive ${Date.now()}\n\n`); } catch {}
|
|
43
|
-
}, 15000);
|
|
44
|
-
const cleanup = () => {
|
|
45
|
-
clearInterval(heartbeat);
|
|
46
|
-
try { unsubscribe(); } catch {}
|
|
47
|
-
};
|
|
48
|
-
req.on('close', cleanup);
|
|
49
|
-
req.on('error', cleanup);
|
|
50
|
-
return true;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (req.method === 'POST' && pathname.startsWith(`${basePath}/projects/`) && pathname.endsWith('/start')) {
|
|
54
|
-
const slug = pathname.slice(`${basePath}/projects/`.length).replace(/\/start$/, '');
|
|
55
|
-
const resolveProject = options.resolveProject || options.projectResolver;
|
|
56
|
-
if (typeof resolveProject !== 'function') {
|
|
57
|
-
sendJson(res, 500, { ok: false, error: 'resolveProject option is required' });
|
|
58
|
-
return true;
|
|
59
|
-
}
|
|
60
|
-
const project = await resolveProject(slug, req);
|
|
61
|
-
if (!project) {
|
|
62
|
-
sendJson(res, 404, { ok: false, error: `project not found: ${slug}` });
|
|
63
|
-
return true;
|
|
64
|
-
}
|
|
65
|
-
const started = runner.startChatSession({
|
|
66
|
-
slug: project.slug || slug,
|
|
67
|
-
projectPath: project.projectPath || project.gitPath || project.localPath || project.path,
|
|
68
|
-
kbPath: project.kbPath || project.workPath || project.projectPath || project.path,
|
|
69
|
-
aiProfile: project.aiProfile || null,
|
|
70
|
-
permissionMode: project.permissionMode || 'default',
|
|
71
|
-
});
|
|
72
|
-
sendJson(res, 200, { ok: true, slug, ...started, aiProfileId: project.aiProfile && project.aiProfile.id || null });
|
|
73
|
-
return true;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const sessionId = sessionIdFromPath(pathname, basePath);
|
|
77
|
-
if (!sessionId) {
|
|
78
|
-
sendJson(res, 404, { ok: false, error: 'unknown Claude route' });
|
|
79
|
-
return true;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (req.method === 'GET' && pathname === `${basePath}/sessions/${sessionId}`) {
|
|
83
|
-
const state = runner.getState(sessionId);
|
|
84
|
-
if (!state) sendJson(res, 404, { ok: false, error: 'session not found' });
|
|
85
|
-
else sendJson(res, 200, { ok: true, ...state });
|
|
86
|
-
return true;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (req.method === 'GET' && pathname === `${basePath}/sessions/${sessionId}/token-usage`) {
|
|
90
|
-
if (!runner.getSession(sessionId)) sendJson(res, 404, { ok: false, error: 'session not found' });
|
|
91
|
-
else sendJson(res, 200, { ok: true, sessionId, ...runner.getSessionTokenUsage(sessionId) });
|
|
92
|
-
return true;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (req.method === 'GET' && pathname === `${basePath}/sessions/${sessionId}/stream`) {
|
|
96
|
-
if (!runner.getSession(sessionId)) {
|
|
97
|
-
sendJson(res, 404, { ok: false, error: 'session not found' });
|
|
98
|
-
return true;
|
|
99
|
-
}
|
|
100
|
-
writeSseHeaders(res);
|
|
101
|
-
writeSse(res, 'claude/hello', { sessionId, time: new Date().toISOString() });
|
|
102
|
-
const session = runner.getSession(sessionId);
|
|
103
|
-
const events = Array.isArray(session && session.outputBuffer) ? session.outputBuffer : [];
|
|
104
|
-
for (const event of events.slice(-1000)) {
|
|
105
|
-
if (event && event.type) writeSse(res, event.type, event);
|
|
106
|
-
}
|
|
107
|
-
const unsubscribe = runner.subscribe(sessionId, event => {
|
|
108
|
-
try { writeSse(res, event.type, event); } catch {}
|
|
109
|
-
});
|
|
110
|
-
const heartbeat = setInterval(() => {
|
|
111
|
-
try { res.write(`: keepalive ${Date.now()}\n\n`); } catch {}
|
|
112
|
-
}, 15000);
|
|
113
|
-
const cleanup = () => {
|
|
114
|
-
clearInterval(heartbeat);
|
|
115
|
-
try { unsubscribe(); } catch {}
|
|
116
|
-
};
|
|
117
|
-
req.on('close', cleanup);
|
|
118
|
-
req.on('error', cleanup);
|
|
119
|
-
return true;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (req.method === 'POST' && pathname === `${basePath}/sessions/${sessionId}/input`) {
|
|
123
|
-
if (!runner.getSession(sessionId)) {
|
|
124
|
-
sendJson(res, 404, { ok: false, error: 'session not found' });
|
|
125
|
-
return true;
|
|
126
|
-
}
|
|
127
|
-
const body = await readBody(req);
|
|
128
|
-
if (!body || typeof body.text !== 'string' || !body.text.trim()) {
|
|
129
|
-
sendJson(res, 400, { ok: false, error: 'body.text is required' });
|
|
130
|
-
return true;
|
|
131
|
-
}
|
|
132
|
-
const resolveProfile = options.resolveProfile || options.profileResolver;
|
|
133
|
-
const profile = typeof resolveProfile === 'function'
|
|
134
|
-
? await resolveProfile(runner.getState(sessionId), body, req)
|
|
135
|
-
: null;
|
|
136
|
-
const result = await runner.sendInput(sessionId, body.text, profile, {
|
|
137
|
-
permissionMode: typeof body.permissionMode === 'string' ? body.permissionMode : undefined,
|
|
138
|
-
allowedTools: Array.isArray(body.allowedTools) ? body.allowedTools : undefined,
|
|
139
|
-
});
|
|
140
|
-
sendJson(res, 200, { ok: true, sessionId, pendingPermission: result && result.pendingPermission || null, aiProfileId: profile && profile.id || null });
|
|
141
|
-
return true;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (req.method === 'POST' && pathname === `${basePath}/sessions/${sessionId}/permission`) {
|
|
145
|
-
const body = await readBody(req);
|
|
146
|
-
if (!body || typeof body.requestId !== 'string') {
|
|
147
|
-
sendJson(res, 400, { ok: false, error: 'body.requestId is required' });
|
|
148
|
-
return true;
|
|
149
|
-
}
|
|
150
|
-
const result = runner.resolvePermission(sessionId, body.requestId, {
|
|
151
|
-
allow: body.allow === true,
|
|
152
|
-
message: typeof body.message === 'string' ? body.message : '',
|
|
153
|
-
});
|
|
154
|
-
sendJson(res, 200, { ok: true, sessionId, ...result });
|
|
155
|
-
return true;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (req.method === 'POST' && pathname === `${basePath}/sessions/${sessionId}/abort`) {
|
|
159
|
-
runner.abort(sessionId);
|
|
160
|
-
sendJson(res, 200, { ok: true, sessionId });
|
|
161
|
-
return true;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
sendJson(res, 404, { ok: false, error: 'unknown Claude route' });
|
|
165
|
-
return true;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
module.exports = {
|
|
169
|
-
handleClaudeRoutes,
|
|
170
|
-
};
|
|
1
|
+
const { sendJson, readBody, writeSseHeaders, writeSse } = require('./node-helpers');
|
|
2
|
+
|
|
3
|
+
function sessionIdFromPath(pathname, basePath) {
|
|
4
|
+
const rest = pathname.slice(basePath.length).replace(/^\/+/, '');
|
|
5
|
+
const parts = rest.split('/');
|
|
6
|
+
return parts[0] === 'sessions' ? parts[1] || '' : '';
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function ensureRunner(runner) {
|
|
10
|
+
if (!runner || typeof runner.listSessions !== 'function') {
|
|
11
|
+
throw new Error('Claude runner is not available');
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function handleClaudeRoutes(req, res, options = {}) {
|
|
16
|
+
const runner = options.runner;
|
|
17
|
+
const basePath = options.basePath || '/api/claude';
|
|
18
|
+
const url = options.url || new URL(req.url, 'http://127.0.0.1');
|
|
19
|
+
const pathname = url.pathname;
|
|
20
|
+
if (pathname !== basePath && !pathname.startsWith(`${basePath}/`)) return false;
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
ensureRunner(runner);
|
|
24
|
+
} catch (error) {
|
|
25
|
+
sendJson(res, 503, { ok: false, error: error.message });
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (req.method === 'GET' && pathname === `${basePath}/sessions`) {
|
|
30
|
+
const projectSlug = url.searchParams.get('slug') || url.searchParams.get('projectSlug') || null;
|
|
31
|
+
sendJson(res, 200, { ok: true, sessions: runner.listSessions({ projectSlug }) });
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (req.method === 'GET' && pathname === `${basePath}/sessions-stream`) {
|
|
36
|
+
writeSseHeaders(res);
|
|
37
|
+
writeSse(res, 'claude/snapshot', { sessions: runner.listSessions({}) });
|
|
38
|
+
const unsubscribe = runner.subscribeList(event => {
|
|
39
|
+
try { writeSse(res, 'claude/sessions-changed', event); } catch {}
|
|
40
|
+
});
|
|
41
|
+
const heartbeat = setInterval(() => {
|
|
42
|
+
try { res.write(`: keepalive ${Date.now()}\n\n`); } catch {}
|
|
43
|
+
}, 15000);
|
|
44
|
+
const cleanup = () => {
|
|
45
|
+
clearInterval(heartbeat);
|
|
46
|
+
try { unsubscribe(); } catch {}
|
|
47
|
+
};
|
|
48
|
+
req.on('close', cleanup);
|
|
49
|
+
req.on('error', cleanup);
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (req.method === 'POST' && pathname.startsWith(`${basePath}/projects/`) && pathname.endsWith('/start')) {
|
|
54
|
+
const slug = pathname.slice(`${basePath}/projects/`.length).replace(/\/start$/, '');
|
|
55
|
+
const resolveProject = options.resolveProject || options.projectResolver;
|
|
56
|
+
if (typeof resolveProject !== 'function') {
|
|
57
|
+
sendJson(res, 500, { ok: false, error: 'resolveProject option is required' });
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
const project = await resolveProject(slug, req);
|
|
61
|
+
if (!project) {
|
|
62
|
+
sendJson(res, 404, { ok: false, error: `project not found: ${slug}` });
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
const started = runner.startChatSession({
|
|
66
|
+
slug: project.slug || slug,
|
|
67
|
+
projectPath: project.projectPath || project.gitPath || project.localPath || project.path,
|
|
68
|
+
kbPath: project.kbPath || project.workPath || project.projectPath || project.path,
|
|
69
|
+
aiProfile: project.aiProfile || null,
|
|
70
|
+
permissionMode: project.permissionMode || 'default',
|
|
71
|
+
});
|
|
72
|
+
sendJson(res, 200, { ok: true, slug, ...started, aiProfileId: project.aiProfile && project.aiProfile.id || null });
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const sessionId = sessionIdFromPath(pathname, basePath);
|
|
77
|
+
if (!sessionId) {
|
|
78
|
+
sendJson(res, 404, { ok: false, error: 'unknown Claude route' });
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (req.method === 'GET' && pathname === `${basePath}/sessions/${sessionId}`) {
|
|
83
|
+
const state = runner.getState(sessionId);
|
|
84
|
+
if (!state) sendJson(res, 404, { ok: false, error: 'session not found' });
|
|
85
|
+
else sendJson(res, 200, { ok: true, ...state });
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (req.method === 'GET' && pathname === `${basePath}/sessions/${sessionId}/token-usage`) {
|
|
90
|
+
if (!runner.getSession(sessionId)) sendJson(res, 404, { ok: false, error: 'session not found' });
|
|
91
|
+
else sendJson(res, 200, { ok: true, sessionId, ...runner.getSessionTokenUsage(sessionId) });
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (req.method === 'GET' && pathname === `${basePath}/sessions/${sessionId}/stream`) {
|
|
96
|
+
if (!runner.getSession(sessionId)) {
|
|
97
|
+
sendJson(res, 404, { ok: false, error: 'session not found' });
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
writeSseHeaders(res);
|
|
101
|
+
writeSse(res, 'claude/hello', { sessionId, time: new Date().toISOString() });
|
|
102
|
+
const session = runner.getSession(sessionId);
|
|
103
|
+
const events = Array.isArray(session && session.outputBuffer) ? session.outputBuffer : [];
|
|
104
|
+
for (const event of events.slice(-1000)) {
|
|
105
|
+
if (event && event.type) writeSse(res, event.type, event);
|
|
106
|
+
}
|
|
107
|
+
const unsubscribe = runner.subscribe(sessionId, event => {
|
|
108
|
+
try { writeSse(res, event.type, event); } catch {}
|
|
109
|
+
});
|
|
110
|
+
const heartbeat = setInterval(() => {
|
|
111
|
+
try { res.write(`: keepalive ${Date.now()}\n\n`); } catch {}
|
|
112
|
+
}, 15000);
|
|
113
|
+
const cleanup = () => {
|
|
114
|
+
clearInterval(heartbeat);
|
|
115
|
+
try { unsubscribe(); } catch {}
|
|
116
|
+
};
|
|
117
|
+
req.on('close', cleanup);
|
|
118
|
+
req.on('error', cleanup);
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (req.method === 'POST' && pathname === `${basePath}/sessions/${sessionId}/input`) {
|
|
123
|
+
if (!runner.getSession(sessionId)) {
|
|
124
|
+
sendJson(res, 404, { ok: false, error: 'session not found' });
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
const body = await readBody(req);
|
|
128
|
+
if (!body || typeof body.text !== 'string' || !body.text.trim()) {
|
|
129
|
+
sendJson(res, 400, { ok: false, error: 'body.text is required' });
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
const resolveProfile = options.resolveProfile || options.profileResolver;
|
|
133
|
+
const profile = typeof resolveProfile === 'function'
|
|
134
|
+
? await resolveProfile(runner.getState(sessionId), body, req)
|
|
135
|
+
: null;
|
|
136
|
+
const result = await runner.sendInput(sessionId, body.text, profile, {
|
|
137
|
+
permissionMode: typeof body.permissionMode === 'string' ? body.permissionMode : undefined,
|
|
138
|
+
allowedTools: Array.isArray(body.allowedTools) ? body.allowedTools : undefined,
|
|
139
|
+
});
|
|
140
|
+
sendJson(res, 200, { ok: true, sessionId, pendingPermission: result && result.pendingPermission || null, aiProfileId: profile && profile.id || null });
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (req.method === 'POST' && pathname === `${basePath}/sessions/${sessionId}/permission`) {
|
|
145
|
+
const body = await readBody(req);
|
|
146
|
+
if (!body || typeof body.requestId !== 'string') {
|
|
147
|
+
sendJson(res, 400, { ok: false, error: 'body.requestId is required' });
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
const result = runner.resolvePermission(sessionId, body.requestId, {
|
|
151
|
+
allow: body.allow === true,
|
|
152
|
+
message: typeof body.message === 'string' ? body.message : '',
|
|
153
|
+
});
|
|
154
|
+
sendJson(res, 200, { ok: true, sessionId, ...result });
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (req.method === 'POST' && pathname === `${basePath}/sessions/${sessionId}/abort`) {
|
|
159
|
+
runner.abort(sessionId);
|
|
160
|
+
sendJson(res, 200, { ok: true, sessionId });
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
sendJson(res, 404, { ok: false, error: 'unknown Claude route' });
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
module.exports = {
|
|
169
|
+
handleClaudeRoutes,
|
|
170
|
+
};
|
|
@@ -1,45 +1,45 @@
|
|
|
1
|
-
function sendJson(res, status, payload) {
|
|
2
|
-
res.writeHead(status, {
|
|
3
|
-
'Content-Type': 'application/json; charset=utf-8',
|
|
4
|
-
'Cache-Control': 'no-store',
|
|
5
|
-
});
|
|
6
|
-
res.end(JSON.stringify(payload));
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
function readBody(req) {
|
|
10
|
-
return new Promise((resolve, reject) => {
|
|
11
|
-
const chunks = [];
|
|
12
|
-
req.on('data', chunk => chunks.push(chunk));
|
|
13
|
-
req.on('end', () => {
|
|
14
|
-
if (!chunks.length) return resolve({});
|
|
15
|
-
const text = Buffer.concat(chunks).toString('utf8');
|
|
16
|
-
try {
|
|
17
|
-
resolve(JSON.parse(text));
|
|
18
|
-
} catch (error) {
|
|
19
|
-
reject(new Error(`invalid JSON body: ${error.message}`));
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
req.on('error', reject);
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function writeSseHeaders(res) {
|
|
27
|
-
res.writeHead(200, {
|
|
28
|
-
'Content-Type': 'text/event-stream',
|
|
29
|
-
'Cache-Control': 'no-store, no-transform',
|
|
30
|
-
Connection: 'keep-alive',
|
|
31
|
-
'X-Accel-Buffering': 'no',
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function writeSse(res, event, data) {
|
|
36
|
-
res.write(`event: ${event}\n`);
|
|
37
|
-
res.write(`data: ${JSON.stringify(data)}\n\n`);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
module.exports = {
|
|
41
|
-
sendJson,
|
|
42
|
-
readBody,
|
|
43
|
-
writeSseHeaders,
|
|
44
|
-
writeSse,
|
|
45
|
-
};
|
|
1
|
+
function sendJson(res, status, payload) {
|
|
2
|
+
res.writeHead(status, {
|
|
3
|
+
'Content-Type': 'application/json; charset=utf-8',
|
|
4
|
+
'Cache-Control': 'no-store',
|
|
5
|
+
});
|
|
6
|
+
res.end(JSON.stringify(payload));
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function readBody(req) {
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
const chunks = [];
|
|
12
|
+
req.on('data', chunk => chunks.push(chunk));
|
|
13
|
+
req.on('end', () => {
|
|
14
|
+
if (!chunks.length) return resolve({});
|
|
15
|
+
const text = Buffer.concat(chunks).toString('utf8');
|
|
16
|
+
try {
|
|
17
|
+
resolve(JSON.parse(text));
|
|
18
|
+
} catch (error) {
|
|
19
|
+
reject(new Error(`invalid JSON body: ${error.message}`));
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
req.on('error', reject);
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function writeSseHeaders(res) {
|
|
27
|
+
res.writeHead(200, {
|
|
28
|
+
'Content-Type': 'text/event-stream',
|
|
29
|
+
'Cache-Control': 'no-store, no-transform',
|
|
30
|
+
Connection: 'keep-alive',
|
|
31
|
+
'X-Accel-Buffering': 'no',
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function writeSse(res, event, data) {
|
|
36
|
+
res.write(`event: ${event}\n`);
|
|
37
|
+
res.write(`data: ${JSON.stringify(data)}\n\n`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = {
|
|
41
|
+
sendJson,
|
|
42
|
+
readBody,
|
|
43
|
+
writeSseHeaders,
|
|
44
|
+
writeSse,
|
|
45
|
+
};
|
package/src/backend/index.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
const runtimeConfig = require('./lib/runtime-config');
|
|
2
|
-
|
|
3
|
-
module.exports = {
|
|
4
|
-
...runtimeConfig,
|
|
5
|
-
dataDir: require('./lib/data-dir'),
|
|
6
|
-
aiWorkspace: require('./lib/ai-workspace'),
|
|
7
|
-
automationConfig: require('./lib/automation-config'),
|
|
8
|
-
modelContextWindows: require('./lib/model-context-windows'),
|
|
9
|
-
llmClient: require('./lib/llm-client'),
|
|
10
|
-
promptRegistry: require('./lib/prompt-registry'),
|
|
11
|
-
aiVendorPresets: require('./lib/ai-vendor-presets'),
|
|
12
|
-
aiAdapter: require('./lib/ai-adapter'),
|
|
13
|
-
aiProfiles: require('./lib/ai-profile-store'),
|
|
14
|
-
createRunner() {
|
|
15
|
-
return require('./lib/claude-cli-runner');
|
|
16
|
-
},
|
|
17
|
-
http: {
|
|
18
|
-
...require('./http/claude-routes'),
|
|
19
|
-
...require('./http/ai-profile-routes'),
|
|
20
|
-
},
|
|
21
|
-
};
|
|
1
|
+
const runtimeConfig = require('./lib/runtime-config');
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
...runtimeConfig,
|
|
5
|
+
dataDir: require('./lib/data-dir'),
|
|
6
|
+
aiWorkspace: require('./lib/ai-workspace'),
|
|
7
|
+
automationConfig: require('./lib/automation-config'),
|
|
8
|
+
modelContextWindows: require('./lib/model-context-windows'),
|
|
9
|
+
llmClient: require('./lib/llm-client'),
|
|
10
|
+
promptRegistry: require('./lib/prompt-registry'),
|
|
11
|
+
aiVendorPresets: require('./lib/ai-vendor-presets'),
|
|
12
|
+
aiAdapter: require('./lib/ai-adapter'),
|
|
13
|
+
aiProfiles: require('./lib/ai-profile-store'),
|
|
14
|
+
createRunner() {
|
|
15
|
+
return require('./lib/claude-cli-runner');
|
|
16
|
+
},
|
|
17
|
+
http: {
|
|
18
|
+
...require('./http/claude-routes'),
|
|
19
|
+
...require('./http/ai-profile-routes'),
|
|
20
|
+
},
|
|
21
|
+
};
|