sparkecoder 0.1.20 → 0.1.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/index.d.ts +2 -2
- package/dist/agent/index.js +53 -3
- package/dist/agent/index.js.map +1 -1
- package/dist/cli.js +397 -46
- package/dist/cli.js.map +1 -1
- package/dist/db/index.d.ts +2 -1
- package/dist/db/index.js.map +1 -1
- package/dist/{index-BzedNBK-.d.ts → index-CNwLFGiZ.d.ts} +24 -3
- package/dist/index.d.ts +4 -4
- package/dist/index.js +392 -41
- package/dist/index.js.map +1 -1
- package/dist/{schema-CkrIadxa.d.ts → schema-Df7MU3nM.d.ts} +26 -3
- package/dist/server/index.js +392 -41
- package/dist/server/index.js.map +1 -1
- package/dist/tools/index.js.map +1 -1
- package/package.json +1 -1
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/standalone/web/.next/BUILD_ID +1 -1
- package/web/.next/standalone/web/.next/build-manifest.json +2 -2
- package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
- package/web/.next/standalone/web/.next/server/app/(main)/page.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
- package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.html +1 -1
- package/web/.next/standalone/web/.next/server/app/index.rsc +4 -4
- package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +4 -4
- package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_1d78db71._.js → 2374f_387a1437._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_378282b1._.js → 2374f_5f58fd73._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_30f9df13._.js → 2374f_65fcfd95._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_8825dcc9._.js → 2374f_741f6b67._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_9bf3c7f3._.js → 2374f_814be2c9._.js} +2 -2
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_5de336d2._.js → 2374f_84859a94._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_bbc99511._.js → 2374f_cfd0137a._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_d94c2b70._.js → 2374f_f1038f7c._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__a984d933._.js → [root-of-the-server]__3ec22171._.js} +2 -2
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_96bca05b._.js +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_c7618534._.js +8 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_d7d3e40d._.js +1 -1
- package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
- package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
- package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
- package/web/.next/standalone/web/.next/static/chunks/{55705f91c6cfe307.js → 3bb454ca848ec78e.js} +3 -3
- package/web/.next/standalone/web/.next/static/chunks/{5ec82ce8f3aabaf0.js → 5e5b485d77ac0d8f.js} +1 -1
- package/web/.next/standalone/web/.next/static/chunks/{c9d8a4b9a763e232.js → beb9625c4a470042.js} +1 -1
- package/web/.next/standalone/web/.next/static/chunks/c81c1aec4369c77f.js +5 -0
- package/web/.next/standalone/web/.next/static/chunks/cb355fac10c6ad11.css +1 -0
- package/web/.next/standalone/web/.next/static/static/chunks/{55705f91c6cfe307.js → 3bb454ca848ec78e.js} +3 -3
- package/web/.next/standalone/web/.next/static/static/chunks/{5ec82ce8f3aabaf0.js → 5e5b485d77ac0d8f.js} +1 -1
- package/web/.next/standalone/web/.next/static/static/chunks/{c9d8a4b9a763e232.js → beb9625c4a470042.js} +1 -1
- package/web/.next/standalone/web/.next/static/static/chunks/c81c1aec4369c77f.js +5 -0
- package/web/.next/standalone/web/.next/static/static/chunks/cb355fac10c6ad11.css +1 -0
- package/web/.next/standalone/web/src/app/(main)/layout.tsx +2 -2
- package/web/.next/standalone/web/src/components/ai-elements/speech-input.tsx +89 -36
- package/web/.next/standalone/web/src/components/chat-interface.tsx +354 -38
- package/web/.next/standalone/web/src/components/ui/sidebar.tsx +2 -2
- package/web/.next/standalone/web/src/lib/api.ts +133 -2
- package/web/.next/static/chunks/{55705f91c6cfe307.js → 3bb454ca848ec78e.js} +3 -3
- package/web/.next/static/chunks/{5ec82ce8f3aabaf0.js → 5e5b485d77ac0d8f.js} +1 -1
- package/web/.next/static/chunks/{c9d8a4b9a763e232.js → beb9625c4a470042.js} +1 -1
- package/web/.next/static/chunks/c81c1aec4369c77f.js +5 -0
- package/web/.next/static/chunks/cb355fac10c6ad11.css +1 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_19b6934c._.js +0 -8
- package/web/.next/standalone/web/.next/static/chunks/61d61c75ce7cd4ba.js +0 -5
- package/web/.next/standalone/web/.next/static/chunks/d0a69c59b1c0d99c.css +0 -1
- package/web/.next/standalone/web/.next/static/static/chunks/61d61c75ce7cd4ba.js +0 -5
- package/web/.next/standalone/web/.next/static/static/chunks/d0a69c59b1c0d99c.css +0 -1
- package/web/.next/static/chunks/61d61c75ce7cd4ba.js +0 -5
- package/web/.next/static/chunks/d0a69c59b1c0d99c.css +0 -1
- /package/web/.next/standalone/web/.next/static/{ogmHZngrFt0TlARPSetHj → n86r6x1RoUipFp6nLIk-R}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{ogmHZngrFt0TlARPSetHj → n86r6x1RoUipFp6nLIk-R}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{ogmHZngrFt0TlARPSetHj → n86r6x1RoUipFp6nLIk-R}/_ssgManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/static/{ogmHZngrFt0TlARPSetHj → n86r6x1RoUipFp6nLIk-R}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/static/{ogmHZngrFt0TlARPSetHj → n86r6x1RoUipFp6nLIk-R}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/static/{ogmHZngrFt0TlARPSetHj → n86r6x1RoUipFp6nLIk-R}/_ssgManifest.js +0 -0
- /package/web/.next/static/{ogmHZngrFt0TlARPSetHj → n86r6x1RoUipFp6nLIk-R}/_buildManifest.js +0 -0
- /package/web/.next/static/{ogmHZngrFt0TlARPSetHj → n86r6x1RoUipFp6nLIk-R}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{ogmHZngrFt0TlARPSetHj → n86r6x1RoUipFp6nLIk-R}/_ssgManifest.js +0 -0
|
@@ -1263,9 +1263,28 @@ interface SystemModelMessage {
|
|
|
1263
1263
|
role: 'system';
|
|
1264
1264
|
content: string;
|
|
1265
1265
|
}
|
|
1266
|
+
interface UserTextPart {
|
|
1267
|
+
type: 'text';
|
|
1268
|
+
text: string;
|
|
1269
|
+
}
|
|
1270
|
+
interface UserImagePart {
|
|
1271
|
+
type: 'image';
|
|
1272
|
+
image: string;
|
|
1273
|
+
mediaType?: string;
|
|
1274
|
+
filename?: string;
|
|
1275
|
+
savedPath?: string;
|
|
1276
|
+
}
|
|
1277
|
+
interface UserFilePart {
|
|
1278
|
+
type: 'file';
|
|
1279
|
+
data: string;
|
|
1280
|
+
mediaType?: string;
|
|
1281
|
+
filename?: string;
|
|
1282
|
+
savedPath?: string;
|
|
1283
|
+
}
|
|
1284
|
+
type UserContentPart = UserTextPart | UserImagePart | UserFilePart;
|
|
1266
1285
|
interface UserModelMessage {
|
|
1267
1286
|
role: 'user';
|
|
1268
|
-
content: string;
|
|
1287
|
+
content: string | UserContentPart[];
|
|
1269
1288
|
}
|
|
1270
1289
|
interface AssistantModelMessage {
|
|
1271
1290
|
role: 'assistant';
|
|
@@ -1330,7 +1349,11 @@ type schema_TodoItem = TodoItem;
|
|
|
1330
1349
|
type schema_ToolExecution = ToolExecution;
|
|
1331
1350
|
type schema_ToolModelMessage = ToolModelMessage;
|
|
1332
1351
|
type schema_ToolResultPart = ToolResultPart;
|
|
1352
|
+
type schema_UserContentPart = UserContentPart;
|
|
1353
|
+
type schema_UserFilePart = UserFilePart;
|
|
1354
|
+
type schema_UserImagePart = UserImagePart;
|
|
1333
1355
|
type schema_UserModelMessage = UserModelMessage;
|
|
1356
|
+
type schema_UserTextPart = UserTextPart;
|
|
1334
1357
|
declare const schema_activeStreams: typeof activeStreams;
|
|
1335
1358
|
declare const schema_checkpoints: typeof checkpoints;
|
|
1336
1359
|
declare const schema_fileBackups: typeof fileBackups;
|
|
@@ -1341,7 +1364,7 @@ declare const schema_terminals: typeof terminals;
|
|
|
1341
1364
|
declare const schema_todoItems: typeof todoItems;
|
|
1342
1365
|
declare const schema_toolExecutions: typeof toolExecutions;
|
|
1343
1366
|
declare namespace schema {
|
|
1344
|
-
export { type schema_ActiveStream as ActiveStream, type schema_AssistantContentPart as AssistantContentPart, type schema_AssistantModelMessage as AssistantModelMessage, type schema_Checkpoint as Checkpoint, type schema_FileBackup as FileBackup, type schema_LoadedSkill as LoadedSkill, type schema_Message as Message, type schema_ModelMessage as ModelMessage, type schema_NewActiveStream as NewActiveStream, type schema_NewCheckpoint as NewCheckpoint, type schema_NewFileBackup as NewFileBackup, type schema_NewMessage as NewMessage, type schema_NewSession as NewSession, type schema_NewTerminal as NewTerminal, type schema_NewTodoItem as NewTodoItem, type schema_NewToolExecution as NewToolExecution, type schema_Session as Session, type schema_SessionConfig as SessionConfig, type schema_SystemModelMessage as SystemModelMessage, type schema_Terminal as Terminal, type schema_TodoItem as TodoItem, type schema_ToolExecution as ToolExecution, type schema_ToolModelMessage as ToolModelMessage, type schema_ToolResultPart as ToolResultPart, type schema_UserModelMessage as UserModelMessage, schema_activeStreams as activeStreams, schema_checkpoints as checkpoints, schema_fileBackups as fileBackups, schema_loadedSkills as loadedSkills, schema_messages as messages, schema_sessions as sessions, schema_terminals as terminals, schema_todoItems as todoItems, schema_toolExecutions as toolExecutions };
|
|
1367
|
+
export { type schema_ActiveStream as ActiveStream, type schema_AssistantContentPart as AssistantContentPart, type schema_AssistantModelMessage as AssistantModelMessage, type schema_Checkpoint as Checkpoint, type schema_FileBackup as FileBackup, type schema_LoadedSkill as LoadedSkill, type schema_Message as Message, type schema_ModelMessage as ModelMessage, type schema_NewActiveStream as NewActiveStream, type schema_NewCheckpoint as NewCheckpoint, type schema_NewFileBackup as NewFileBackup, type schema_NewMessage as NewMessage, type schema_NewSession as NewSession, type schema_NewTerminal as NewTerminal, type schema_NewTodoItem as NewTodoItem, type schema_NewToolExecution as NewToolExecution, type schema_Session as Session, type schema_SessionConfig as SessionConfig, type schema_SystemModelMessage as SystemModelMessage, type schema_Terminal as Terminal, type schema_TodoItem as TodoItem, type schema_ToolExecution as ToolExecution, type schema_ToolModelMessage as ToolModelMessage, type schema_ToolResultPart as ToolResultPart, type schema_UserContentPart as UserContentPart, type schema_UserFilePart as UserFilePart, type schema_UserImagePart as UserImagePart, type schema_UserModelMessage as UserModelMessage, type schema_UserTextPart as UserTextPart, schema_activeStreams as activeStreams, schema_checkpoints as checkpoints, schema_fileBackups as fileBackups, schema_loadedSkills as loadedSkills, schema_messages as messages, schema_sessions as sessions, schema_terminals as terminals, schema_todoItems as todoItems, schema_toolExecutions as toolExecutions };
|
|
1345
1368
|
}
|
|
1346
1369
|
|
|
1347
|
-
export { type ActiveStream as A, type Checkpoint as C, type FileBackup as F, type LoadedSkill as L, type ModelMessage as M, type NewSession as N, type Session as S, type TodoItem as T, type Message as a, type SessionConfig as b, type NewTodoItem as c, type NewToolExecution as d, type ToolExecution as e, type NewTerminal as f, type Terminal as g, schema as s };
|
|
1370
|
+
export { type ActiveStream as A, type Checkpoint as C, type FileBackup as F, type LoadedSkill as L, type ModelMessage as M, type NewSession as N, type Session as S, type TodoItem as T, type UserContentPart as U, type Message as a, type SessionConfig as b, type NewTodoItem as c, type NewToolExecution as d, type ToolExecution as e, type NewTerminal as f, type Terminal as g, type UserFilePart as h, type UserImagePart as i, type UserModelMessage as j, type UserTextPart as k, schema as s };
|
package/dist/server/index.js
CHANGED
|
@@ -10,8 +10,8 @@ import { Hono as Hono5 } from "hono";
|
|
|
10
10
|
import { serve } from "@hono/node-server";
|
|
11
11
|
import { cors } from "hono/cors";
|
|
12
12
|
import { logger } from "hono/logger";
|
|
13
|
-
import { existsSync as
|
|
14
|
-
import { resolve as resolve8, dirname as dirname6, join as
|
|
13
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
14
|
+
import { resolve as resolve8, dirname as dirname6, join as join5 } from "path";
|
|
15
15
|
import { spawn as spawn2 } from "child_process";
|
|
16
16
|
import { createServer as createNetServer } from "net";
|
|
17
17
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
@@ -20,6 +20,9 @@ import { fileURLToPath as fileURLToPath2 } from "url";
|
|
|
20
20
|
import { Hono } from "hono";
|
|
21
21
|
import { zValidator } from "@hono/zod-validator";
|
|
22
22
|
import { z as z9 } from "zod";
|
|
23
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync3, writeFileSync as writeFileSync2, readdirSync, statSync, unlinkSync } from "fs";
|
|
24
|
+
import { join as join3, basename as basename2, extname as extname5 } from "path";
|
|
25
|
+
import { nanoid as nanoid4 } from "nanoid";
|
|
23
26
|
|
|
24
27
|
// src/db/index.ts
|
|
25
28
|
import Database from "better-sqlite3";
|
|
@@ -3260,11 +3263,12 @@ ${this.summary}`
|
|
|
3260
3263
|
}
|
|
3261
3264
|
/**
|
|
3262
3265
|
* Add a user message to the context
|
|
3266
|
+
* Content can be a string or an array of content parts (for messages with images/files)
|
|
3263
3267
|
*/
|
|
3264
|
-
addUserMessage(
|
|
3268
|
+
addUserMessage(content) {
|
|
3265
3269
|
const userMessage = {
|
|
3266
3270
|
role: "user",
|
|
3267
|
-
content
|
|
3271
|
+
content
|
|
3268
3272
|
};
|
|
3269
3273
|
messageQueries.create(this.sessionId, userMessage);
|
|
3270
3274
|
}
|
|
@@ -3364,13 +3368,62 @@ var Agent = class _Agent {
|
|
|
3364
3368
|
getSession() {
|
|
3365
3369
|
return this.session;
|
|
3366
3370
|
}
|
|
3371
|
+
/**
|
|
3372
|
+
* Build user message content from prompt and attachments
|
|
3373
|
+
*/
|
|
3374
|
+
buildUserMessageContent(prompt, attachments) {
|
|
3375
|
+
if (!attachments || attachments.length === 0) {
|
|
3376
|
+
return prompt;
|
|
3377
|
+
}
|
|
3378
|
+
const contentParts = [];
|
|
3379
|
+
const attachmentDescriptions = attachments.map((a, i) => {
|
|
3380
|
+
const name = a.filename || `attachment_${i + 1}`;
|
|
3381
|
+
const typeLabel = a.type === "image" ? "Image" : "File";
|
|
3382
|
+
const location = a.savedPath || "(path unknown)";
|
|
3383
|
+
return `${i + 1}. ${typeLabel}: "${name}" saved at: ${location}`;
|
|
3384
|
+
}).join("\n");
|
|
3385
|
+
contentParts.push({
|
|
3386
|
+
type: "text",
|
|
3387
|
+
text: `[FILE ATTACHMENTS - The user has attached the following files which are saved on disk]
|
|
3388
|
+
${attachmentDescriptions}
|
|
3389
|
+
|
|
3390
|
+
You can reference these files by their paths above. The file contents are also shown inline below.`
|
|
3391
|
+
});
|
|
3392
|
+
if (prompt) {
|
|
3393
|
+
contentParts.push({ type: "text", text: `
|
|
3394
|
+
[USER MESSAGE]
|
|
3395
|
+
${prompt}` });
|
|
3396
|
+
}
|
|
3397
|
+
for (const attachment of attachments) {
|
|
3398
|
+
if (attachment.type === "image") {
|
|
3399
|
+
contentParts.push({
|
|
3400
|
+
type: "image",
|
|
3401
|
+
image: attachment.data,
|
|
3402
|
+
// base64 data URL or raw base64
|
|
3403
|
+
mediaType: attachment.mediaType,
|
|
3404
|
+
filename: attachment.filename,
|
|
3405
|
+
savedPath: attachment.savedPath
|
|
3406
|
+
});
|
|
3407
|
+
} else {
|
|
3408
|
+
contentParts.push({
|
|
3409
|
+
type: "file",
|
|
3410
|
+
data: attachment.data,
|
|
3411
|
+
mediaType: attachment.mediaType || "application/octet-stream",
|
|
3412
|
+
filename: attachment.filename,
|
|
3413
|
+
savedPath: attachment.savedPath
|
|
3414
|
+
});
|
|
3415
|
+
}
|
|
3416
|
+
}
|
|
3417
|
+
return contentParts;
|
|
3418
|
+
}
|
|
3367
3419
|
/**
|
|
3368
3420
|
* Run the agent with a prompt (streaming)
|
|
3369
3421
|
*/
|
|
3370
3422
|
async stream(options) {
|
|
3371
3423
|
const config = getConfig();
|
|
3424
|
+
const userContent = this.buildUserMessageContent(options.prompt, options.attachments);
|
|
3372
3425
|
if (!options.skipSaveUserMessage) {
|
|
3373
|
-
this.context.addUserMessage(
|
|
3426
|
+
this.context.addUserMessage(userContent);
|
|
3374
3427
|
}
|
|
3375
3428
|
sessionQueries.updateStatus(this.session.id, "active");
|
|
3376
3429
|
const systemPrompt = await buildSystemPrompt({
|
|
@@ -3930,11 +3983,138 @@ sessions2.get("/:id/diff/:filePath", async (c) => {
|
|
|
3930
3983
|
currentContent: fileDiff.currentContent
|
|
3931
3984
|
});
|
|
3932
3985
|
});
|
|
3986
|
+
function getAttachmentsDir(sessionId) {
|
|
3987
|
+
const appDataDir = getAppDataDirectory();
|
|
3988
|
+
return join3(appDataDir, "attachments", sessionId);
|
|
3989
|
+
}
|
|
3990
|
+
function ensureAttachmentsDir(sessionId) {
|
|
3991
|
+
const dir = getAttachmentsDir(sessionId);
|
|
3992
|
+
if (!existsSync10(dir)) {
|
|
3993
|
+
mkdirSync3(dir, { recursive: true });
|
|
3994
|
+
}
|
|
3995
|
+
return dir;
|
|
3996
|
+
}
|
|
3997
|
+
sessions2.get("/:id/attachments", async (c) => {
|
|
3998
|
+
const sessionId = c.req.param("id");
|
|
3999
|
+
const session = sessionQueries.getById(sessionId);
|
|
4000
|
+
if (!session) {
|
|
4001
|
+
return c.json({ error: "Session not found" }, 404);
|
|
4002
|
+
}
|
|
4003
|
+
const dir = getAttachmentsDir(sessionId);
|
|
4004
|
+
if (!existsSync10(dir)) {
|
|
4005
|
+
return c.json({ sessionId, attachments: [], count: 0 });
|
|
4006
|
+
}
|
|
4007
|
+
const files = readdirSync(dir);
|
|
4008
|
+
const attachments = files.map((filename) => {
|
|
4009
|
+
const filePath = join3(dir, filename);
|
|
4010
|
+
const stats = statSync(filePath);
|
|
4011
|
+
return {
|
|
4012
|
+
id: filename.split("_")[0],
|
|
4013
|
+
// Extract the nanoid prefix
|
|
4014
|
+
filename,
|
|
4015
|
+
path: filePath,
|
|
4016
|
+
size: stats.size,
|
|
4017
|
+
createdAt: stats.birthtime.toISOString()
|
|
4018
|
+
};
|
|
4019
|
+
});
|
|
4020
|
+
return c.json({
|
|
4021
|
+
sessionId,
|
|
4022
|
+
attachments,
|
|
4023
|
+
count: attachments.length
|
|
4024
|
+
});
|
|
4025
|
+
});
|
|
4026
|
+
sessions2.post("/:id/attachments", async (c) => {
|
|
4027
|
+
const sessionId = c.req.param("id");
|
|
4028
|
+
const session = sessionQueries.getById(sessionId);
|
|
4029
|
+
if (!session) {
|
|
4030
|
+
return c.json({ error: "Session not found" }, 404);
|
|
4031
|
+
}
|
|
4032
|
+
const contentType = c.req.header("content-type") || "";
|
|
4033
|
+
if (contentType.includes("multipart/form-data")) {
|
|
4034
|
+
try {
|
|
4035
|
+
const formData = await c.req.formData();
|
|
4036
|
+
const file = formData.get("file");
|
|
4037
|
+
if (!file || !(file instanceof File)) {
|
|
4038
|
+
return c.json({ error: "No file provided" }, 400);
|
|
4039
|
+
}
|
|
4040
|
+
const dir = ensureAttachmentsDir(sessionId);
|
|
4041
|
+
const id = nanoid4(10);
|
|
4042
|
+
const ext = extname5(file.name) || "";
|
|
4043
|
+
const safeFilename = `${id}_${basename2(file.name).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
|
|
4044
|
+
const filePath = join3(dir, safeFilename);
|
|
4045
|
+
const arrayBuffer = await file.arrayBuffer();
|
|
4046
|
+
writeFileSync2(filePath, Buffer.from(arrayBuffer));
|
|
4047
|
+
return c.json({
|
|
4048
|
+
id,
|
|
4049
|
+
filename: file.name,
|
|
4050
|
+
storedAs: safeFilename,
|
|
4051
|
+
path: filePath,
|
|
4052
|
+
size: file.size,
|
|
4053
|
+
mediaType: file.type,
|
|
4054
|
+
sessionId
|
|
4055
|
+
}, 201);
|
|
4056
|
+
} catch (err) {
|
|
4057
|
+
console.error("Failed to upload attachment:", err);
|
|
4058
|
+
return c.json({ error: "Failed to upload file" }, 500);
|
|
4059
|
+
}
|
|
4060
|
+
}
|
|
4061
|
+
try {
|
|
4062
|
+
const body = await c.req.json();
|
|
4063
|
+
if (!body.filename || !body.data) {
|
|
4064
|
+
return c.json({ error: "Missing filename or data" }, 400);
|
|
4065
|
+
}
|
|
4066
|
+
const dir = ensureAttachmentsDir(sessionId);
|
|
4067
|
+
const id = nanoid4(10);
|
|
4068
|
+
const ext = extname5(body.filename) || "";
|
|
4069
|
+
const safeFilename = `${id}_${basename2(body.filename).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
|
|
4070
|
+
const filePath = join3(dir, safeFilename);
|
|
4071
|
+
let base64Data = body.data;
|
|
4072
|
+
if (base64Data.includes(",")) {
|
|
4073
|
+
base64Data = base64Data.split(",")[1];
|
|
4074
|
+
}
|
|
4075
|
+
const buffer = Buffer.from(base64Data, "base64");
|
|
4076
|
+
writeFileSync2(filePath, buffer);
|
|
4077
|
+
return c.json({
|
|
4078
|
+
id,
|
|
4079
|
+
filename: body.filename,
|
|
4080
|
+
storedAs: safeFilename,
|
|
4081
|
+
path: filePath,
|
|
4082
|
+
size: buffer.length,
|
|
4083
|
+
mediaType: body.mediaType,
|
|
4084
|
+
sessionId
|
|
4085
|
+
}, 201);
|
|
4086
|
+
} catch (err) {
|
|
4087
|
+
console.error("Failed to upload attachment:", err);
|
|
4088
|
+
return c.json({ error: "Failed to upload file" }, 500);
|
|
4089
|
+
}
|
|
4090
|
+
});
|
|
4091
|
+
sessions2.delete("/:id/attachments/:attachmentId", async (c) => {
|
|
4092
|
+
const sessionId = c.req.param("id");
|
|
4093
|
+
const attachmentId = c.req.param("attachmentId");
|
|
4094
|
+
const session = sessionQueries.getById(sessionId);
|
|
4095
|
+
if (!session) {
|
|
4096
|
+
return c.json({ error: "Session not found" }, 404);
|
|
4097
|
+
}
|
|
4098
|
+
const dir = getAttachmentsDir(sessionId);
|
|
4099
|
+
if (!existsSync10(dir)) {
|
|
4100
|
+
return c.json({ error: "Attachment not found" }, 404);
|
|
4101
|
+
}
|
|
4102
|
+
const files = readdirSync(dir);
|
|
4103
|
+
const file = files.find((f) => f.startsWith(attachmentId + "_"));
|
|
4104
|
+
if (!file) {
|
|
4105
|
+
return c.json({ error: "Attachment not found" }, 404);
|
|
4106
|
+
}
|
|
4107
|
+
const filePath = join3(dir, file);
|
|
4108
|
+
unlinkSync(filePath);
|
|
4109
|
+
return c.json({ success: true, id: attachmentId });
|
|
4110
|
+
});
|
|
3933
4111
|
|
|
3934
4112
|
// src/server/routes/agents.ts
|
|
3935
4113
|
import { Hono as Hono2 } from "hono";
|
|
3936
4114
|
import { zValidator as zValidator2 } from "@hono/zod-validator";
|
|
3937
4115
|
import { z as z10 } from "zod";
|
|
4116
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
4117
|
+
import { join as join4 } from "path";
|
|
3938
4118
|
|
|
3939
4119
|
// src/server/resumable-stream.ts
|
|
3940
4120
|
import { createResumableStreamContext } from "resumable-stream/generic";
|
|
@@ -4009,11 +4189,23 @@ var streamContext = createResumableStreamContext({
|
|
|
4009
4189
|
});
|
|
4010
4190
|
|
|
4011
4191
|
// src/server/routes/agents.ts
|
|
4012
|
-
import { nanoid as
|
|
4192
|
+
import { nanoid as nanoid5 } from "nanoid";
|
|
4013
4193
|
var agents = new Hono2();
|
|
4014
|
-
var
|
|
4015
|
-
|
|
4194
|
+
var attachmentSchema = z10.object({
|
|
4195
|
+
type: z10.enum(["image", "file"]),
|
|
4196
|
+
data: z10.string(),
|
|
4197
|
+
// base64 data URL or raw base64
|
|
4198
|
+
mediaType: z10.string().optional(),
|
|
4199
|
+
filename: z10.string().optional()
|
|
4016
4200
|
});
|
|
4201
|
+
var runPromptSchema = z10.object({
|
|
4202
|
+
prompt: z10.string(),
|
|
4203
|
+
// Can be empty if attachments are provided
|
|
4204
|
+
attachments: z10.array(attachmentSchema).optional()
|
|
4205
|
+
}).refine(
|
|
4206
|
+
(data) => data.prompt.trim().length > 0 || data.attachments && data.attachments.length > 0,
|
|
4207
|
+
{ message: "Either prompt or attachments must be provided" }
|
|
4208
|
+
);
|
|
4017
4209
|
var quickStartSchema = z10.object({
|
|
4018
4210
|
prompt: z10.string().min(1),
|
|
4019
4211
|
name: z10.string().optional(),
|
|
@@ -4025,7 +4217,53 @@ var rejectSchema = z10.object({
|
|
|
4025
4217
|
reason: z10.string().optional()
|
|
4026
4218
|
}).optional();
|
|
4027
4219
|
var streamAbortControllers = /* @__PURE__ */ new Map();
|
|
4028
|
-
function
|
|
4220
|
+
function getAttachmentsDirectory(sessionId) {
|
|
4221
|
+
const appDataDir = getAppDataDirectory();
|
|
4222
|
+
return join4(appDataDir, "attachments", sessionId);
|
|
4223
|
+
}
|
|
4224
|
+
function saveAttachmentToDisk(sessionId, attachment, index) {
|
|
4225
|
+
const attachmentsDir = getAttachmentsDirectory(sessionId);
|
|
4226
|
+
if (!existsSync11(attachmentsDir)) {
|
|
4227
|
+
mkdirSync4(attachmentsDir, { recursive: true });
|
|
4228
|
+
}
|
|
4229
|
+
let filename = attachment.filename;
|
|
4230
|
+
if (!filename) {
|
|
4231
|
+
const ext = getExtensionFromMediaType(attachment.mediaType, attachment.type);
|
|
4232
|
+
filename = `attachment_${index + 1}${ext}`;
|
|
4233
|
+
}
|
|
4234
|
+
let base64Data = attachment.data;
|
|
4235
|
+
if (base64Data.includes(",")) {
|
|
4236
|
+
base64Data = base64Data.split(",")[1];
|
|
4237
|
+
}
|
|
4238
|
+
const filePath = join4(attachmentsDir, filename);
|
|
4239
|
+
const buffer = Buffer.from(base64Data, "base64");
|
|
4240
|
+
writeFileSync3(filePath, buffer);
|
|
4241
|
+
return filePath;
|
|
4242
|
+
}
|
|
4243
|
+
function getExtensionFromMediaType(mediaType, type) {
|
|
4244
|
+
if (!mediaType) {
|
|
4245
|
+
return type === "image" ? ".png" : ".bin";
|
|
4246
|
+
}
|
|
4247
|
+
const mimeToExt = {
|
|
4248
|
+
"image/png": ".png",
|
|
4249
|
+
"image/jpeg": ".jpg",
|
|
4250
|
+
"image/jpg": ".jpg",
|
|
4251
|
+
"image/gif": ".gif",
|
|
4252
|
+
"image/webp": ".webp",
|
|
4253
|
+
"image/svg+xml": ".svg",
|
|
4254
|
+
"application/pdf": ".pdf",
|
|
4255
|
+
"text/plain": ".txt",
|
|
4256
|
+
"text/markdown": ".md",
|
|
4257
|
+
"application/json": ".json",
|
|
4258
|
+
"application/javascript": ".js",
|
|
4259
|
+
"text/javascript": ".js",
|
|
4260
|
+
"text/typescript": ".ts",
|
|
4261
|
+
"text/html": ".html",
|
|
4262
|
+
"text/css": ".css"
|
|
4263
|
+
};
|
|
4264
|
+
return mimeToExt[mediaType] || ".bin";
|
|
4265
|
+
}
|
|
4266
|
+
function createAgentStreamProducer(sessionId, prompt, streamId, attachments) {
|
|
4029
4267
|
return () => {
|
|
4030
4268
|
const { readable, writable } = new TransformStream();
|
|
4031
4269
|
const writer = writable.getWriter();
|
|
@@ -4058,9 +4296,53 @@ function createAgentStreamProducer(sessionId, prompt, streamId) {
|
|
|
4058
4296
|
try {
|
|
4059
4297
|
const agent = await Agent.create({ sessionId });
|
|
4060
4298
|
await writeSSE(JSON.stringify({ type: "data-stream-id", streamId }));
|
|
4299
|
+
let broadcastContent;
|
|
4300
|
+
if (attachments && attachments.length > 0) {
|
|
4301
|
+
const contentParts = [];
|
|
4302
|
+
const attachmentDescriptions = attachments.map((a, i) => {
|
|
4303
|
+
const name = a.filename || `attachment_${i + 1}`;
|
|
4304
|
+
const typeLabel = a.type === "image" ? "Image" : "File";
|
|
4305
|
+
const location = a.savedPath || "(path unknown)";
|
|
4306
|
+
return `${i + 1}. ${typeLabel}: "${name}" saved at: ${location}`;
|
|
4307
|
+
}).join("\n");
|
|
4308
|
+
contentParts.push({
|
|
4309
|
+
type: "text",
|
|
4310
|
+
text: `[FILE ATTACHMENTS - The user has attached the following files which are saved on disk]
|
|
4311
|
+
${attachmentDescriptions}
|
|
4312
|
+
|
|
4313
|
+
You can reference these files by their paths above. The file contents are also shown inline below.`
|
|
4314
|
+
});
|
|
4315
|
+
if (prompt) {
|
|
4316
|
+
contentParts.push({ type: "text", text: `
|
|
4317
|
+
[USER MESSAGE]
|
|
4318
|
+
${prompt}` });
|
|
4319
|
+
}
|
|
4320
|
+
for (const attachment of attachments) {
|
|
4321
|
+
if (attachment.type === "image") {
|
|
4322
|
+
contentParts.push({
|
|
4323
|
+
type: "image",
|
|
4324
|
+
image: attachment.data,
|
|
4325
|
+
mediaType: attachment.mediaType,
|
|
4326
|
+
filename: attachment.filename,
|
|
4327
|
+
savedPath: attachment.savedPath
|
|
4328
|
+
});
|
|
4329
|
+
} else {
|
|
4330
|
+
contentParts.push({
|
|
4331
|
+
type: "file",
|
|
4332
|
+
data: attachment.data,
|
|
4333
|
+
mediaType: attachment.mediaType || "application/octet-stream",
|
|
4334
|
+
filename: attachment.filename,
|
|
4335
|
+
savedPath: attachment.savedPath
|
|
4336
|
+
});
|
|
4337
|
+
}
|
|
4338
|
+
}
|
|
4339
|
+
broadcastContent = contentParts;
|
|
4340
|
+
} else {
|
|
4341
|
+
broadcastContent = prompt;
|
|
4342
|
+
}
|
|
4061
4343
|
await writeSSE(JSON.stringify({
|
|
4062
4344
|
type: "data-user-message",
|
|
4063
|
-
data: { id: `user_${Date.now()}`, content:
|
|
4345
|
+
data: { id: `user_${Date.now()}`, content: broadcastContent }
|
|
4064
4346
|
}));
|
|
4065
4347
|
const messageId = `msg_${Date.now()}`;
|
|
4066
4348
|
await writeSSE(JSON.stringify({ type: "start", messageId }));
|
|
@@ -4068,6 +4350,8 @@ function createAgentStreamProducer(sessionId, prompt, streamId) {
|
|
|
4068
4350
|
let textStarted = false;
|
|
4069
4351
|
const result = await agent.stream({
|
|
4070
4352
|
prompt,
|
|
4353
|
+
attachments,
|
|
4354
|
+
// Pass attachments to agent
|
|
4071
4355
|
abortSignal: abortController.signal,
|
|
4072
4356
|
// Use our managed abort controller, NOT client signal
|
|
4073
4357
|
skipSaveUserMessage: true,
|
|
@@ -4177,14 +4461,20 @@ function createAgentStreamProducer(sessionId, prompt, streamId) {
|
|
|
4177
4461
|
} else {
|
|
4178
4462
|
await writeSSE(JSON.stringify({ type: "finish" }));
|
|
4179
4463
|
}
|
|
4180
|
-
|
|
4464
|
+
try {
|
|
4465
|
+
activeStreamQueries.finish(streamId);
|
|
4466
|
+
} catch {
|
|
4467
|
+
}
|
|
4181
4468
|
} catch (error) {
|
|
4182
4469
|
if (error.name === "AbortError" || error.message?.includes("aborted")) {
|
|
4183
4470
|
await writeSSE(JSON.stringify({ type: "abort" }));
|
|
4184
4471
|
} else {
|
|
4185
4472
|
console.error("Agent error:", error);
|
|
4186
4473
|
await writeSSE(JSON.stringify({ type: "error", errorText: error.message }));
|
|
4187
|
-
|
|
4474
|
+
try {
|
|
4475
|
+
activeStreamQueries.markError(streamId);
|
|
4476
|
+
} catch {
|
|
4477
|
+
}
|
|
4188
4478
|
}
|
|
4189
4479
|
} finally {
|
|
4190
4480
|
cleanupAbortController();
|
|
@@ -4200,19 +4490,74 @@ agents.post(
|
|
|
4200
4490
|
zValidator2("json", runPromptSchema),
|
|
4201
4491
|
async (c) => {
|
|
4202
4492
|
const id = c.req.param("id");
|
|
4203
|
-
const { prompt } = c.req.valid("json");
|
|
4493
|
+
const { prompt, attachments } = c.req.valid("json");
|
|
4204
4494
|
const session = sessionQueries.getById(id);
|
|
4205
4495
|
if (!session) {
|
|
4206
4496
|
return c.json({ error: "Session not found" }, 404);
|
|
4207
4497
|
}
|
|
4208
4498
|
const nextSequence = messageQueries.getNextSequence(id);
|
|
4209
4499
|
await createCheckpoint(id, session.workingDirectory, nextSequence);
|
|
4210
|
-
|
|
4211
|
-
const
|
|
4500
|
+
let userMessageContent;
|
|
4501
|
+
const streamAttachments = attachments;
|
|
4502
|
+
if (streamAttachments && streamAttachments.length > 0) {
|
|
4503
|
+
for (let i = 0; i < streamAttachments.length; i++) {
|
|
4504
|
+
const attachment = streamAttachments[i];
|
|
4505
|
+
try {
|
|
4506
|
+
const savedPath = saveAttachmentToDisk(id, attachment, i);
|
|
4507
|
+
attachment.savedPath = savedPath;
|
|
4508
|
+
} catch (err) {
|
|
4509
|
+
console.error(`Failed to save attachment ${i}:`, err);
|
|
4510
|
+
}
|
|
4511
|
+
}
|
|
4512
|
+
const contentParts = [];
|
|
4513
|
+
const attachmentDescriptions = streamAttachments.map((a, i) => {
|
|
4514
|
+
const name = a.filename || `attachment_${i + 1}`;
|
|
4515
|
+
const typeLabel = a.type === "image" ? "Image" : "File";
|
|
4516
|
+
const location = a.savedPath || "(path unknown)";
|
|
4517
|
+
return `${i + 1}. ${typeLabel}: "${name}" saved at: ${location}`;
|
|
4518
|
+
}).join("\n");
|
|
4519
|
+
contentParts.push({
|
|
4520
|
+
type: "text",
|
|
4521
|
+
text: `[FILE ATTACHMENTS - The user has attached the following files which are saved on disk]
|
|
4522
|
+
${attachmentDescriptions}
|
|
4523
|
+
|
|
4524
|
+
You can reference these files by their paths above. The file contents are also shown inline below.`
|
|
4525
|
+
});
|
|
4526
|
+
if (prompt) {
|
|
4527
|
+
contentParts.push({ type: "text", text: `
|
|
4528
|
+
[USER MESSAGE]
|
|
4529
|
+
${prompt}` });
|
|
4530
|
+
}
|
|
4531
|
+
for (const attachment of streamAttachments) {
|
|
4532
|
+
if (attachment.type === "image") {
|
|
4533
|
+
contentParts.push({
|
|
4534
|
+
type: "image",
|
|
4535
|
+
image: attachment.data,
|
|
4536
|
+
// base64 data URL or raw base64
|
|
4537
|
+
mediaType: attachment.mediaType,
|
|
4538
|
+
filename: attachment.filename,
|
|
4539
|
+
savedPath: attachment.savedPath
|
|
4540
|
+
});
|
|
4541
|
+
} else {
|
|
4542
|
+
contentParts.push({
|
|
4543
|
+
type: "file",
|
|
4544
|
+
data: attachment.data,
|
|
4545
|
+
mediaType: attachment.mediaType || "application/octet-stream",
|
|
4546
|
+
filename: attachment.filename,
|
|
4547
|
+
savedPath: attachment.savedPath
|
|
4548
|
+
});
|
|
4549
|
+
}
|
|
4550
|
+
}
|
|
4551
|
+
userMessageContent = contentParts;
|
|
4552
|
+
} else {
|
|
4553
|
+
userMessageContent = prompt;
|
|
4554
|
+
}
|
|
4555
|
+
messageQueries.create(id, { role: "user", content: userMessageContent });
|
|
4556
|
+
const streamId = `stream_${id}_${nanoid5(10)}`;
|
|
4212
4557
|
activeStreamQueries.create(id, streamId);
|
|
4213
4558
|
const stream = await streamContext.resumableStream(
|
|
4214
4559
|
streamId,
|
|
4215
|
-
createAgentStreamProducer(id, prompt, streamId)
|
|
4560
|
+
createAgentStreamProducer(id, prompt, streamId, streamAttachments)
|
|
4216
4561
|
);
|
|
4217
4562
|
if (!stream) {
|
|
4218
4563
|
return c.json({ error: "Failed to create stream" }, 500);
|
|
@@ -4405,7 +4750,7 @@ agents.post(
|
|
|
4405
4750
|
sessionConfig: body.toolApprovals ? { toolApprovals: body.toolApprovals } : void 0
|
|
4406
4751
|
});
|
|
4407
4752
|
const session = agent.getSession();
|
|
4408
|
-
const streamId = `stream_${session.id}_${
|
|
4753
|
+
const streamId = `stream_${session.id}_${nanoid5(10)}`;
|
|
4409
4754
|
await createCheckpoint(session.id, session.workingDirectory, 0);
|
|
4410
4755
|
activeStreamQueries.create(session.id, streamId);
|
|
4411
4756
|
const createQuickStreamProducer = () => {
|
|
@@ -5064,11 +5409,11 @@ function getWebDirectory() {
|
|
|
5064
5409
|
try {
|
|
5065
5410
|
const currentDir = dirname6(fileURLToPath2(import.meta.url));
|
|
5066
5411
|
const webDir = resolve8(currentDir, "..", "web");
|
|
5067
|
-
if (
|
|
5412
|
+
if (existsSync12(webDir) && existsSync12(join5(webDir, "package.json"))) {
|
|
5068
5413
|
return webDir;
|
|
5069
5414
|
}
|
|
5070
5415
|
const altWebDir = resolve8(currentDir, "..", "..", "web");
|
|
5071
|
-
if (
|
|
5416
|
+
if (existsSync12(altWebDir) && existsSync12(join5(altWebDir, "package.json"))) {
|
|
5072
5417
|
return altWebDir;
|
|
5073
5418
|
}
|
|
5074
5419
|
return null;
|
|
@@ -5126,23 +5471,23 @@ async function findWebPort(preferredPort) {
|
|
|
5126
5471
|
return { port: preferredPort, alreadyRunning: false };
|
|
5127
5472
|
}
|
|
5128
5473
|
function hasProductionBuild(webDir) {
|
|
5129
|
-
const buildIdPath =
|
|
5130
|
-
return
|
|
5474
|
+
const buildIdPath = join5(webDir, ".next", "BUILD_ID");
|
|
5475
|
+
return existsSync12(buildIdPath);
|
|
5131
5476
|
}
|
|
5132
5477
|
function hasSourceFiles(webDir) {
|
|
5133
|
-
const appDir =
|
|
5134
|
-
const pagesDir =
|
|
5135
|
-
const rootAppDir =
|
|
5136
|
-
const rootPagesDir =
|
|
5137
|
-
return
|
|
5478
|
+
const appDir = join5(webDir, "src", "app");
|
|
5479
|
+
const pagesDir = join5(webDir, "src", "pages");
|
|
5480
|
+
const rootAppDir = join5(webDir, "app");
|
|
5481
|
+
const rootPagesDir = join5(webDir, "pages");
|
|
5482
|
+
return existsSync12(appDir) || existsSync12(pagesDir) || existsSync12(rootAppDir) || existsSync12(rootPagesDir);
|
|
5138
5483
|
}
|
|
5139
5484
|
function getStandaloneServerPath(webDir) {
|
|
5140
5485
|
const possiblePaths = [
|
|
5141
|
-
|
|
5142
|
-
|
|
5486
|
+
join5(webDir, ".next", "standalone", "server.js"),
|
|
5487
|
+
join5(webDir, ".next", "standalone", "web", "server.js")
|
|
5143
5488
|
];
|
|
5144
5489
|
for (const serverPath of possiblePaths) {
|
|
5145
|
-
if (
|
|
5490
|
+
if (existsSync12(serverPath)) {
|
|
5146
5491
|
return serverPath;
|
|
5147
5492
|
}
|
|
5148
5493
|
}
|
|
@@ -5182,15 +5527,15 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
5182
5527
|
if (!quiet) console.log(` \u2713 Web UI already running at http://localhost:${actualPort}`);
|
|
5183
5528
|
return { process: null, port: actualPort };
|
|
5184
5529
|
}
|
|
5185
|
-
const usePnpm =
|
|
5186
|
-
const useNpm = !usePnpm &&
|
|
5530
|
+
const usePnpm = existsSync12(join5(webDir, "pnpm-lock.yaml"));
|
|
5531
|
+
const useNpm = !usePnpm && existsSync12(join5(webDir, "package-lock.json"));
|
|
5187
5532
|
const pkgManager = usePnpm ? "pnpm" : useNpm ? "npm" : "npx";
|
|
5188
5533
|
const { NODE_OPTIONS, TSX_TSCONFIG_PATH, ...cleanEnv } = process.env;
|
|
5189
5534
|
const apiUrl = publicUrl || `http://127.0.0.1:${apiPort}`;
|
|
5190
5535
|
const runtimeConfig = { apiBaseUrl: apiUrl };
|
|
5191
|
-
const runtimeConfigPath =
|
|
5536
|
+
const runtimeConfigPath = join5(webDir, "runtime-config.json");
|
|
5192
5537
|
try {
|
|
5193
|
-
|
|
5538
|
+
writeFileSync4(runtimeConfigPath, JSON.stringify(runtimeConfig, null, 2));
|
|
5194
5539
|
if (!quiet) console.log(` \u{1F4DD} Runtime config written to ${runtimeConfigPath}`);
|
|
5195
5540
|
} catch (err) {
|
|
5196
5541
|
if (!quiet) console.warn(` \u26A0 Could not write runtime config: ${err}`);
|
|
@@ -5216,7 +5561,7 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
5216
5561
|
if (!quiet) console.log(" \u{1F4E6} Starting Web UI from standalone build...");
|
|
5217
5562
|
} else if (hasBuild && (isProduction || !hasSource)) {
|
|
5218
5563
|
command = pkgManager;
|
|
5219
|
-
args = pkgManager === "npx" ? ["next", "start", "-p", String(actualPort)] : ["run", "start"
|
|
5564
|
+
args = pkgManager === "npx" ? ["next", "start", "-p", String(actualPort)] : ["run", "start"];
|
|
5220
5565
|
} else if (hasSource) {
|
|
5221
5566
|
if (isProduction && !hasBuild) {
|
|
5222
5567
|
if (!quiet) console.log(" \u{1F4E6} Building Web UI for production...");
|
|
@@ -5228,10 +5573,10 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
5228
5573
|
}
|
|
5229
5574
|
if (!quiet) console.log(" \u2713 Web UI build complete");
|
|
5230
5575
|
command = pkgManager;
|
|
5231
|
-
args = pkgManager === "npx" ? ["next", "start", "-p", String(actualPort)] : ["run", "start"
|
|
5576
|
+
args = pkgManager === "npx" ? ["next", "start", "-p", String(actualPort)] : ["run", "start"];
|
|
5232
5577
|
} else {
|
|
5233
5578
|
command = pkgManager;
|
|
5234
|
-
args = pkgManager === "npx" ? ["next", "dev", "-p", String(actualPort)] : ["run", "dev"
|
|
5579
|
+
args = pkgManager === "npx" ? ["next", "dev", "-p", String(actualPort)] : ["run", "dev"];
|
|
5235
5580
|
}
|
|
5236
5581
|
} else {
|
|
5237
5582
|
if (!quiet) {
|
|
@@ -5259,6 +5604,12 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
5259
5604
|
}, startupTimeout);
|
|
5260
5605
|
child.stdout?.on("data", (data) => {
|
|
5261
5606
|
const output = data.toString();
|
|
5607
|
+
if (!quiet) {
|
|
5608
|
+
const lines = output.trim().split("\n").filter((l) => l.trim());
|
|
5609
|
+
for (const line of lines) {
|
|
5610
|
+
console.log(` Web UI: ${line}`);
|
|
5611
|
+
}
|
|
5612
|
+
}
|
|
5262
5613
|
if (!started && (output.includes("Ready") || output.includes("started") || output.includes("localhost"))) {
|
|
5263
5614
|
started = true;
|
|
5264
5615
|
clearTimeout(timeout);
|
|
@@ -5266,9 +5617,9 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
5266
5617
|
}
|
|
5267
5618
|
});
|
|
5268
5619
|
child.stderr?.on("data", (data) => {
|
|
5269
|
-
const output = data.toString();
|
|
5270
|
-
if (output
|
|
5271
|
-
|
|
5620
|
+
const output = data.toString().trim();
|
|
5621
|
+
if (!quiet && output) {
|
|
5622
|
+
console.error(` Web UI: ${output.slice(0, 500)}`);
|
|
5272
5623
|
}
|
|
5273
5624
|
});
|
|
5274
5625
|
child.on("error", (err) => {
|
|
@@ -5374,8 +5725,8 @@ async function startServer(options = {}) {
|
|
|
5374
5725
|
if (options.workingDirectory) {
|
|
5375
5726
|
config.resolvedWorkingDirectory = options.workingDirectory;
|
|
5376
5727
|
}
|
|
5377
|
-
if (!
|
|
5378
|
-
|
|
5728
|
+
if (!existsSync12(config.resolvedWorkingDirectory)) {
|
|
5729
|
+
mkdirSync5(config.resolvedWorkingDirectory, { recursive: true });
|
|
5379
5730
|
if (!options.quiet) console.log(`\u{1F4C1} Created agent workspace: ${config.resolvedWorkingDirectory}`);
|
|
5380
5731
|
}
|
|
5381
5732
|
initDatabase(config.resolvedDatabasePath);
|