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
package/dist/cli.js
CHANGED
|
@@ -18,8 +18,8 @@ import { Hono as Hono5 } from "hono";
|
|
|
18
18
|
import { serve } from "@hono/node-server";
|
|
19
19
|
import { cors } from "hono/cors";
|
|
20
20
|
import { logger } from "hono/logger";
|
|
21
|
-
import { existsSync as
|
|
22
|
-
import { resolve as resolve8, dirname as dirname6, join as
|
|
21
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
22
|
+
import { resolve as resolve8, dirname as dirname6, join as join5 } from "path";
|
|
23
23
|
import { spawn as spawn2 } from "child_process";
|
|
24
24
|
import { createServer as createNetServer } from "net";
|
|
25
25
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
@@ -28,6 +28,9 @@ import { fileURLToPath as fileURLToPath2 } from "url";
|
|
|
28
28
|
import { Hono } from "hono";
|
|
29
29
|
import { zValidator } from "@hono/zod-validator";
|
|
30
30
|
import { z as z9 } from "zod";
|
|
31
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync3, writeFileSync as writeFileSync2, readdirSync, statSync, unlinkSync } from "fs";
|
|
32
|
+
import { join as join3, basename as basename2, extname as extname5 } from "path";
|
|
33
|
+
import { nanoid as nanoid4 } from "nanoid";
|
|
31
34
|
|
|
32
35
|
// src/db/index.ts
|
|
33
36
|
import Database from "better-sqlite3";
|
|
@@ -3295,11 +3298,12 @@ ${this.summary}`
|
|
|
3295
3298
|
}
|
|
3296
3299
|
/**
|
|
3297
3300
|
* Add a user message to the context
|
|
3301
|
+
* Content can be a string or an array of content parts (for messages with images/files)
|
|
3298
3302
|
*/
|
|
3299
|
-
addUserMessage(
|
|
3303
|
+
addUserMessage(content) {
|
|
3300
3304
|
const userMessage = {
|
|
3301
3305
|
role: "user",
|
|
3302
|
-
content
|
|
3306
|
+
content
|
|
3303
3307
|
};
|
|
3304
3308
|
messageQueries.create(this.sessionId, userMessage);
|
|
3305
3309
|
}
|
|
@@ -3399,13 +3403,62 @@ var Agent = class _Agent {
|
|
|
3399
3403
|
getSession() {
|
|
3400
3404
|
return this.session;
|
|
3401
3405
|
}
|
|
3406
|
+
/**
|
|
3407
|
+
* Build user message content from prompt and attachments
|
|
3408
|
+
*/
|
|
3409
|
+
buildUserMessageContent(prompt, attachments) {
|
|
3410
|
+
if (!attachments || attachments.length === 0) {
|
|
3411
|
+
return prompt;
|
|
3412
|
+
}
|
|
3413
|
+
const contentParts = [];
|
|
3414
|
+
const attachmentDescriptions = attachments.map((a, i) => {
|
|
3415
|
+
const name = a.filename || `attachment_${i + 1}`;
|
|
3416
|
+
const typeLabel = a.type === "image" ? "Image" : "File";
|
|
3417
|
+
const location = a.savedPath || "(path unknown)";
|
|
3418
|
+
return `${i + 1}. ${typeLabel}: "${name}" saved at: ${location}`;
|
|
3419
|
+
}).join("\n");
|
|
3420
|
+
contentParts.push({
|
|
3421
|
+
type: "text",
|
|
3422
|
+
text: `[FILE ATTACHMENTS - The user has attached the following files which are saved on disk]
|
|
3423
|
+
${attachmentDescriptions}
|
|
3424
|
+
|
|
3425
|
+
You can reference these files by their paths above. The file contents are also shown inline below.`
|
|
3426
|
+
});
|
|
3427
|
+
if (prompt) {
|
|
3428
|
+
contentParts.push({ type: "text", text: `
|
|
3429
|
+
[USER MESSAGE]
|
|
3430
|
+
${prompt}` });
|
|
3431
|
+
}
|
|
3432
|
+
for (const attachment of attachments) {
|
|
3433
|
+
if (attachment.type === "image") {
|
|
3434
|
+
contentParts.push({
|
|
3435
|
+
type: "image",
|
|
3436
|
+
image: attachment.data,
|
|
3437
|
+
// base64 data URL or raw base64
|
|
3438
|
+
mediaType: attachment.mediaType,
|
|
3439
|
+
filename: attachment.filename,
|
|
3440
|
+
savedPath: attachment.savedPath
|
|
3441
|
+
});
|
|
3442
|
+
} else {
|
|
3443
|
+
contentParts.push({
|
|
3444
|
+
type: "file",
|
|
3445
|
+
data: attachment.data,
|
|
3446
|
+
mediaType: attachment.mediaType || "application/octet-stream",
|
|
3447
|
+
filename: attachment.filename,
|
|
3448
|
+
savedPath: attachment.savedPath
|
|
3449
|
+
});
|
|
3450
|
+
}
|
|
3451
|
+
}
|
|
3452
|
+
return contentParts;
|
|
3453
|
+
}
|
|
3402
3454
|
/**
|
|
3403
3455
|
* Run the agent with a prompt (streaming)
|
|
3404
3456
|
*/
|
|
3405
3457
|
async stream(options) {
|
|
3406
3458
|
const config = getConfig();
|
|
3459
|
+
const userContent = this.buildUserMessageContent(options.prompt, options.attachments);
|
|
3407
3460
|
if (!options.skipSaveUserMessage) {
|
|
3408
|
-
this.context.addUserMessage(
|
|
3461
|
+
this.context.addUserMessage(userContent);
|
|
3409
3462
|
}
|
|
3410
3463
|
sessionQueries.updateStatus(this.session.id, "active");
|
|
3411
3464
|
const systemPrompt = await buildSystemPrompt({
|
|
@@ -3965,11 +4018,138 @@ sessions2.get("/:id/diff/:filePath", async (c) => {
|
|
|
3965
4018
|
currentContent: fileDiff.currentContent
|
|
3966
4019
|
});
|
|
3967
4020
|
});
|
|
4021
|
+
function getAttachmentsDir(sessionId) {
|
|
4022
|
+
const appDataDir = getAppDataDirectory();
|
|
4023
|
+
return join3(appDataDir, "attachments", sessionId);
|
|
4024
|
+
}
|
|
4025
|
+
function ensureAttachmentsDir(sessionId) {
|
|
4026
|
+
const dir = getAttachmentsDir(sessionId);
|
|
4027
|
+
if (!existsSync10(dir)) {
|
|
4028
|
+
mkdirSync3(dir, { recursive: true });
|
|
4029
|
+
}
|
|
4030
|
+
return dir;
|
|
4031
|
+
}
|
|
4032
|
+
sessions2.get("/:id/attachments", async (c) => {
|
|
4033
|
+
const sessionId = c.req.param("id");
|
|
4034
|
+
const session = sessionQueries.getById(sessionId);
|
|
4035
|
+
if (!session) {
|
|
4036
|
+
return c.json({ error: "Session not found" }, 404);
|
|
4037
|
+
}
|
|
4038
|
+
const dir = getAttachmentsDir(sessionId);
|
|
4039
|
+
if (!existsSync10(dir)) {
|
|
4040
|
+
return c.json({ sessionId, attachments: [], count: 0 });
|
|
4041
|
+
}
|
|
4042
|
+
const files = readdirSync(dir);
|
|
4043
|
+
const attachments = files.map((filename) => {
|
|
4044
|
+
const filePath = join3(dir, filename);
|
|
4045
|
+
const stats = statSync(filePath);
|
|
4046
|
+
return {
|
|
4047
|
+
id: filename.split("_")[0],
|
|
4048
|
+
// Extract the nanoid prefix
|
|
4049
|
+
filename,
|
|
4050
|
+
path: filePath,
|
|
4051
|
+
size: stats.size,
|
|
4052
|
+
createdAt: stats.birthtime.toISOString()
|
|
4053
|
+
};
|
|
4054
|
+
});
|
|
4055
|
+
return c.json({
|
|
4056
|
+
sessionId,
|
|
4057
|
+
attachments,
|
|
4058
|
+
count: attachments.length
|
|
4059
|
+
});
|
|
4060
|
+
});
|
|
4061
|
+
sessions2.post("/:id/attachments", async (c) => {
|
|
4062
|
+
const sessionId = c.req.param("id");
|
|
4063
|
+
const session = sessionQueries.getById(sessionId);
|
|
4064
|
+
if (!session) {
|
|
4065
|
+
return c.json({ error: "Session not found" }, 404);
|
|
4066
|
+
}
|
|
4067
|
+
const contentType = c.req.header("content-type") || "";
|
|
4068
|
+
if (contentType.includes("multipart/form-data")) {
|
|
4069
|
+
try {
|
|
4070
|
+
const formData = await c.req.formData();
|
|
4071
|
+
const file = formData.get("file");
|
|
4072
|
+
if (!file || !(file instanceof File)) {
|
|
4073
|
+
return c.json({ error: "No file provided" }, 400);
|
|
4074
|
+
}
|
|
4075
|
+
const dir = ensureAttachmentsDir(sessionId);
|
|
4076
|
+
const id = nanoid4(10);
|
|
4077
|
+
const ext = extname5(file.name) || "";
|
|
4078
|
+
const safeFilename = `${id}_${basename2(file.name).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
|
|
4079
|
+
const filePath = join3(dir, safeFilename);
|
|
4080
|
+
const arrayBuffer = await file.arrayBuffer();
|
|
4081
|
+
writeFileSync2(filePath, Buffer.from(arrayBuffer));
|
|
4082
|
+
return c.json({
|
|
4083
|
+
id,
|
|
4084
|
+
filename: file.name,
|
|
4085
|
+
storedAs: safeFilename,
|
|
4086
|
+
path: filePath,
|
|
4087
|
+
size: file.size,
|
|
4088
|
+
mediaType: file.type,
|
|
4089
|
+
sessionId
|
|
4090
|
+
}, 201);
|
|
4091
|
+
} catch (err) {
|
|
4092
|
+
console.error("Failed to upload attachment:", err);
|
|
4093
|
+
return c.json({ error: "Failed to upload file" }, 500);
|
|
4094
|
+
}
|
|
4095
|
+
}
|
|
4096
|
+
try {
|
|
4097
|
+
const body = await c.req.json();
|
|
4098
|
+
if (!body.filename || !body.data) {
|
|
4099
|
+
return c.json({ error: "Missing filename or data" }, 400);
|
|
4100
|
+
}
|
|
4101
|
+
const dir = ensureAttachmentsDir(sessionId);
|
|
4102
|
+
const id = nanoid4(10);
|
|
4103
|
+
const ext = extname5(body.filename) || "";
|
|
4104
|
+
const safeFilename = `${id}_${basename2(body.filename).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
|
|
4105
|
+
const filePath = join3(dir, safeFilename);
|
|
4106
|
+
let base64Data = body.data;
|
|
4107
|
+
if (base64Data.includes(",")) {
|
|
4108
|
+
base64Data = base64Data.split(",")[1];
|
|
4109
|
+
}
|
|
4110
|
+
const buffer = Buffer.from(base64Data, "base64");
|
|
4111
|
+
writeFileSync2(filePath, buffer);
|
|
4112
|
+
return c.json({
|
|
4113
|
+
id,
|
|
4114
|
+
filename: body.filename,
|
|
4115
|
+
storedAs: safeFilename,
|
|
4116
|
+
path: filePath,
|
|
4117
|
+
size: buffer.length,
|
|
4118
|
+
mediaType: body.mediaType,
|
|
4119
|
+
sessionId
|
|
4120
|
+
}, 201);
|
|
4121
|
+
} catch (err) {
|
|
4122
|
+
console.error("Failed to upload attachment:", err);
|
|
4123
|
+
return c.json({ error: "Failed to upload file" }, 500);
|
|
4124
|
+
}
|
|
4125
|
+
});
|
|
4126
|
+
sessions2.delete("/:id/attachments/:attachmentId", async (c) => {
|
|
4127
|
+
const sessionId = c.req.param("id");
|
|
4128
|
+
const attachmentId = c.req.param("attachmentId");
|
|
4129
|
+
const session = sessionQueries.getById(sessionId);
|
|
4130
|
+
if (!session) {
|
|
4131
|
+
return c.json({ error: "Session not found" }, 404);
|
|
4132
|
+
}
|
|
4133
|
+
const dir = getAttachmentsDir(sessionId);
|
|
4134
|
+
if (!existsSync10(dir)) {
|
|
4135
|
+
return c.json({ error: "Attachment not found" }, 404);
|
|
4136
|
+
}
|
|
4137
|
+
const files = readdirSync(dir);
|
|
4138
|
+
const file = files.find((f) => f.startsWith(attachmentId + "_"));
|
|
4139
|
+
if (!file) {
|
|
4140
|
+
return c.json({ error: "Attachment not found" }, 404);
|
|
4141
|
+
}
|
|
4142
|
+
const filePath = join3(dir, file);
|
|
4143
|
+
unlinkSync(filePath);
|
|
4144
|
+
return c.json({ success: true, id: attachmentId });
|
|
4145
|
+
});
|
|
3968
4146
|
|
|
3969
4147
|
// src/server/routes/agents.ts
|
|
3970
4148
|
import { Hono as Hono2 } from "hono";
|
|
3971
4149
|
import { zValidator as zValidator2 } from "@hono/zod-validator";
|
|
3972
4150
|
import { z as z10 } from "zod";
|
|
4151
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
4152
|
+
import { join as join4 } from "path";
|
|
3973
4153
|
|
|
3974
4154
|
// src/server/resumable-stream.ts
|
|
3975
4155
|
import { createResumableStreamContext } from "resumable-stream/generic";
|
|
@@ -4044,11 +4224,23 @@ var streamContext = createResumableStreamContext({
|
|
|
4044
4224
|
});
|
|
4045
4225
|
|
|
4046
4226
|
// src/server/routes/agents.ts
|
|
4047
|
-
import { nanoid as
|
|
4227
|
+
import { nanoid as nanoid5 } from "nanoid";
|
|
4048
4228
|
var agents = new Hono2();
|
|
4049
|
-
var
|
|
4050
|
-
|
|
4229
|
+
var attachmentSchema = z10.object({
|
|
4230
|
+
type: z10.enum(["image", "file"]),
|
|
4231
|
+
data: z10.string(),
|
|
4232
|
+
// base64 data URL or raw base64
|
|
4233
|
+
mediaType: z10.string().optional(),
|
|
4234
|
+
filename: z10.string().optional()
|
|
4051
4235
|
});
|
|
4236
|
+
var runPromptSchema = z10.object({
|
|
4237
|
+
prompt: z10.string(),
|
|
4238
|
+
// Can be empty if attachments are provided
|
|
4239
|
+
attachments: z10.array(attachmentSchema).optional()
|
|
4240
|
+
}).refine(
|
|
4241
|
+
(data) => data.prompt.trim().length > 0 || data.attachments && data.attachments.length > 0,
|
|
4242
|
+
{ message: "Either prompt or attachments must be provided" }
|
|
4243
|
+
);
|
|
4052
4244
|
var quickStartSchema = z10.object({
|
|
4053
4245
|
prompt: z10.string().min(1),
|
|
4054
4246
|
name: z10.string().optional(),
|
|
@@ -4060,7 +4252,53 @@ var rejectSchema = z10.object({
|
|
|
4060
4252
|
reason: z10.string().optional()
|
|
4061
4253
|
}).optional();
|
|
4062
4254
|
var streamAbortControllers = /* @__PURE__ */ new Map();
|
|
4063
|
-
function
|
|
4255
|
+
function getAttachmentsDirectory(sessionId) {
|
|
4256
|
+
const appDataDir = getAppDataDirectory();
|
|
4257
|
+
return join4(appDataDir, "attachments", sessionId);
|
|
4258
|
+
}
|
|
4259
|
+
function saveAttachmentToDisk(sessionId, attachment, index) {
|
|
4260
|
+
const attachmentsDir = getAttachmentsDirectory(sessionId);
|
|
4261
|
+
if (!existsSync11(attachmentsDir)) {
|
|
4262
|
+
mkdirSync4(attachmentsDir, { recursive: true });
|
|
4263
|
+
}
|
|
4264
|
+
let filename = attachment.filename;
|
|
4265
|
+
if (!filename) {
|
|
4266
|
+
const ext = getExtensionFromMediaType(attachment.mediaType, attachment.type);
|
|
4267
|
+
filename = `attachment_${index + 1}${ext}`;
|
|
4268
|
+
}
|
|
4269
|
+
let base64Data = attachment.data;
|
|
4270
|
+
if (base64Data.includes(",")) {
|
|
4271
|
+
base64Data = base64Data.split(",")[1];
|
|
4272
|
+
}
|
|
4273
|
+
const filePath = join4(attachmentsDir, filename);
|
|
4274
|
+
const buffer = Buffer.from(base64Data, "base64");
|
|
4275
|
+
writeFileSync3(filePath, buffer);
|
|
4276
|
+
return filePath;
|
|
4277
|
+
}
|
|
4278
|
+
function getExtensionFromMediaType(mediaType, type) {
|
|
4279
|
+
if (!mediaType) {
|
|
4280
|
+
return type === "image" ? ".png" : ".bin";
|
|
4281
|
+
}
|
|
4282
|
+
const mimeToExt = {
|
|
4283
|
+
"image/png": ".png",
|
|
4284
|
+
"image/jpeg": ".jpg",
|
|
4285
|
+
"image/jpg": ".jpg",
|
|
4286
|
+
"image/gif": ".gif",
|
|
4287
|
+
"image/webp": ".webp",
|
|
4288
|
+
"image/svg+xml": ".svg",
|
|
4289
|
+
"application/pdf": ".pdf",
|
|
4290
|
+
"text/plain": ".txt",
|
|
4291
|
+
"text/markdown": ".md",
|
|
4292
|
+
"application/json": ".json",
|
|
4293
|
+
"application/javascript": ".js",
|
|
4294
|
+
"text/javascript": ".js",
|
|
4295
|
+
"text/typescript": ".ts",
|
|
4296
|
+
"text/html": ".html",
|
|
4297
|
+
"text/css": ".css"
|
|
4298
|
+
};
|
|
4299
|
+
return mimeToExt[mediaType] || ".bin";
|
|
4300
|
+
}
|
|
4301
|
+
function createAgentStreamProducer(sessionId, prompt, streamId, attachments) {
|
|
4064
4302
|
return () => {
|
|
4065
4303
|
const { readable, writable } = new TransformStream();
|
|
4066
4304
|
const writer = writable.getWriter();
|
|
@@ -4093,9 +4331,53 @@ function createAgentStreamProducer(sessionId, prompt, streamId) {
|
|
|
4093
4331
|
try {
|
|
4094
4332
|
const agent = await Agent.create({ sessionId });
|
|
4095
4333
|
await writeSSE(JSON.stringify({ type: "data-stream-id", streamId }));
|
|
4334
|
+
let broadcastContent;
|
|
4335
|
+
if (attachments && attachments.length > 0) {
|
|
4336
|
+
const contentParts = [];
|
|
4337
|
+
const attachmentDescriptions = attachments.map((a, i) => {
|
|
4338
|
+
const name = a.filename || `attachment_${i + 1}`;
|
|
4339
|
+
const typeLabel = a.type === "image" ? "Image" : "File";
|
|
4340
|
+
const location = a.savedPath || "(path unknown)";
|
|
4341
|
+
return `${i + 1}. ${typeLabel}: "${name}" saved at: ${location}`;
|
|
4342
|
+
}).join("\n");
|
|
4343
|
+
contentParts.push({
|
|
4344
|
+
type: "text",
|
|
4345
|
+
text: `[FILE ATTACHMENTS - The user has attached the following files which are saved on disk]
|
|
4346
|
+
${attachmentDescriptions}
|
|
4347
|
+
|
|
4348
|
+
You can reference these files by their paths above. The file contents are also shown inline below.`
|
|
4349
|
+
});
|
|
4350
|
+
if (prompt) {
|
|
4351
|
+
contentParts.push({ type: "text", text: `
|
|
4352
|
+
[USER MESSAGE]
|
|
4353
|
+
${prompt}` });
|
|
4354
|
+
}
|
|
4355
|
+
for (const attachment of attachments) {
|
|
4356
|
+
if (attachment.type === "image") {
|
|
4357
|
+
contentParts.push({
|
|
4358
|
+
type: "image",
|
|
4359
|
+
image: attachment.data,
|
|
4360
|
+
mediaType: attachment.mediaType,
|
|
4361
|
+
filename: attachment.filename,
|
|
4362
|
+
savedPath: attachment.savedPath
|
|
4363
|
+
});
|
|
4364
|
+
} else {
|
|
4365
|
+
contentParts.push({
|
|
4366
|
+
type: "file",
|
|
4367
|
+
data: attachment.data,
|
|
4368
|
+
mediaType: attachment.mediaType || "application/octet-stream",
|
|
4369
|
+
filename: attachment.filename,
|
|
4370
|
+
savedPath: attachment.savedPath
|
|
4371
|
+
});
|
|
4372
|
+
}
|
|
4373
|
+
}
|
|
4374
|
+
broadcastContent = contentParts;
|
|
4375
|
+
} else {
|
|
4376
|
+
broadcastContent = prompt;
|
|
4377
|
+
}
|
|
4096
4378
|
await writeSSE(JSON.stringify({
|
|
4097
4379
|
type: "data-user-message",
|
|
4098
|
-
data: { id: `user_${Date.now()}`, content:
|
|
4380
|
+
data: { id: `user_${Date.now()}`, content: broadcastContent }
|
|
4099
4381
|
}));
|
|
4100
4382
|
const messageId = `msg_${Date.now()}`;
|
|
4101
4383
|
await writeSSE(JSON.stringify({ type: "start", messageId }));
|
|
@@ -4103,6 +4385,8 @@ function createAgentStreamProducer(sessionId, prompt, streamId) {
|
|
|
4103
4385
|
let textStarted = false;
|
|
4104
4386
|
const result = await agent.stream({
|
|
4105
4387
|
prompt,
|
|
4388
|
+
attachments,
|
|
4389
|
+
// Pass attachments to agent
|
|
4106
4390
|
abortSignal: abortController.signal,
|
|
4107
4391
|
// Use our managed abort controller, NOT client signal
|
|
4108
4392
|
skipSaveUserMessage: true,
|
|
@@ -4212,14 +4496,20 @@ function createAgentStreamProducer(sessionId, prompt, streamId) {
|
|
|
4212
4496
|
} else {
|
|
4213
4497
|
await writeSSE(JSON.stringify({ type: "finish" }));
|
|
4214
4498
|
}
|
|
4215
|
-
|
|
4499
|
+
try {
|
|
4500
|
+
activeStreamQueries.finish(streamId);
|
|
4501
|
+
} catch {
|
|
4502
|
+
}
|
|
4216
4503
|
} catch (error) {
|
|
4217
4504
|
if (error.name === "AbortError" || error.message?.includes("aborted")) {
|
|
4218
4505
|
await writeSSE(JSON.stringify({ type: "abort" }));
|
|
4219
4506
|
} else {
|
|
4220
4507
|
console.error("Agent error:", error);
|
|
4221
4508
|
await writeSSE(JSON.stringify({ type: "error", errorText: error.message }));
|
|
4222
|
-
|
|
4509
|
+
try {
|
|
4510
|
+
activeStreamQueries.markError(streamId);
|
|
4511
|
+
} catch {
|
|
4512
|
+
}
|
|
4223
4513
|
}
|
|
4224
4514
|
} finally {
|
|
4225
4515
|
cleanupAbortController();
|
|
@@ -4235,19 +4525,74 @@ agents.post(
|
|
|
4235
4525
|
zValidator2("json", runPromptSchema),
|
|
4236
4526
|
async (c) => {
|
|
4237
4527
|
const id = c.req.param("id");
|
|
4238
|
-
const { prompt } = c.req.valid("json");
|
|
4528
|
+
const { prompt, attachments } = c.req.valid("json");
|
|
4239
4529
|
const session = sessionQueries.getById(id);
|
|
4240
4530
|
if (!session) {
|
|
4241
4531
|
return c.json({ error: "Session not found" }, 404);
|
|
4242
4532
|
}
|
|
4243
4533
|
const nextSequence = messageQueries.getNextSequence(id);
|
|
4244
4534
|
await createCheckpoint(id, session.workingDirectory, nextSequence);
|
|
4245
|
-
|
|
4246
|
-
const
|
|
4535
|
+
let userMessageContent;
|
|
4536
|
+
const streamAttachments = attachments;
|
|
4537
|
+
if (streamAttachments && streamAttachments.length > 0) {
|
|
4538
|
+
for (let i = 0; i < streamAttachments.length; i++) {
|
|
4539
|
+
const attachment = streamAttachments[i];
|
|
4540
|
+
try {
|
|
4541
|
+
const savedPath = saveAttachmentToDisk(id, attachment, i);
|
|
4542
|
+
attachment.savedPath = savedPath;
|
|
4543
|
+
} catch (err) {
|
|
4544
|
+
console.error(`Failed to save attachment ${i}:`, err);
|
|
4545
|
+
}
|
|
4546
|
+
}
|
|
4547
|
+
const contentParts = [];
|
|
4548
|
+
const attachmentDescriptions = streamAttachments.map((a, i) => {
|
|
4549
|
+
const name = a.filename || `attachment_${i + 1}`;
|
|
4550
|
+
const typeLabel = a.type === "image" ? "Image" : "File";
|
|
4551
|
+
const location = a.savedPath || "(path unknown)";
|
|
4552
|
+
return `${i + 1}. ${typeLabel}: "${name}" saved at: ${location}`;
|
|
4553
|
+
}).join("\n");
|
|
4554
|
+
contentParts.push({
|
|
4555
|
+
type: "text",
|
|
4556
|
+
text: `[FILE ATTACHMENTS - The user has attached the following files which are saved on disk]
|
|
4557
|
+
${attachmentDescriptions}
|
|
4558
|
+
|
|
4559
|
+
You can reference these files by their paths above. The file contents are also shown inline below.`
|
|
4560
|
+
});
|
|
4561
|
+
if (prompt) {
|
|
4562
|
+
contentParts.push({ type: "text", text: `
|
|
4563
|
+
[USER MESSAGE]
|
|
4564
|
+
${prompt}` });
|
|
4565
|
+
}
|
|
4566
|
+
for (const attachment of streamAttachments) {
|
|
4567
|
+
if (attachment.type === "image") {
|
|
4568
|
+
contentParts.push({
|
|
4569
|
+
type: "image",
|
|
4570
|
+
image: attachment.data,
|
|
4571
|
+
// base64 data URL or raw base64
|
|
4572
|
+
mediaType: attachment.mediaType,
|
|
4573
|
+
filename: attachment.filename,
|
|
4574
|
+
savedPath: attachment.savedPath
|
|
4575
|
+
});
|
|
4576
|
+
} else {
|
|
4577
|
+
contentParts.push({
|
|
4578
|
+
type: "file",
|
|
4579
|
+
data: attachment.data,
|
|
4580
|
+
mediaType: attachment.mediaType || "application/octet-stream",
|
|
4581
|
+
filename: attachment.filename,
|
|
4582
|
+
savedPath: attachment.savedPath
|
|
4583
|
+
});
|
|
4584
|
+
}
|
|
4585
|
+
}
|
|
4586
|
+
userMessageContent = contentParts;
|
|
4587
|
+
} else {
|
|
4588
|
+
userMessageContent = prompt;
|
|
4589
|
+
}
|
|
4590
|
+
messageQueries.create(id, { role: "user", content: userMessageContent });
|
|
4591
|
+
const streamId = `stream_${id}_${nanoid5(10)}`;
|
|
4247
4592
|
activeStreamQueries.create(id, streamId);
|
|
4248
4593
|
const stream = await streamContext.resumableStream(
|
|
4249
4594
|
streamId,
|
|
4250
|
-
createAgentStreamProducer(id, prompt, streamId)
|
|
4595
|
+
createAgentStreamProducer(id, prompt, streamId, streamAttachments)
|
|
4251
4596
|
);
|
|
4252
4597
|
if (!stream) {
|
|
4253
4598
|
return c.json({ error: "Failed to create stream" }, 500);
|
|
@@ -4440,7 +4785,7 @@ agents.post(
|
|
|
4440
4785
|
sessionConfig: body.toolApprovals ? { toolApprovals: body.toolApprovals } : void 0
|
|
4441
4786
|
});
|
|
4442
4787
|
const session = agent.getSession();
|
|
4443
|
-
const streamId = `stream_${session.id}_${
|
|
4788
|
+
const streamId = `stream_${session.id}_${nanoid5(10)}`;
|
|
4444
4789
|
await createCheckpoint(session.id, session.workingDirectory, 0);
|
|
4445
4790
|
activeStreamQueries.create(session.id, streamId);
|
|
4446
4791
|
const createQuickStreamProducer = () => {
|
|
@@ -5154,11 +5499,11 @@ function getWebDirectory() {
|
|
|
5154
5499
|
try {
|
|
5155
5500
|
const currentDir = dirname6(fileURLToPath2(import.meta.url));
|
|
5156
5501
|
const webDir = resolve8(currentDir, "..", "web");
|
|
5157
|
-
if (
|
|
5502
|
+
if (existsSync12(webDir) && existsSync12(join5(webDir, "package.json"))) {
|
|
5158
5503
|
return webDir;
|
|
5159
5504
|
}
|
|
5160
5505
|
const altWebDir = resolve8(currentDir, "..", "..", "web");
|
|
5161
|
-
if (
|
|
5506
|
+
if (existsSync12(altWebDir) && existsSync12(join5(altWebDir, "package.json"))) {
|
|
5162
5507
|
return altWebDir;
|
|
5163
5508
|
}
|
|
5164
5509
|
return null;
|
|
@@ -5216,23 +5561,23 @@ async function findWebPort(preferredPort) {
|
|
|
5216
5561
|
return { port: preferredPort, alreadyRunning: false };
|
|
5217
5562
|
}
|
|
5218
5563
|
function hasProductionBuild(webDir) {
|
|
5219
|
-
const buildIdPath =
|
|
5220
|
-
return
|
|
5564
|
+
const buildIdPath = join5(webDir, ".next", "BUILD_ID");
|
|
5565
|
+
return existsSync12(buildIdPath);
|
|
5221
5566
|
}
|
|
5222
5567
|
function hasSourceFiles(webDir) {
|
|
5223
|
-
const appDir =
|
|
5224
|
-
const pagesDir =
|
|
5225
|
-
const rootAppDir =
|
|
5226
|
-
const rootPagesDir =
|
|
5227
|
-
return
|
|
5568
|
+
const appDir = join5(webDir, "src", "app");
|
|
5569
|
+
const pagesDir = join5(webDir, "src", "pages");
|
|
5570
|
+
const rootAppDir = join5(webDir, "app");
|
|
5571
|
+
const rootPagesDir = join5(webDir, "pages");
|
|
5572
|
+
return existsSync12(appDir) || existsSync12(pagesDir) || existsSync12(rootAppDir) || existsSync12(rootPagesDir);
|
|
5228
5573
|
}
|
|
5229
5574
|
function getStandaloneServerPath(webDir) {
|
|
5230
5575
|
const possiblePaths = [
|
|
5231
|
-
|
|
5232
|
-
|
|
5576
|
+
join5(webDir, ".next", "standalone", "server.js"),
|
|
5577
|
+
join5(webDir, ".next", "standalone", "web", "server.js")
|
|
5233
5578
|
];
|
|
5234
5579
|
for (const serverPath of possiblePaths) {
|
|
5235
|
-
if (
|
|
5580
|
+
if (existsSync12(serverPath)) {
|
|
5236
5581
|
return serverPath;
|
|
5237
5582
|
}
|
|
5238
5583
|
}
|
|
@@ -5272,15 +5617,15 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
5272
5617
|
if (!quiet) console.log(` \u2713 Web UI already running at http://localhost:${actualPort}`);
|
|
5273
5618
|
return { process: null, port: actualPort };
|
|
5274
5619
|
}
|
|
5275
|
-
const usePnpm =
|
|
5276
|
-
const useNpm = !usePnpm &&
|
|
5620
|
+
const usePnpm = existsSync12(join5(webDir, "pnpm-lock.yaml"));
|
|
5621
|
+
const useNpm = !usePnpm && existsSync12(join5(webDir, "package-lock.json"));
|
|
5277
5622
|
const pkgManager = usePnpm ? "pnpm" : useNpm ? "npm" : "npx";
|
|
5278
5623
|
const { NODE_OPTIONS, TSX_TSCONFIG_PATH, ...cleanEnv } = process.env;
|
|
5279
5624
|
const apiUrl = publicUrl || `http://127.0.0.1:${apiPort}`;
|
|
5280
5625
|
const runtimeConfig = { apiBaseUrl: apiUrl };
|
|
5281
|
-
const runtimeConfigPath =
|
|
5626
|
+
const runtimeConfigPath = join5(webDir, "runtime-config.json");
|
|
5282
5627
|
try {
|
|
5283
|
-
|
|
5628
|
+
writeFileSync4(runtimeConfigPath, JSON.stringify(runtimeConfig, null, 2));
|
|
5284
5629
|
if (!quiet) console.log(` \u{1F4DD} Runtime config written to ${runtimeConfigPath}`);
|
|
5285
5630
|
} catch (err) {
|
|
5286
5631
|
if (!quiet) console.warn(` \u26A0 Could not write runtime config: ${err}`);
|
|
@@ -5306,7 +5651,7 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
5306
5651
|
if (!quiet) console.log(" \u{1F4E6} Starting Web UI from standalone build...");
|
|
5307
5652
|
} else if (hasBuild && (isProduction || !hasSource)) {
|
|
5308
5653
|
command = pkgManager;
|
|
5309
|
-
args = pkgManager === "npx" ? ["next", "start", "-p", String(actualPort)] : ["run", "start"
|
|
5654
|
+
args = pkgManager === "npx" ? ["next", "start", "-p", String(actualPort)] : ["run", "start"];
|
|
5310
5655
|
} else if (hasSource) {
|
|
5311
5656
|
if (isProduction && !hasBuild) {
|
|
5312
5657
|
if (!quiet) console.log(" \u{1F4E6} Building Web UI for production...");
|
|
@@ -5318,10 +5663,10 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
5318
5663
|
}
|
|
5319
5664
|
if (!quiet) console.log(" \u2713 Web UI build complete");
|
|
5320
5665
|
command = pkgManager;
|
|
5321
|
-
args = pkgManager === "npx" ? ["next", "start", "-p", String(actualPort)] : ["run", "start"
|
|
5666
|
+
args = pkgManager === "npx" ? ["next", "start", "-p", String(actualPort)] : ["run", "start"];
|
|
5322
5667
|
} else {
|
|
5323
5668
|
command = pkgManager;
|
|
5324
|
-
args = pkgManager === "npx" ? ["next", "dev", "-p", String(actualPort)] : ["run", "dev"
|
|
5669
|
+
args = pkgManager === "npx" ? ["next", "dev", "-p", String(actualPort)] : ["run", "dev"];
|
|
5325
5670
|
}
|
|
5326
5671
|
} else {
|
|
5327
5672
|
if (!quiet) {
|
|
@@ -5349,6 +5694,12 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
5349
5694
|
}, startupTimeout);
|
|
5350
5695
|
child.stdout?.on("data", (data) => {
|
|
5351
5696
|
const output = data.toString();
|
|
5697
|
+
if (!quiet) {
|
|
5698
|
+
const lines = output.trim().split("\n").filter((l) => l.trim());
|
|
5699
|
+
for (const line of lines) {
|
|
5700
|
+
console.log(` Web UI: ${line}`);
|
|
5701
|
+
}
|
|
5702
|
+
}
|
|
5352
5703
|
if (!started && (output.includes("Ready") || output.includes("started") || output.includes("localhost"))) {
|
|
5353
5704
|
started = true;
|
|
5354
5705
|
clearTimeout(timeout);
|
|
@@ -5356,9 +5707,9 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
5356
5707
|
}
|
|
5357
5708
|
});
|
|
5358
5709
|
child.stderr?.on("data", (data) => {
|
|
5359
|
-
const output = data.toString();
|
|
5360
|
-
if (output
|
|
5361
|
-
|
|
5710
|
+
const output = data.toString().trim();
|
|
5711
|
+
if (!quiet && output) {
|
|
5712
|
+
console.error(` Web UI: ${output.slice(0, 500)}`);
|
|
5362
5713
|
}
|
|
5363
5714
|
});
|
|
5364
5715
|
child.on("error", (err) => {
|
|
@@ -5464,8 +5815,8 @@ async function startServer(options = {}) {
|
|
|
5464
5815
|
if (options.workingDirectory) {
|
|
5465
5816
|
config.resolvedWorkingDirectory = options.workingDirectory;
|
|
5466
5817
|
}
|
|
5467
|
-
if (!
|
|
5468
|
-
|
|
5818
|
+
if (!existsSync12(config.resolvedWorkingDirectory)) {
|
|
5819
|
+
mkdirSync5(config.resolvedWorkingDirectory, { recursive: true });
|
|
5469
5820
|
if (!options.quiet) console.log(`\u{1F4C1} Created agent workspace: ${config.resolvedWorkingDirectory}`);
|
|
5470
5821
|
}
|
|
5471
5822
|
initDatabase(config.resolvedDatabasePath);
|
|
@@ -5968,8 +6319,8 @@ function generateOpenAPISpec() {
|
|
|
5968
6319
|
}
|
|
5969
6320
|
|
|
5970
6321
|
// src/cli.ts
|
|
5971
|
-
import { writeFileSync as
|
|
5972
|
-
import { resolve as resolve9, join as
|
|
6322
|
+
import { writeFileSync as writeFileSync5, existsSync as existsSync13 } from "fs";
|
|
6323
|
+
import { resolve as resolve9, join as join6 } from "path";
|
|
5973
6324
|
async function apiRequest(baseUrl, path, options = {}) {
|
|
5974
6325
|
const url = `${baseUrl}${path}`;
|
|
5975
6326
|
const init = {
|
|
@@ -6526,19 +6877,19 @@ program.command("init").description("Create a sparkecoder.config.json file").opt
|
|
|
6526
6877
|
let configLocation;
|
|
6527
6878
|
if (options.global) {
|
|
6528
6879
|
const appDataDir = ensureAppDataDirectory();
|
|
6529
|
-
configPath =
|
|
6880
|
+
configPath = join6(appDataDir, "sparkecoder.config.json");
|
|
6530
6881
|
configLocation = "global";
|
|
6531
6882
|
} else {
|
|
6532
6883
|
configPath = resolve9(process.cwd(), "sparkecoder.config.json");
|
|
6533
6884
|
configLocation = "local";
|
|
6534
6885
|
}
|
|
6535
|
-
if (
|
|
6886
|
+
if (existsSync13(configPath) && !options.force) {
|
|
6536
6887
|
console.log(chalk.yellow("Config file already exists. Use --force to overwrite."));
|
|
6537
6888
|
console.log(chalk.dim(` ${configPath}`));
|
|
6538
6889
|
return;
|
|
6539
6890
|
}
|
|
6540
6891
|
const config = createDefaultConfig();
|
|
6541
|
-
|
|
6892
|
+
writeFileSync5(configPath, JSON.stringify(config, null, 2));
|
|
6542
6893
|
console.log(chalk.green(`\u2713 Created ${configLocation} config`));
|
|
6543
6894
|
console.log(chalk.dim(` ${configPath}`));
|
|
6544
6895
|
console.log(chalk.dim("Set AI_GATEWAY_API_KEY and run sparkecoder to start"));
|