@sandagent/daemon 0.9.3 → 0.9.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -325,6 +325,55 @@ Response: `application/x-ndjson` chunked stream — each line is an AI SDK UI me
325
325
  | POST | `/api/fs/remove` | `{"path":"tmp","recursive":true}` |
326
326
  | POST | `/api/fs/move` | `{"from":"a.txt","to":"b.txt"}` |
327
327
  | POST | `/api/fs/copy` | `{"from":"a.txt","to":"b.txt"}` |
328
+ | POST | `/api/fs/upload` | `multipart/form-data` — see below |
329
+
330
+ #### `POST /api/fs/upload`
331
+
332
+ Upload one or more files via `multipart/form-data`.
333
+
334
+ Form fields:
335
+
336
+ | Field | Type | Required | Description |
337
+ |-------|------|----------|-------------|
338
+ | `path` | string | No | Target directory (default: `.`) |
339
+ | `volume` | string | No | Volume name for multi-tenant isolation |
340
+ | `create_dirs` | string | No | Create parent dirs (default: `"true"`) |
341
+ | `file` | file | Yes | One or more files to upload |
342
+
343
+ Example:
344
+
345
+ ```bash
346
+ # Upload a single file
347
+ curl -X POST http://localhost:3080/api/fs/upload \
348
+ -F "path=uploads" \
349
+ -F "file=@local-file.txt"
350
+
351
+ # Upload multiple files
352
+ curl -X POST http://localhost:3080/api/fs/upload \
353
+ -F "path=data" \
354
+ -F "file=@report.csv" \
355
+ -F "file=@image.png"
356
+
357
+ # Upload to a specific volume
358
+ curl -X POST http://localhost:3080/api/fs/upload \
359
+ -F "path=docs" \
360
+ -F "volume=vol-001" \
361
+ -F "file=@readme.md"
362
+ ```
363
+
364
+ Response:
365
+
366
+ ```json
367
+ {
368
+ "ok": true,
369
+ "data": {
370
+ "files": [
371
+ { "fieldname": "file", "filename": "report.csv", "path": "/workspace/data/report.csv", "size": 1234 }
372
+ ]
373
+ },
374
+ "error": null
375
+ }
376
+ ```
328
377
 
329
378
  All fs endpoints accept optional `volume` for multi-tenant isolation.
330
379
 
package/dist/bundle.mjs CHANGED
@@ -206419,6 +206419,42 @@ var require_lib4 = __commonJS({
206419
206419
  import * as http3 from "node:http";
206420
206420
  import { URL as URL2 } from "node:url";
206421
206421
 
206422
+ // src/multipart.ts
206423
+ function parseMultipart(contentType, body) {
206424
+ const match2 = contentType.match(/boundary=(?:"([^"]+)"|([^\s;]+))/);
206425
+ if (!match2) throw new Error("missing multipart boundary");
206426
+ const boundary = match2[1] ?? match2[2];
206427
+ const delimiter2 = Buffer.from(`--${boundary}`);
206428
+ const fields = {};
206429
+ const files = [];
206430
+ let start = body.indexOf(delimiter2) + delimiter2.length;
206431
+ while (start < body.length) {
206432
+ const end = body.indexOf(delimiter2, start);
206433
+ if (end === -1) break;
206434
+ const part = body.subarray(start, end);
206435
+ const partStart = part[0] === 13 && part[1] === 10 ? 2 : 0;
206436
+ const headerEnd = part.indexOf("\r\n\r\n", partStart);
206437
+ if (headerEnd === -1) {
206438
+ start = end + delimiter2.length;
206439
+ continue;
206440
+ }
206441
+ const headerStr = part.subarray(partStart, headerEnd).toString("utf-8");
206442
+ let data = part.subarray(headerEnd + 4);
206443
+ if (data.length >= 2 && data[data.length - 2] === 13 && data[data.length - 1] === 10) {
206444
+ data = data.subarray(0, data.length - 2);
206445
+ }
206446
+ const nameMatch = headerStr.match(/name="([^"]+)"/);
206447
+ const filenameMatch = headerStr.match(/filename="([^"]+)"/);
206448
+ if (filenameMatch && nameMatch) {
206449
+ files.push({ filename: filenameMatch[1], data: Buffer.from(data) });
206450
+ } else if (nameMatch) {
206451
+ fields[nameMatch[1]] = data.toString("utf-8");
206452
+ }
206453
+ start = end + delimiter2.length;
206454
+ }
206455
+ return { fields, files };
206456
+ }
206457
+
206422
206458
  // src/routes/fs.ts
206423
206459
  import * as fs2 from "node:fs/promises";
206424
206460
  import * as path2 from "node:path";
@@ -206596,6 +206632,26 @@ async function fsCopy(state, body) {
206596
206632
  await fs2.copyFile(from, to);
206597
206633
  return ok({ path: to });
206598
206634
  }
206635
+ async function fsUpload(state, parts) {
206636
+ const volume = parts.fields.volume;
206637
+ const targetDir = parts.fields.path ?? ".";
206638
+ const createDirs = parts.fields.create_dirs !== "false";
206639
+ const root2 = resolveVolumeRoot(state, volume);
206640
+ const dir = resolveUnderRoot(root2, targetDir);
206641
+ if (createDirs) await ensureDir(dir);
206642
+ const results = [];
206643
+ for (const file of parts.files) {
206644
+ const target = resolveUnderRoot(root2, path2.join(targetDir, file.filename));
206645
+ await fs2.writeFile(target, file.data);
206646
+ results.push({
206647
+ fieldname: "file",
206648
+ filename: file.filename,
206649
+ path: target,
206650
+ size: file.data.length
206651
+ });
206652
+ }
206653
+ return ok({ files: results });
206654
+ }
206599
206655
 
206600
206656
  // src/routes/git.ts
206601
206657
  import { execFile } from "node:child_process";
@@ -262139,7 +262195,12 @@ function createPiRunner(options2 = {}) {
262139
262195
  resourceLoader
262140
262196
  });
262141
262197
  if (options2.systemPrompt != null && options2.systemPrompt !== "") {
262142
- session.agent.setSystemPrompt(options2.systemPrompt);
262198
+ const existing = session.agent.state.systemPrompt ?? "";
262199
+ session.agent.setSystemPrompt(existing ? `${existing}
262200
+
262201
+ ---
262202
+
262203
+ ${options2.systemPrompt}` : options2.systemPrompt);
262143
262204
  }
262144
262205
  const eventQueue = [];
262145
262206
  let isComplete = false;
@@ -262405,6 +262466,10 @@ async function sandagentRun(req, res, env2) {
262405
262466
  function createDaemon(config) {
262406
262467
  const router = new DaemonRouter({ root: config.root });
262407
262468
  const env2 = process.env;
262469
+ const state = {
262470
+ root: config.root,
262471
+ volumesRoot: `${config.root}/volumes`
262472
+ };
262408
262473
  return http3.createServer(async (req, res) => {
262409
262474
  const method = req.method ?? "GET";
262410
262475
  const url = new URL2(
@@ -262416,6 +262481,29 @@ function createDaemon(config) {
262416
262481
  const body2 = JSON.parse(await readBody(req) || "{}");
262417
262482
  return sandagentRun(body2, res, env2);
262418
262483
  }
262484
+ if (method === "POST" && pathname === "/api/fs/upload") {
262485
+ try {
262486
+ const ct2 = req.headers["content-type"] ?? "";
262487
+ if (!ct2.includes("multipart/form-data")) {
262488
+ res.writeHead(400, { "Content-Type": "application/json" });
262489
+ res.end(
262490
+ JSON.stringify(fail("content-type must be multipart/form-data"))
262491
+ );
262492
+ return;
262493
+ }
262494
+ const raw = await readBodyRaw(req);
262495
+ const parts = parseMultipart(ct2, raw);
262496
+ const result2 = await fsUpload(state, parts);
262497
+ res.writeHead(200, { "Content-Type": "application/json" });
262498
+ res.end(JSON.stringify(result2));
262499
+ } catch (err) {
262500
+ const status2 = err instanceof AppError ? err.status : 500;
262501
+ const msg = err instanceof Error ? err.message : String(err);
262502
+ res.writeHead(status2, { "Content-Type": "application/json" });
262503
+ res.end(JSON.stringify(fail(msg)));
262504
+ }
262505
+ return;
262506
+ }
262419
262507
  const params = method === "GET" ? Object.fromEntries(url.searchParams) : JSON.parse(await readBody(req) || "{}");
262420
262508
  const result = await router.handle(method, pathname, params);
262421
262509
  const status = result?.status ?? 404;
@@ -262432,6 +262520,14 @@ function readBody(req) {
262432
262520
  req.on("error", reject);
262433
262521
  });
262434
262522
  }
262523
+ function readBodyRaw(req) {
262524
+ return new Promise((resolve14, reject) => {
262525
+ const chunks = [];
262526
+ req.on("data", (c) => chunks.push(c));
262527
+ req.on("end", () => resolve14(Buffer.concat(chunks)));
262528
+ req.on("error", reject);
262529
+ });
262530
+ }
262435
262531
 
262436
262532
  // src/cli.ts
262437
262533
  var host = process.env.SANDAGENT_DAEMON_HOST ?? "0.0.0.0";
package/dist/index.js CHANGED
@@ -206591,6 +206591,26 @@ async function fsCopy(state, body) {
206591
206591
  await fs2.copyFile(from, to);
206592
206592
  return ok({ path: to });
206593
206593
  }
206594
+ async function fsUpload(state, parts) {
206595
+ const volume = parts.fields.volume;
206596
+ const targetDir = parts.fields.path ?? ".";
206597
+ const createDirs = parts.fields.create_dirs !== "false";
206598
+ const root = resolveVolumeRoot(state, volume);
206599
+ const dir = resolveUnderRoot(root, targetDir);
206600
+ if (createDirs) await ensureDir(dir);
206601
+ const results = [];
206602
+ for (const file of parts.files) {
206603
+ const target = resolveUnderRoot(root, path2.join(targetDir, file.filename));
206604
+ await fs2.writeFile(target, file.data);
206605
+ results.push({
206606
+ fieldname: "file",
206607
+ filename: file.filename,
206608
+ path: target,
206609
+ size: file.data.length
206610
+ });
206611
+ }
206612
+ return ok({ files: results });
206613
+ }
206594
206614
 
206595
206615
  // src/routes/git.ts
206596
206616
  import { execFile } from "node:child_process";
@@ -206764,6 +206784,42 @@ var DaemonRouter = class {
206764
206784
  import * as http3 from "node:http";
206765
206785
  import { URL as URL2 } from "node:url";
206766
206786
 
206787
+ // src/multipart.ts
206788
+ function parseMultipart(contentType, body) {
206789
+ const match2 = contentType.match(/boundary=(?:"([^"]+)"|([^\s;]+))/);
206790
+ if (!match2) throw new Error("missing multipart boundary");
206791
+ const boundary = match2[1] ?? match2[2];
206792
+ const delimiter2 = Buffer.from(`--${boundary}`);
206793
+ const fields = {};
206794
+ const files = [];
206795
+ let start = body.indexOf(delimiter2) + delimiter2.length;
206796
+ while (start < body.length) {
206797
+ const end = body.indexOf(delimiter2, start);
206798
+ if (end === -1) break;
206799
+ const part = body.subarray(start, end);
206800
+ const partStart = part[0] === 13 && part[1] === 10 ? 2 : 0;
206801
+ const headerEnd = part.indexOf("\r\n\r\n", partStart);
206802
+ if (headerEnd === -1) {
206803
+ start = end + delimiter2.length;
206804
+ continue;
206805
+ }
206806
+ const headerStr = part.subarray(partStart, headerEnd).toString("utf-8");
206807
+ let data = part.subarray(headerEnd + 4);
206808
+ if (data.length >= 2 && data[data.length - 2] === 13 && data[data.length - 1] === 10) {
206809
+ data = data.subarray(0, data.length - 2);
206810
+ }
206811
+ const nameMatch = headerStr.match(/name="([^"]+)"/);
206812
+ const filenameMatch = headerStr.match(/filename="([^"]+)"/);
206813
+ if (filenameMatch && nameMatch) {
206814
+ files.push({ filename: filenameMatch[1], data: Buffer.from(data) });
206815
+ } else if (nameMatch) {
206816
+ fields[nameMatch[1]] = data.toString("utf-8");
206817
+ }
206818
+ start = end + delimiter2.length;
206819
+ }
206820
+ return { fields, files };
206821
+ }
206822
+
206767
206823
  // ../../packages/runner-claude/dist/ai-sdk-stream.js
206768
206824
  import { appendFileSync, existsSync, unlinkSync } from "node:fs";
206769
206825
  import { join as join3 } from "node:path";
@@ -262138,7 +262194,12 @@ function createPiRunner(options2 = {}) {
262138
262194
  resourceLoader
262139
262195
  });
262140
262196
  if (options2.systemPrompt != null && options2.systemPrompt !== "") {
262141
- session.agent.setSystemPrompt(options2.systemPrompt);
262197
+ const existing = session.agent.state.systemPrompt ?? "";
262198
+ session.agent.setSystemPrompt(existing ? `${existing}
262199
+
262200
+ ---
262201
+
262202
+ ${options2.systemPrompt}` : options2.systemPrompt);
262142
262203
  }
262143
262204
  const eventQueue = [];
262144
262205
  let isComplete = false;
@@ -262404,6 +262465,10 @@ async function sandagentRun(req, res, env2) {
262404
262465
  function createDaemon(config) {
262405
262466
  const router = new DaemonRouter({ root: config.root });
262406
262467
  const env2 = process.env;
262468
+ const state = {
262469
+ root: config.root,
262470
+ volumesRoot: `${config.root}/volumes`
262471
+ };
262407
262472
  return http3.createServer(async (req, res) => {
262408
262473
  const method = req.method ?? "GET";
262409
262474
  const url = new URL2(
@@ -262415,6 +262480,29 @@ function createDaemon(config) {
262415
262480
  const body2 = JSON.parse(await readBody(req) || "{}");
262416
262481
  return sandagentRun(body2, res, env2);
262417
262482
  }
262483
+ if (method === "POST" && pathname === "/api/fs/upload") {
262484
+ try {
262485
+ const ct2 = req.headers["content-type"] ?? "";
262486
+ if (!ct2.includes("multipart/form-data")) {
262487
+ res.writeHead(400, { "Content-Type": "application/json" });
262488
+ res.end(
262489
+ JSON.stringify(fail("content-type must be multipart/form-data"))
262490
+ );
262491
+ return;
262492
+ }
262493
+ const raw = await readBodyRaw(req);
262494
+ const parts = parseMultipart(ct2, raw);
262495
+ const result2 = await fsUpload(state, parts);
262496
+ res.writeHead(200, { "Content-Type": "application/json" });
262497
+ res.end(JSON.stringify(result2));
262498
+ } catch (err) {
262499
+ const status2 = err instanceof AppError ? err.status : 500;
262500
+ const msg = err instanceof Error ? err.message : String(err);
262501
+ res.writeHead(status2, { "Content-Type": "application/json" });
262502
+ res.end(JSON.stringify(fail(msg)));
262503
+ }
262504
+ return;
262505
+ }
262418
262506
  const params = method === "GET" ? Object.fromEntries(url.searchParams) : JSON.parse(await readBody(req) || "{}");
262419
262507
  const result = await router.handle(method, pathname, params);
262420
262508
  const status = result?.status ?? 404;
@@ -262431,6 +262519,14 @@ function readBody(req) {
262431
262519
  req.on("error", reject);
262432
262520
  });
262433
262521
  }
262522
+ function readBodyRaw(req) {
262523
+ return new Promise((resolve14, reject) => {
262524
+ const chunks = [];
262525
+ req.on("data", (c) => chunks.push(c));
262526
+ req.on("end", () => resolve14(Buffer.concat(chunks)));
262527
+ req.on("error", reject);
262528
+ });
262529
+ }
262434
262530
  export {
262435
262531
  DaemonRouter,
262436
262532
  createDaemon
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Minimal multipart/form-data parser — zero external dependencies.
3
+ * Parses raw body buffer using the boundary from Content-Type header.
4
+ */
5
+ export interface ParsedMultipart {
6
+ fields: Record<string, string>;
7
+ files: Array<{
8
+ filename: string;
9
+ data: Buffer;
10
+ }>;
11
+ }
12
+ export declare function parseMultipart(contentType: string, body: Buffer): ParsedMultipart;
13
+ //# sourceMappingURL=multipart.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multipart.d.ts","sourceRoot":"","sources":["../src/multipart.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,KAAK,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClD;AAED,wBAAgB,cAAc,CAC5B,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,GACX,eAAe,CAiDjB"}
@@ -1 +1 @@
1
- {"version":3,"file":"nextjs.d.ts","sourceRoot":"","sources":["../src/nextjs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,IAKzD,KAAK,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC,CA6B/C"}
1
+ {"version":3,"file":"nextjs.d.ts","sourceRoot":"","sources":["../src/nextjs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAQH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,IASzD,KAAK,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC,CAkD/C"}
package/dist/nextjs.js CHANGED
@@ -206414,6 +206414,42 @@ var require_lib4 = __commonJS({
206414
206414
  }
206415
206415
  });
206416
206416
 
206417
+ // src/multipart.ts
206418
+ function parseMultipart(contentType, body) {
206419
+ const match2 = contentType.match(/boundary=(?:"([^"]+)"|([^\s;]+))/);
206420
+ if (!match2) throw new Error("missing multipart boundary");
206421
+ const boundary = match2[1] ?? match2[2];
206422
+ const delimiter2 = Buffer.from(`--${boundary}`);
206423
+ const fields = {};
206424
+ const files = [];
206425
+ let start = body.indexOf(delimiter2) + delimiter2.length;
206426
+ while (start < body.length) {
206427
+ const end = body.indexOf(delimiter2, start);
206428
+ if (end === -1) break;
206429
+ const part = body.subarray(start, end);
206430
+ const partStart = part[0] === 13 && part[1] === 10 ? 2 : 0;
206431
+ const headerEnd = part.indexOf("\r\n\r\n", partStart);
206432
+ if (headerEnd === -1) {
206433
+ start = end + delimiter2.length;
206434
+ continue;
206435
+ }
206436
+ const headerStr = part.subarray(partStart, headerEnd).toString("utf-8");
206437
+ let data = part.subarray(headerEnd + 4);
206438
+ if (data.length >= 2 && data[data.length - 2] === 13 && data[data.length - 1] === 10) {
206439
+ data = data.subarray(0, data.length - 2);
206440
+ }
206441
+ const nameMatch = headerStr.match(/name="([^"]+)"/);
206442
+ const filenameMatch = headerStr.match(/filename="([^"]+)"/);
206443
+ if (filenameMatch && nameMatch) {
206444
+ files.push({ filename: filenameMatch[1], data: Buffer.from(data) });
206445
+ } else if (nameMatch) {
206446
+ fields[nameMatch[1]] = data.toString("utf-8");
206447
+ }
206448
+ start = end + delimiter2.length;
206449
+ }
206450
+ return { fields, files };
206451
+ }
206452
+
206417
206453
  // src/routes/fs.ts
206418
206454
  import * as fs2 from "node:fs/promises";
206419
206455
  import * as path2 from "node:path";
@@ -206591,6 +206627,26 @@ async function fsCopy(state, body) {
206591
206627
  await fs2.copyFile(from, to);
206592
206628
  return ok({ path: to });
206593
206629
  }
206630
+ async function fsUpload(state, parts) {
206631
+ const volume = parts.fields.volume;
206632
+ const targetDir = parts.fields.path ?? ".";
206633
+ const createDirs = parts.fields.create_dirs !== "false";
206634
+ const root = resolveVolumeRoot(state, volume);
206635
+ const dir = resolveUnderRoot(root, targetDir);
206636
+ if (createDirs) await ensureDir(dir);
206637
+ const results = [];
206638
+ for (const file of parts.files) {
206639
+ const target = resolveUnderRoot(root, path2.join(targetDir, file.filename));
206640
+ await fs2.writeFile(target, file.data);
206641
+ results.push({
206642
+ fieldname: "file",
206643
+ filename: file.filename,
206644
+ path: target,
206645
+ size: file.data.length
206646
+ });
206647
+ }
206648
+ return ok({ files: results });
206649
+ }
206594
206650
 
206595
206651
  // src/routes/git.ts
206596
206652
  import { execFile } from "node:child_process";
@@ -262134,7 +262190,12 @@ function createPiRunner(options2 = {}) {
262134
262190
  resourceLoader
262135
262191
  });
262136
262192
  if (options2.systemPrompt != null && options2.systemPrompt !== "") {
262137
- session.agent.setSystemPrompt(options2.systemPrompt);
262193
+ const existing = session.agent.state.systemPrompt ?? "";
262194
+ session.agent.setSystemPrompt(existing ? `${existing}
262195
+
262196
+ ---
262197
+
262198
+ ${options2.systemPrompt}` : options2.systemPrompt);
262138
262199
  }
262139
262200
  const eventQueue = [];
262140
262201
  let isComplete = false;
@@ -262410,6 +262471,10 @@ function createNextHandler(opts) {
262410
262471
  const router = new DaemonRouter({ root: opts.root });
262411
262472
  const env2 = process.env;
262412
262473
  const prefix = opts.prefix ?? "/api/daemon";
262474
+ const state = {
262475
+ root: opts.root,
262476
+ volumesRoot: `${opts.root}/volumes`
262477
+ };
262413
262478
  return async (req) => {
262414
262479
  const url = new URL(req.url);
262415
262480
  const pathname = url.pathname.startsWith(prefix) ? url.pathname.slice(prefix.length) || "/" : url.pathname;
@@ -262418,6 +262483,25 @@ function createNextHandler(opts) {
262418
262483
  const body = await req.json().catch(() => ({}));
262419
262484
  return codingRunStream(body, env2);
262420
262485
  }
262486
+ if (method === "POST" && pathname === "/api/fs/upload") {
262487
+ try {
262488
+ const ct2 = req.headers.get("content-type") ?? "";
262489
+ if (!ct2.includes("multipart/form-data")) {
262490
+ return Response.json(
262491
+ fail("content-type must be multipart/form-data"),
262492
+ { status: 400 }
262493
+ );
262494
+ }
262495
+ const raw = Buffer.from(await req.arrayBuffer());
262496
+ const parts = parseMultipart(ct2, raw);
262497
+ const result2 = await fsUpload(state, parts);
262498
+ return Response.json(result2);
262499
+ } catch (err) {
262500
+ const status = err instanceof AppError ? err.status : 500;
262501
+ const msg = err instanceof Error ? err.message : String(err);
262502
+ return Response.json(fail(msg), { status });
262503
+ }
262504
+ }
262421
262505
  const params = method === "GET" ? Object.fromEntries(url.searchParams) : await req.json().catch(() => ({}));
262422
262506
  const result = await router.handle(method, pathname, params);
262423
262507
  if (!result) {
@@ -79,5 +79,27 @@ export declare function fsMove(state: AppState, body: MoveCopyBody): Promise<imp
79
79
  export declare function fsCopy(state: AppState, body: MoveCopyBody): Promise<import("../utils.js").ApiEnvelope<{
80
80
  path: string;
81
81
  }>>;
82
+ export interface UploadedFile {
83
+ fieldname: string;
84
+ filename: string;
85
+ path: string;
86
+ size: number;
87
+ }
88
+ export interface UploadResult {
89
+ files: UploadedFile[];
90
+ }
91
+ /**
92
+ * Handle multipart/form-data file upload.
93
+ * Expects form field "path" for target directory and one or more "file" parts.
94
+ */
95
+ export declare function fsUpload(state: AppState, parts: {
96
+ fields: Record<string, string>;
97
+ files: Array<{
98
+ filename: string;
99
+ data: Buffer;
100
+ }>;
101
+ }): Promise<import("../utils.js").ApiEnvelope<{
102
+ files: UploadedFile[];
103
+ }>>;
82
104
  export {};
83
105
  //# sourceMappingURL=fs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../src/routes/fs.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAU5C,UAAU,SAAS;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,SAAS;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,UAAU,SAAS;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAID,UAAU,SAAS;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,UAAU,UAAU;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,UAAU,SAAS;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,UAAU,YAAY;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAID,wBAAsB,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS;;;;;MAkBzD;AAED,wBAAsB,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS;;;IAKzD;AAED,wBAAsB,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS;;;;IASzD;AAED,wBAAsB,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS;;;IAQ3D;AAED,wBAAsB,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS;UAMhD,MAAM;UACN,MAAM;YACJ,OAAO;UACT,MAAM;MA4Bf;AAED,wBAAsB,OAAO,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS;;;IAQ7D;AAED,wBAAsB,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS;;IAQ9D;AAED,wBAAsB,OAAO,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS;;IAK7D;AAED,wBAAsB,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU;;IAU/D;AAED,wBAAsB,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY;;IAS/D;AAED,wBAAsB,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY;;IAS/D"}
1
+ {"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../src/routes/fs.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAU5C,UAAU,SAAS;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,SAAS;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,UAAU,SAAS;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAID,UAAU,SAAS;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,UAAU,UAAU;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,UAAU,SAAS;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,UAAU,YAAY;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAID,wBAAsB,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS;;;;;MAkBzD;AAED,wBAAsB,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS;;;IAKzD;AAED,wBAAsB,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS;;;;IASzD;AAED,wBAAsB,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS;;;IAQ3D;AAED,wBAAsB,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS;UAMhD,MAAM;UACN,MAAM;YACJ,OAAO;UACT,MAAM;MA4Bf;AAED,wBAAsB,OAAO,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS;;;IAQ7D;AAED,wBAAsB,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS;;IAQ9D;AAED,wBAAsB,OAAO,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS;;IAK7D;AAED,wBAAsB,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU;;IAU/D;AAED,wBAAsB,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY;;IAS/D;AAED,wBAAsB,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY;;IAS/D;AAID,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB;AAED;;;GAGG;AACH,wBAAsB,QAAQ,CAC5B,KAAK,EAAE,QAAQ,EACf,KAAK,EAAE;IACL,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,KAAK,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClD;;IAuBF"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAMlC,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC,MAAM,CA8B9D"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAQlC,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC,MAAM,CA2D9D"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sandagent/daemon",
3
- "version": "0.9.3",
3
+ "version": "0.9.5",
4
4
  "description": "SandAgent Daemon - Unified API gateway for sandbox services (file, git, volumes)",
5
5
  "type": "module",
6
6
  "bin": {