@surething/cockpit 1.0.216 → 1.0.217
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/.next-prod/BUILD_ID +1 -1
- package/.next-prod/app-path-routes-manifest.json +3 -3
- package/.next-prod/build-manifest.json +2 -2
- package/.next-prod/prerender-manifest.json +3 -3
- package/.next-prod/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next-prod/server/app/_global-error.html +1 -1
- package/.next-prod/server/app/_global-error.rsc +1 -1
- package/.next-prod/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next-prod/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/.next-prod/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/.next-prod/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next-prod/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next-prod/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next-prod/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next-prod/server/app/_not-found.html +1 -1
- package/.next-prod/server/app/_not-found.rsc +3 -3
- package/.next-prod/server/app/_not-found.segments/_full.segment.rsc +3 -3
- package/.next-prod/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next-prod/server/app/_not-found.segments/_index.segment.rsc +3 -3
- package/.next-prod/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next-prod/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next-prod/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next-prod/server/app/api/terminal/bubble-order/route.js +1 -1
- package/.next-prod/server/app/page_client-reference-manifest.js +1 -1
- package/.next-prod/server/app/project/page_client-reference-manifest.js +1 -1
- package/.next-prod/server/app/review/[id]/page_client-reference-manifest.js +1 -1
- package/.next-prod/server/app-paths-manifest.json +3 -3
- package/.next-prod/server/chunks/8916.js +1 -1
- package/.next-prod/server/chunks/9658.js +5 -5
- package/.next-prod/server/chunks/9877.js +1 -1
- package/.next-prod/server/functions-config-manifest.json +1 -0
- package/.next-prod/server/middleware-build-manifest.js +1 -1
- package/.next-prod/server/pages/404.html +1 -1
- package/.next-prod/server/pages/500.html +1 -1
- package/.next-prod/server/server-reference-manifest.json +1 -1
- package/.next-prod/static/chunks/6345-d477b8d5c682b1fb.js +14 -0
- package/.next-prod/static/chunks/6917-0a22d7764ca45244.js +29 -0
- package/.next-prod/static/chunks/app/{layout-a0362651ba6e6e6f.js → layout-8e3a54b794cb35b6.js} +1 -1
- package/.next-prod/static/chunks/app/{page-1b14cabf47df9ff7.js → page-3ab0a0f28cbdc8e2.js} +1 -1
- package/.next-prod/static/chunks/app/project/{page-1b14cabf47df9ff7.js → page-3ab0a0f28cbdc8e2.js} +1 -1
- package/.next-prod/static/css/fc2730c2dbe4866e.css +1 -0
- package/.next-prod/trace +13 -13
- package/.next-prod/trace-build +1 -1
- package/README.md +3 -2
- package/README.zh.md +3 -2
- package/bin/cock-codegraph.mjs +21 -6
- package/bin/cock-connection.mjs +151 -0
- package/bin/cock.mjs +12 -1
- package/bin/setup-dev.mjs +15 -13
- package/dist/{chunk-CZWJPTRO.mjs → chunk-GCYLMG43.mjs} +2486 -1047
- package/dist/chunk-O4P2J44N.mjs +314 -0
- package/dist/{chunk-KRTISG5I.mjs → chunk-W6G6X3FP.mjs} +196 -9
- package/dist/httpApi.mjs +75 -2
- package/dist/scheduledTasks.mjs +9 -1158
- package/dist/{server-OSOMFNXR.mjs → server-ZBUZ24TC.mjs} +4 -2
- package/dist/wsServer.mjs +24 -19
- package/package.json +1 -1
- package/server.mjs +5 -1
- package/.next-prod/static/chunks/5188-415582403ef0e29c.js +0 -29
- package/.next-prod/static/chunks/6345-e5ceeb2aeb698eb6.js +0 -14
- package/.next-prod/static/css/cc6d733cdf607b30.css +0 -1
- package/dist/chunk-ZJ6CC3MH.mjs +0 -223
- /package/.next-prod/static/{GAYKr2BmQpFqJgRJfvQ3D → 7pu1LXbRRLfg05VN3u39s}/_buildManifest.js +0 -0
- /package/.next-prod/static/{GAYKr2BmQpFqJgRJfvQ3D → 7pu1LXbRRLfg05VN3u39s}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import {
|
|
2
|
+
GLOBAL_STATE_FILE,
|
|
3
|
+
findCodexSessionPath,
|
|
4
|
+
findKimiSessionPath,
|
|
5
|
+
getClaude2SessionPath,
|
|
6
|
+
getClaudeSessionPath,
|
|
7
|
+
getOllamaSessionPath,
|
|
8
|
+
readJsonFile,
|
|
9
|
+
withFileLock,
|
|
10
|
+
writeJsonFile
|
|
11
|
+
} from "./chunk-GCYLMG43.mjs";
|
|
12
|
+
|
|
13
|
+
// packages/feature/agent/src/server/state/globalState.ts
|
|
14
|
+
import { createReadStream, existsSync } from "fs";
|
|
15
|
+
import { createInterface } from "readline";
|
|
16
|
+
var MAX_SESSIONS = 15;
|
|
17
|
+
var MAX_TEXT_LEN = 50;
|
|
18
|
+
function truncate(s) {
|
|
19
|
+
if (!s) return s;
|
|
20
|
+
const chars = [...s];
|
|
21
|
+
return chars.length <= MAX_TEXT_LEN ? s : chars.slice(0, MAX_TEXT_LEN).join("") + "\u2026";
|
|
22
|
+
}
|
|
23
|
+
async function updateGlobalState(cwd, sessionId, status, title, lastUserMessage) {
|
|
24
|
+
if (!existsSync(cwd)) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
return withFileLock(GLOBAL_STATE_FILE, async () => {
|
|
28
|
+
const state = await readJsonFile(GLOBAL_STATE_FILE, { sessions: [] });
|
|
29
|
+
for (const s of state.sessions) {
|
|
30
|
+
if (!s.status) {
|
|
31
|
+
const legacy = s;
|
|
32
|
+
s.status = legacy.isLoading ? "loading" : "normal";
|
|
33
|
+
delete legacy.isLoading;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const existingIndex = state.sessions.findIndex(
|
|
37
|
+
(s) => s.cwd === cwd && s.sessionId === sessionId
|
|
38
|
+
);
|
|
39
|
+
const existed = existingIndex >= 0;
|
|
40
|
+
const existing = existed ? state.sessions[existingIndex] : void 0;
|
|
41
|
+
const newSession = {
|
|
42
|
+
cwd,
|
|
43
|
+
sessionId,
|
|
44
|
+
lastActive: Date.now(),
|
|
45
|
+
status,
|
|
46
|
+
title: truncate(title || existing?.title),
|
|
47
|
+
lastUserMessage: truncate(lastUserMessage || existing?.lastUserMessage)
|
|
48
|
+
};
|
|
49
|
+
if (existingIndex >= 0) {
|
|
50
|
+
state.sessions[existingIndex] = newSession;
|
|
51
|
+
} else {
|
|
52
|
+
state.sessions.push(newSession);
|
|
53
|
+
}
|
|
54
|
+
state.sessions.sort((a, b) => b.lastActive - a.lastActive);
|
|
55
|
+
state.sessions = state.sessions.slice(0, MAX_SESSIONS);
|
|
56
|
+
await writeJsonFile(GLOBAL_STATE_FILE, state);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
async function getSessionTitle(cwd, sessionId) {
|
|
60
|
+
const claudePath = getClaudeSessionPath(cwd, sessionId);
|
|
61
|
+
if (existsSync(claudePath)) {
|
|
62
|
+
return getClaudeStyleTitle(claudePath);
|
|
63
|
+
}
|
|
64
|
+
const claude2Path = getClaude2SessionPath(cwd, sessionId);
|
|
65
|
+
if (existsSync(claude2Path)) {
|
|
66
|
+
return getClaudeStyleTitle(claude2Path);
|
|
67
|
+
}
|
|
68
|
+
const ollamaPath = getOllamaSessionPath(cwd, sessionId);
|
|
69
|
+
if (existsSync(ollamaPath)) {
|
|
70
|
+
return getClaudeStyleTitle(ollamaPath);
|
|
71
|
+
}
|
|
72
|
+
const codexPath = findCodexSessionPath(sessionId);
|
|
73
|
+
if (codexPath && existsSync(codexPath)) {
|
|
74
|
+
const title = await getCodexTitle(codexPath);
|
|
75
|
+
return title || "Untitled Session";
|
|
76
|
+
}
|
|
77
|
+
const kimiPath = findKimiSessionPath(sessionId);
|
|
78
|
+
if (kimiPath && existsSync(kimiPath)) {
|
|
79
|
+
const title = await getKimiTitle(kimiPath);
|
|
80
|
+
return title || "Untitled Session";
|
|
81
|
+
}
|
|
82
|
+
return "Untitled Session";
|
|
83
|
+
}
|
|
84
|
+
async function getLastUserMessage(cwd, sessionId) {
|
|
85
|
+
const claudePath = getClaudeSessionPath(cwd, sessionId);
|
|
86
|
+
if (existsSync(claudePath)) {
|
|
87
|
+
return await getClaudeStyleLastUserMessage(claudePath);
|
|
88
|
+
}
|
|
89
|
+
const claude2Path = getClaude2SessionPath(cwd, sessionId);
|
|
90
|
+
if (existsSync(claude2Path)) {
|
|
91
|
+
return await getClaudeStyleLastUserMessage(claude2Path);
|
|
92
|
+
}
|
|
93
|
+
const ollamaPath = getOllamaSessionPath(cwd, sessionId);
|
|
94
|
+
if (existsSync(ollamaPath)) {
|
|
95
|
+
return await getClaudeStyleLastUserMessage(ollamaPath);
|
|
96
|
+
}
|
|
97
|
+
const codexPath = findCodexSessionPath(sessionId);
|
|
98
|
+
if (codexPath && existsSync(codexPath)) {
|
|
99
|
+
return await getCodexLastUserMessage(codexPath);
|
|
100
|
+
}
|
|
101
|
+
const kimiPath = findKimiSessionPath(sessionId);
|
|
102
|
+
if (kimiPath && existsSync(kimiPath)) {
|
|
103
|
+
return await getKimiLastUserMessage(kimiPath);
|
|
104
|
+
}
|
|
105
|
+
return void 0;
|
|
106
|
+
}
|
|
107
|
+
function filterCommandTags(text) {
|
|
108
|
+
let filtered = text.replace(/<command-[^>]*>[\s\S]*?<\/command-[^>]*>/g, "");
|
|
109
|
+
filtered = filtered.replace(/<local-command-[^>]*>[\s\S]*?<\/local-command-[^>]*>/g, "");
|
|
110
|
+
filtered = filtered.trim();
|
|
111
|
+
return filtered;
|
|
112
|
+
}
|
|
113
|
+
function isValidUserMessage(text) {
|
|
114
|
+
if (text.startsWith("This session is being continued")) return false;
|
|
115
|
+
if (text.startsWith("Caveat: The messages below")) return false;
|
|
116
|
+
if (!text.trim()) return false;
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
async function getClaudeStyleTitle(filePath) {
|
|
120
|
+
try {
|
|
121
|
+
const fileStream = createReadStream(filePath);
|
|
122
|
+
const rl = createInterface({ input: fileStream, crlfDelay: Infinity });
|
|
123
|
+
let summary = "";
|
|
124
|
+
const userMessages = [];
|
|
125
|
+
for await (const line of rl) {
|
|
126
|
+
if (!line.trim()) continue;
|
|
127
|
+
try {
|
|
128
|
+
const entry = JSON.parse(line);
|
|
129
|
+
if (entry.type === "summary" && entry.summary) {
|
|
130
|
+
summary = entry.summary;
|
|
131
|
+
}
|
|
132
|
+
if (entry.type === "user") {
|
|
133
|
+
const message = entry.message;
|
|
134
|
+
if (!message?.content) continue;
|
|
135
|
+
if (typeof message.content === "string") {
|
|
136
|
+
userMessages.push(message.content);
|
|
137
|
+
} else if (Array.isArray(message.content)) {
|
|
138
|
+
for (const block of message.content) {
|
|
139
|
+
if (block.type === "text" && block.text) userMessages.push(block.text);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
} catch {
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return generateTitle(summary, userMessages);
|
|
147
|
+
} catch {
|
|
148
|
+
return "Untitled Session";
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async function getClaudeStyleLastUserMessage(filePath) {
|
|
152
|
+
try {
|
|
153
|
+
const fileStream = createReadStream(filePath);
|
|
154
|
+
const rl = createInterface({ input: fileStream, crlfDelay: Infinity });
|
|
155
|
+
let lastUserMessage;
|
|
156
|
+
for await (const line of rl) {
|
|
157
|
+
if (!line.trim()) continue;
|
|
158
|
+
try {
|
|
159
|
+
const entry = JSON.parse(line);
|
|
160
|
+
if (entry.type !== "user") continue;
|
|
161
|
+
const message = entry.message;
|
|
162
|
+
if (!message?.content) continue;
|
|
163
|
+
let text = "";
|
|
164
|
+
if (typeof message.content === "string") {
|
|
165
|
+
text = message.content;
|
|
166
|
+
} else if (Array.isArray(message.content)) {
|
|
167
|
+
for (const block of message.content) {
|
|
168
|
+
if (block.type === "text" && block.text) {
|
|
169
|
+
text = block.text;
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (!text) continue;
|
|
175
|
+
const filtered = filterCommandTags(text);
|
|
176
|
+
if (filtered && isValidUserMessage(filtered)) {
|
|
177
|
+
lastUserMessage = filtered;
|
|
178
|
+
}
|
|
179
|
+
} catch {
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return lastUserMessage;
|
|
183
|
+
} catch {
|
|
184
|
+
return void 0;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
async function getCodexLastUserMessage(filePath) {
|
|
188
|
+
try {
|
|
189
|
+
const fileStream = createReadStream(filePath);
|
|
190
|
+
const rl = createInterface({ input: fileStream, crlfDelay: Infinity });
|
|
191
|
+
let last;
|
|
192
|
+
for await (const line of rl) {
|
|
193
|
+
if (!line.trim()) continue;
|
|
194
|
+
let entry;
|
|
195
|
+
try {
|
|
196
|
+
entry = JSON.parse(line);
|
|
197
|
+
} catch {
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (entry.type !== "response_item") continue;
|
|
201
|
+
const payload = entry.payload;
|
|
202
|
+
if (!payload || payload.type !== "message" || payload.role !== "user") continue;
|
|
203
|
+
const text = payload.content?.filter((c) => c.type === "input_text" && c.text).map((c) => c.text).join("") || "";
|
|
204
|
+
if (!text || text.startsWith("<") || text.startsWith("#")) continue;
|
|
205
|
+
const filtered = filterCommandTags(text);
|
|
206
|
+
if (filtered && isValidUserMessage(filtered)) {
|
|
207
|
+
last = filtered;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return last;
|
|
211
|
+
} catch {
|
|
212
|
+
return void 0;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
async function getCodexTitle(filePath) {
|
|
216
|
+
try {
|
|
217
|
+
const fileStream = createReadStream(filePath);
|
|
218
|
+
const rl = createInterface({ input: fileStream, crlfDelay: Infinity });
|
|
219
|
+
for await (const line of rl) {
|
|
220
|
+
if (!line.trim()) continue;
|
|
221
|
+
let entry;
|
|
222
|
+
try {
|
|
223
|
+
entry = JSON.parse(line);
|
|
224
|
+
} catch {
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
if (entry.type !== "response_item") continue;
|
|
228
|
+
const payload = entry.payload;
|
|
229
|
+
if (!payload || payload.type !== "message" || payload.role !== "user") continue;
|
|
230
|
+
const text = payload.content?.filter((c) => c.type === "input_text" && c.text).map((c) => c.text).join("") || "";
|
|
231
|
+
if (!text || text.startsWith("<") || text.startsWith("#")) continue;
|
|
232
|
+
return text.slice(0, 80);
|
|
233
|
+
}
|
|
234
|
+
return void 0;
|
|
235
|
+
} catch {
|
|
236
|
+
return void 0;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
async function getKimiLastUserMessage(filePath) {
|
|
240
|
+
try {
|
|
241
|
+
const fileStream = createReadStream(filePath);
|
|
242
|
+
const rl = createInterface({ input: fileStream, crlfDelay: Infinity });
|
|
243
|
+
let last;
|
|
244
|
+
for await (const line of rl) {
|
|
245
|
+
if (!line.trim()) continue;
|
|
246
|
+
let entry;
|
|
247
|
+
try {
|
|
248
|
+
entry = JSON.parse(line);
|
|
249
|
+
} catch {
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
if (entry.role !== "user") continue;
|
|
253
|
+
const text = typeof entry.content === "string" ? entry.content : Array.isArray(entry.content) ? entry.content.filter((c) => (c.type === "input_text" || c.type === "text") && c.text).map((c) => c.text).join("") : "";
|
|
254
|
+
if (!text || text.startsWith("<system") || text.startsWith("<environment") || text.startsWith("# AGENTS.md") || text.startsWith("<permissions")) {
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
const filtered = filterCommandTags(text);
|
|
258
|
+
if (filtered && isValidUserMessage(filtered)) {
|
|
259
|
+
last = filtered;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return last;
|
|
263
|
+
} catch {
|
|
264
|
+
return void 0;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
async function getKimiTitle(filePath) {
|
|
268
|
+
try {
|
|
269
|
+
const fileStream = createReadStream(filePath);
|
|
270
|
+
const rl = createInterface({ input: fileStream, crlfDelay: Infinity });
|
|
271
|
+
for await (const line of rl) {
|
|
272
|
+
if (!line.trim()) continue;
|
|
273
|
+
let entry;
|
|
274
|
+
try {
|
|
275
|
+
entry = JSON.parse(line);
|
|
276
|
+
} catch {
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
if (entry.role !== "user") continue;
|
|
280
|
+
const text = typeof entry.content === "string" ? entry.content : Array.isArray(entry.content) ? entry.content.filter((c) => (c.type === "input_text" || c.type === "text") && c.text).map((c) => c.text).join("") : "";
|
|
281
|
+
if (!text || text.startsWith("<system") || text.startsWith("<environment") || text.startsWith("# AGENTS.md") || text.startsWith("<permissions")) {
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
return text.slice(0, 80);
|
|
285
|
+
}
|
|
286
|
+
return void 0;
|
|
287
|
+
} catch {
|
|
288
|
+
return void 0;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
function generateTitle(summary, userMessages) {
|
|
292
|
+
if (summary) return summary;
|
|
293
|
+
let commandName = "";
|
|
294
|
+
for (const msg of userMessages) {
|
|
295
|
+
const filtered = filterCommandTags(msg);
|
|
296
|
+
if (!filtered) continue;
|
|
297
|
+
if (filtered.startsWith("/") && !commandName) {
|
|
298
|
+
commandName = filtered;
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
if (commandName) {
|
|
302
|
+
return `${commandName} ${filtered}`;
|
|
303
|
+
}
|
|
304
|
+
return filtered;
|
|
305
|
+
}
|
|
306
|
+
if (commandName) return commandName;
|
|
307
|
+
return "Untitled Session";
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export {
|
|
311
|
+
updateGlobalState,
|
|
312
|
+
getSessionTitle,
|
|
313
|
+
getLastUserMessage
|
|
314
|
+
};
|
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
|
+
AppRuntime,
|
|
3
|
+
Cause_exports,
|
|
4
|
+
Effect_exports,
|
|
5
|
+
Exit_exports,
|
|
6
|
+
FSError,
|
|
7
|
+
Option_exports,
|
|
8
|
+
ValidationError,
|
|
2
9
|
ensureParentDir,
|
|
10
|
+
errorToStatus,
|
|
11
|
+
getBubbleOrderPath,
|
|
3
12
|
getTerminalHistoryPath,
|
|
4
|
-
getTerminalOutputPath
|
|
5
|
-
|
|
13
|
+
getTerminalOutputPath,
|
|
14
|
+
readJsonFile,
|
|
15
|
+
writeJsonFile
|
|
16
|
+
} from "./chunk-GCYLMG43.mjs";
|
|
6
17
|
|
|
7
18
|
// packages/feature/console/src/server/plugins/browser/BrowserBridge.ts
|
|
8
19
|
import { WebSocket } from "ws";
|
|
@@ -33,9 +44,9 @@ function toShortId(fullId) {
|
|
|
33
44
|
var g_browser = globalThis;
|
|
34
45
|
var registry = g_browser.__cockpitBrowserRegistry ?? (g_browser.__cockpitBrowserRegistry = /* @__PURE__ */ new Map());
|
|
35
46
|
var fullIdToShort = g_browser.__cockpitBrowserFullIdToShort ?? (g_browser.__cockpitBrowserFullIdToShort = /* @__PURE__ */ new Map());
|
|
36
|
-
function registerBrowser(fullId, ws) {
|
|
47
|
+
function registerBrowser(fullId, ws, projectCwd, tabId) {
|
|
37
48
|
const shortId = toShortId(fullId);
|
|
38
|
-
registry.set(shortId, { fullId, ws, lastSeen: Date.now() });
|
|
49
|
+
registry.set(shortId, { fullId, ws, lastSeen: Date.now(), projectCwd, tabId });
|
|
39
50
|
fullIdToShort.set(fullId, shortId);
|
|
40
51
|
return shortId;
|
|
41
52
|
}
|
|
@@ -65,7 +76,9 @@ function listBrowsers() {
|
|
|
65
76
|
result.push({
|
|
66
77
|
shortId,
|
|
67
78
|
fullId: entry.fullId,
|
|
68
|
-
connected: entry.ws !== null && entry.ws.readyState === WebSocket.OPEN
|
|
79
|
+
connected: entry.ws !== null && entry.ws.readyState === WebSocket.OPEN,
|
|
80
|
+
projectCwd: entry.projectCwd,
|
|
81
|
+
tabId: entry.tabId
|
|
69
82
|
});
|
|
70
83
|
}
|
|
71
84
|
return result;
|
|
@@ -80,12 +93,12 @@ function createPendingRequest(reqId, timeout) {
|
|
|
80
93
|
pendingRequests.set(reqId, { resolve, reject, timer });
|
|
81
94
|
});
|
|
82
95
|
}
|
|
83
|
-
function resolvePendingRequest(reqId,
|
|
96
|
+
function resolvePendingRequest(reqId, ok2, data, error) {
|
|
84
97
|
const pending = pendingRequests.get(reqId);
|
|
85
98
|
if (!pending) return;
|
|
86
99
|
clearTimeout(pending.timer);
|
|
87
100
|
pendingRequests.delete(reqId);
|
|
88
|
-
if (
|
|
101
|
+
if (ok2) {
|
|
89
102
|
pending.resolve(data);
|
|
90
103
|
} else {
|
|
91
104
|
pending.reject(new Error(error || "Browser command failed"));
|
|
@@ -440,7 +453,8 @@ function listTerminals(getRunning) {
|
|
|
440
453
|
tabId: entry.tabId,
|
|
441
454
|
command: entry.command,
|
|
442
455
|
pid: cmd?.pid ?? 0,
|
|
443
|
-
running: !!cmd
|
|
456
|
+
running: !!cmd,
|
|
457
|
+
projectCwd: entry.projectCwd
|
|
444
458
|
});
|
|
445
459
|
}
|
|
446
460
|
return result;
|
|
@@ -926,6 +940,178 @@ function grepOutput(cmd, pattern, opts = {}) {
|
|
|
926
940
|
};
|
|
927
941
|
}
|
|
928
942
|
|
|
943
|
+
// packages/shared/effect-runtime/src/next.ts
|
|
944
|
+
var ok = (body, status = 200) => new Response(JSON.stringify(body), {
|
|
945
|
+
status,
|
|
946
|
+
headers: { "content-type": "application/json" }
|
|
947
|
+
});
|
|
948
|
+
var extractErrorMessage = (e) => {
|
|
949
|
+
const maybeMsg = e.message;
|
|
950
|
+
if (typeof maybeMsg === "string" && maybeMsg.length > 0) {
|
|
951
|
+
return maybeMsg;
|
|
952
|
+
}
|
|
953
|
+
const cause = e.cause;
|
|
954
|
+
if (cause instanceof Error && cause.message.length > 0) {
|
|
955
|
+
return cause.message;
|
|
956
|
+
}
|
|
957
|
+
switch (e._tag) {
|
|
958
|
+
case "ValidationError": {
|
|
959
|
+
const v = e;
|
|
960
|
+
return `Invalid ${v.field}: ${v.reason}`;
|
|
961
|
+
}
|
|
962
|
+
case "NotFoundError": {
|
|
963
|
+
const n = e;
|
|
964
|
+
return `${n.resource} not found: ${n.id}`;
|
|
965
|
+
}
|
|
966
|
+
case "PermissionError": {
|
|
967
|
+
const p = e;
|
|
968
|
+
return `Permission denied: ${p.action} on ${p.resource}`;
|
|
969
|
+
}
|
|
970
|
+
case "DBError": {
|
|
971
|
+
const d = e;
|
|
972
|
+
return `${d.db} ${d.op} failed`;
|
|
973
|
+
}
|
|
974
|
+
case "FSError": {
|
|
975
|
+
const f = e;
|
|
976
|
+
return `${f.op} ${f.path} failed`;
|
|
977
|
+
}
|
|
978
|
+
case "WSError": {
|
|
979
|
+
const w = e;
|
|
980
|
+
return `${w.proto} ${w.kind} failed`;
|
|
981
|
+
}
|
|
982
|
+
case "AgentError": {
|
|
983
|
+
const a = e;
|
|
984
|
+
return `${a.provider} ${a.kind} failed`;
|
|
985
|
+
}
|
|
986
|
+
default:
|
|
987
|
+
return e._tag;
|
|
988
|
+
}
|
|
989
|
+
};
|
|
990
|
+
var errorToResponse = (cause) => {
|
|
991
|
+
const failure = Cause_exports.failureOption(cause);
|
|
992
|
+
if (Option_exports.isSome(failure)) {
|
|
993
|
+
const e = failure.value;
|
|
994
|
+
const status = errorToStatus(e);
|
|
995
|
+
return new Response(
|
|
996
|
+
JSON.stringify({ error: extractErrorMessage(e), tag: e._tag }),
|
|
997
|
+
{
|
|
998
|
+
status,
|
|
999
|
+
headers: { "content-type": "application/json" }
|
|
1000
|
+
}
|
|
1001
|
+
);
|
|
1002
|
+
}
|
|
1003
|
+
console.error("[handler] uncaught defect:\n" + Cause_exports.pretty(cause));
|
|
1004
|
+
return new Response(
|
|
1005
|
+
JSON.stringify({ error: "Internal Server Error", tag: "InternalError" }),
|
|
1006
|
+
{
|
|
1007
|
+
status: 500,
|
|
1008
|
+
headers: { "content-type": "application/json" }
|
|
1009
|
+
}
|
|
1010
|
+
);
|
|
1011
|
+
};
|
|
1012
|
+
var handler = (fn) => async (req) => {
|
|
1013
|
+
const exit = await AppRuntime.runPromiseExit(fn(req));
|
|
1014
|
+
return Exit_exports.match(exit, {
|
|
1015
|
+
onFailure: (cause) => errorToResponse(cause),
|
|
1016
|
+
onSuccess: (res) => res
|
|
1017
|
+
});
|
|
1018
|
+
};
|
|
1019
|
+
var parseJsonRaw = (req) => Effect_exports.tryPromise({
|
|
1020
|
+
try: () => req.json(),
|
|
1021
|
+
catch: () => new ValidationError({
|
|
1022
|
+
field: "body",
|
|
1023
|
+
reason: "invalid JSON"
|
|
1024
|
+
})
|
|
1025
|
+
});
|
|
1026
|
+
|
|
1027
|
+
// packages/feature/console/src/server/api/terminal/bubble-order.ts
|
|
1028
|
+
async function readBubbleTitles(cwd, tabId) {
|
|
1029
|
+
try {
|
|
1030
|
+
const raw = await readJsonFile(getBubbleOrderPath(cwd, tabId), []);
|
|
1031
|
+
return normalise(raw).titles;
|
|
1032
|
+
} catch {
|
|
1033
|
+
return {};
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
function normalise(raw) {
|
|
1037
|
+
if (Array.isArray(raw)) return { order: raw, titles: {} };
|
|
1038
|
+
if (raw && typeof raw === "object") {
|
|
1039
|
+
const r = raw;
|
|
1040
|
+
return {
|
|
1041
|
+
order: Array.isArray(r.order) ? r.order : [],
|
|
1042
|
+
titles: r.titles && typeof r.titles === "object" ? r.titles : {}
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
return { order: [], titles: {} };
|
|
1046
|
+
}
|
|
1047
|
+
var GET = handler(
|
|
1048
|
+
(req) => Effect_exports.gen(function* () {
|
|
1049
|
+
const sp = new URL(req.url).searchParams;
|
|
1050
|
+
const cwd = sp.get("cwd");
|
|
1051
|
+
const tabId = sp.get("tabId");
|
|
1052
|
+
if (!cwd || !tabId) {
|
|
1053
|
+
return yield* Effect_exports.fail(
|
|
1054
|
+
new ValidationError({
|
|
1055
|
+
field: !cwd ? "cwd" : "tabId",
|
|
1056
|
+
reason: "missing"
|
|
1057
|
+
})
|
|
1058
|
+
);
|
|
1059
|
+
}
|
|
1060
|
+
const orderPath = getBubbleOrderPath(cwd, tabId);
|
|
1061
|
+
const raw = yield* Effect_exports.tryPromise({
|
|
1062
|
+
try: () => readJsonFile(orderPath, []),
|
|
1063
|
+
catch: (cause) => new FSError({ path: orderPath, op: "read", cause })
|
|
1064
|
+
});
|
|
1065
|
+
const file = normalise(raw);
|
|
1066
|
+
return ok({ order: file.order, titles: file.titles });
|
|
1067
|
+
})
|
|
1068
|
+
);
|
|
1069
|
+
var POST = handler(
|
|
1070
|
+
(req) => Effect_exports.gen(function* () {
|
|
1071
|
+
const body = yield* parseJsonRaw(req);
|
|
1072
|
+
if (!body.cwd || !body.tabId) {
|
|
1073
|
+
return yield* Effect_exports.fail(
|
|
1074
|
+
new ValidationError({
|
|
1075
|
+
field: !body.cwd ? "cwd" : "tabId",
|
|
1076
|
+
reason: "missing"
|
|
1077
|
+
})
|
|
1078
|
+
);
|
|
1079
|
+
}
|
|
1080
|
+
const hasOrder = Array.isArray(body.order);
|
|
1081
|
+
const hasTitles = body.titles && typeof body.titles === "object";
|
|
1082
|
+
if (!hasOrder && !hasTitles) {
|
|
1083
|
+
return yield* Effect_exports.fail(
|
|
1084
|
+
new ValidationError({ field: "order|titles", reason: "missing \u2014 provide at least one" })
|
|
1085
|
+
);
|
|
1086
|
+
}
|
|
1087
|
+
const orderPath = getBubbleOrderPath(body.cwd, body.tabId);
|
|
1088
|
+
const existing = normalise(
|
|
1089
|
+
yield* Effect_exports.tryPromise({
|
|
1090
|
+
try: () => readJsonFile(orderPath, []),
|
|
1091
|
+
catch: (cause) => new FSError({ path: orderPath, op: "read", cause })
|
|
1092
|
+
})
|
|
1093
|
+
);
|
|
1094
|
+
const next = {
|
|
1095
|
+
order: hasOrder ? body.order : existing.order,
|
|
1096
|
+
titles: hasTitles ? mergeTitles(existing.titles, body.titles) : existing.titles
|
|
1097
|
+
};
|
|
1098
|
+
yield* Effect_exports.tryPromise({
|
|
1099
|
+
try: () => writeJsonFile(orderPath, next),
|
|
1100
|
+
catch: (cause) => new FSError({ path: orderPath, op: "write", cause })
|
|
1101
|
+
});
|
|
1102
|
+
return ok({ success: true });
|
|
1103
|
+
})
|
|
1104
|
+
);
|
|
1105
|
+
function mergeTitles(base, patch) {
|
|
1106
|
+
const out = { ...base };
|
|
1107
|
+
for (const [k, v] of Object.entries(patch)) {
|
|
1108
|
+
if (typeof v !== "string") continue;
|
|
1109
|
+
if (v === "") delete out[k];
|
|
1110
|
+
else out[k] = v.slice(0, 256);
|
|
1111
|
+
}
|
|
1112
|
+
return out;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
929
1115
|
export {
|
|
930
1116
|
registerBrowser,
|
|
931
1117
|
unregisterBrowser,
|
|
@@ -965,5 +1151,6 @@ export {
|
|
|
965
1151
|
readTail,
|
|
966
1152
|
readHead,
|
|
967
1153
|
readAround,
|
|
968
|
-
grepOutput
|
|
1154
|
+
grepOutput,
|
|
1155
|
+
readBubbleTitles
|
|
969
1156
|
};
|
package/dist/httpApi.mjs
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
listBrowsers,
|
|
11
11
|
listTerminals,
|
|
12
12
|
readAround,
|
|
13
|
+
readBubbleTitles,
|
|
13
14
|
readHead,
|
|
14
15
|
readSince,
|
|
15
16
|
readTail,
|
|
@@ -19,10 +20,10 @@ import {
|
|
|
19
20
|
unregisterBrowser,
|
|
20
21
|
unregisterTerminal,
|
|
21
22
|
writeStdinToCommand
|
|
22
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-W6G6X3FP.mjs";
|
|
23
24
|
import {
|
|
24
25
|
getTerminalHistoryPath
|
|
25
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-GCYLMG43.mjs";
|
|
26
27
|
import "./chunk-7P6ASYW6.mjs";
|
|
27
28
|
|
|
28
29
|
// src/lib/httpApi.ts
|
|
@@ -30,6 +31,7 @@ import { parse } from "url";
|
|
|
30
31
|
import { readFile } from "fs/promises";
|
|
31
32
|
import { randomUUID } from "crypto";
|
|
32
33
|
import { WebSocket } from "ws";
|
|
34
|
+
import { resolve as resolvePath } from "path";
|
|
33
35
|
async function readFinishedOutput(projectCwd, tabId, commandId) {
|
|
34
36
|
try {
|
|
35
37
|
const historyPath = getTerminalHistoryPath(projectCwd, tabId);
|
|
@@ -419,7 +421,78 @@ async function handleBrowserApi(req, res) {
|
|
|
419
421
|
}
|
|
420
422
|
return true;
|
|
421
423
|
}
|
|
424
|
+
async function handleConnectionApi(req, res) {
|
|
425
|
+
const { pathname } = parse(req.url || "", true);
|
|
426
|
+
const match = pathname?.match(/^\/api\/connection\/([a-z]+)$/);
|
|
427
|
+
if (!match || req.method !== "POST") return false;
|
|
428
|
+
if (match[1] !== "list") return false;
|
|
429
|
+
const chunks = [];
|
|
430
|
+
for await (const chunk of req) chunks.push(chunk);
|
|
431
|
+
let body = {};
|
|
432
|
+
try {
|
|
433
|
+
body = JSON.parse(Buffer.concat(chunks).toString());
|
|
434
|
+
} catch {
|
|
435
|
+
}
|
|
436
|
+
const sendJson = (status, data) => {
|
|
437
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
438
|
+
res.end(JSON.stringify(data));
|
|
439
|
+
};
|
|
440
|
+
const filterCwd = body.cwd ? resolvePath(body.cwd) : void 0;
|
|
441
|
+
const aliveOnly = !body.all;
|
|
442
|
+
const terms = listTerminals(getRunningCommand);
|
|
443
|
+
const browsers = listBrowsers();
|
|
444
|
+
const sameCwd = (entryCwd) => !filterCwd ? true : !!entryCwd && resolvePath(entryCwd) === filterCwd;
|
|
445
|
+
const SEP = String.fromCharCode(31);
|
|
446
|
+
const cwdTabPairs = /* @__PURE__ */ new Set();
|
|
447
|
+
for (const t of terms) {
|
|
448
|
+
if (t.projectCwd && t.tabId) cwdTabPairs.add(`${t.projectCwd}${SEP}${t.tabId}`);
|
|
449
|
+
}
|
|
450
|
+
for (const b of browsers) {
|
|
451
|
+
if (b.projectCwd && b.tabId) cwdTabPairs.add(`${b.projectCwd}${SEP}${b.tabId}`);
|
|
452
|
+
}
|
|
453
|
+
const titlesByPair = /* @__PURE__ */ new Map();
|
|
454
|
+
await Promise.all(
|
|
455
|
+
Array.from(cwdTabPairs).map(async (pair) => {
|
|
456
|
+
const [cwd, tabId] = pair.split(SEP);
|
|
457
|
+
titlesByPair.set(pair, await readBubbleTitles(cwd, tabId));
|
|
458
|
+
})
|
|
459
|
+
);
|
|
460
|
+
const titleOf = (cwd, tabId, key) => {
|
|
461
|
+
if (!cwd || !tabId) return void 0;
|
|
462
|
+
const t = titlesByPair.get(`${cwd}${SEP}${tabId}`)?.[key];
|
|
463
|
+
return t || void 0;
|
|
464
|
+
};
|
|
465
|
+
const out = [];
|
|
466
|
+
for (const t of terms) {
|
|
467
|
+
if (!sameCwd(t.projectCwd)) continue;
|
|
468
|
+
if (aliveOnly && !t.running) continue;
|
|
469
|
+
out.push({
|
|
470
|
+
type: "terminal",
|
|
471
|
+
shortId: t.shortId,
|
|
472
|
+
title: titleOf(t.projectCwd, t.tabId, t.commandId),
|
|
473
|
+
projectCwd: t.projectCwd,
|
|
474
|
+
tabId: t.tabId,
|
|
475
|
+
command: t.command,
|
|
476
|
+
alive: t.running
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
for (const b of browsers) {
|
|
480
|
+
if (!sameCwd(b.projectCwd)) continue;
|
|
481
|
+
if (aliveOnly && !b.connected) continue;
|
|
482
|
+
out.push({
|
|
483
|
+
type: "browser",
|
|
484
|
+
shortId: b.shortId,
|
|
485
|
+
title: titleOf(b.projectCwd, b.tabId, b.fullId),
|
|
486
|
+
projectCwd: b.projectCwd,
|
|
487
|
+
tabId: b.tabId,
|
|
488
|
+
alive: b.connected
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
sendJson(200, { ok: true, data: out });
|
|
492
|
+
return true;
|
|
493
|
+
}
|
|
422
494
|
export {
|
|
423
495
|
handleBrowserApi,
|
|
496
|
+
handleConnectionApi,
|
|
424
497
|
handleTerminalApi
|
|
425
498
|
};
|