@mastra/factory 0.24.0-alpha.12 → 0.24.0-alpha.14

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.
Files changed (46) hide show
  1. package/dist/factory.d.ts.map +1 -1
  2. package/dist/factory.js +33 -4
  3. package/dist/factory.js.map +1 -1
  4. package/dist/integrations/github/routes.d.ts.map +1 -1
  5. package/dist/integrations/github/routes.js +5 -2
  6. package/dist/integrations/github/routes.js.map +1 -1
  7. package/dist/routes/fs.d.ts +31 -23
  8. package/dist/routes/fs.d.ts.map +1 -1
  9. package/dist/routes/fs.js +214 -61
  10. package/dist/routes/fs.js.map +1 -1
  11. package/dist/routes/session-access.d.ts +55 -0
  12. package/dist/routes/session-access.d.ts.map +1 -0
  13. package/dist/routes/session-access.js +165 -0
  14. package/dist/routes/session-access.js.map +1 -0
  15. package/dist/routes/source-control-sessions.d.ts +5 -0
  16. package/dist/routes/source-control-sessions.d.ts.map +1 -1
  17. package/dist/routes/source-control-sessions.js +86 -9
  18. package/dist/routes/source-control-sessions.js.map +1 -1
  19. package/dist/routes/surface.d.ts +5 -0
  20. package/dist/routes/surface.d.ts.map +1 -1
  21. package/dist/routes/surface.js +2 -2
  22. package/dist/routes/surface.js.map +1 -1
  23. package/dist/session/resource-migration.d.ts +18 -0
  24. package/dist/session/resource-migration.d.ts.map +1 -1
  25. package/dist/session/resource-migration.js +33 -1
  26. package/dist/session/resource-migration.js.map +1 -1
  27. package/dist/session/session-threads.d.ts +9 -0
  28. package/dist/session/session-threads.d.ts.map +1 -1
  29. package/dist/session/session-threads.js +24 -4
  30. package/dist/session/session-threads.js.map +1 -1
  31. package/dist/storage/domains/source-control/base.d.ts +15 -0
  32. package/dist/storage/domains/source-control/base.d.ts.map +1 -1
  33. package/dist/storage/domains/source-control/base.js +29 -0
  34. package/dist/storage/domains/source-control/base.js.map +1 -1
  35. package/dist/storage/domains/source-control/inmemory.d.ts +5 -1
  36. package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
  37. package/dist/storage/domains/source-control/inmemory.js +7 -0
  38. package/dist/storage/domains/source-control/inmemory.js.map +1 -1
  39. package/dist/tools/session-tools.d.ts.map +1 -1
  40. package/dist/tools/session-tools.js +1 -1
  41. package/dist/tools/session-tools.js.map +1 -1
  42. package/dist/workspace.d.ts +5 -2
  43. package/dist/workspace.d.ts.map +1 -1
  44. package/dist/workspace.js +11 -1
  45. package/dist/workspace.js.map +1 -1
  46. package/package.json +8 -8
@@ -1,5 +1,4 @@
1
1
  import type { ApiRoute } from '@mastra/core/server';
2
- import type { FilesystemStorage } from '../storage/domains/filesystem/base.js';
3
2
  import type { SourceControlSession } from '../storage/domains/source-control/base.js';
4
3
  import type { RouteAuth } from './route.js';
5
4
  /**
@@ -58,23 +57,25 @@ export interface WorkspaceFile {
58
57
  content?: string;
59
58
  truncated?: boolean;
60
59
  }
61
- export interface WorkspaceFilesListing {
62
- /** The Factory session resource id. */
60
+ export interface WorkspaceTreeEntry {
61
+ name: string;
62
+ /** Path from the session's layout root, the form the file route takes. */
63
+ path: string;
64
+ type: 'file' | 'directory';
65
+ size?: number;
66
+ /** Set when the entry is a symlink: where it points, as written in the link. */
67
+ symlinkTarget?: string;
68
+ /** Set when the entry is a symlink: the tree path of what it resolves to. */
69
+ linkedPath?: string;
70
+ }
71
+ /** One directory of a session's checkouts, read live from its sandbox. */
72
+ export interface WorkspaceTreeListing {
63
73
  workspacePath: string;
64
- /** The agent thread whose terminal file list was captured. */
65
- threadId: string;
66
- files: Array<{
67
- path: string;
68
- }>;
69
- /**
70
- * The environment repositories the paths are prefixed by, in position
71
- * order. Present only when the session's environment holds two or more
72
- * repositories; a one-repository session lists unprefixed paths.
73
- */
74
- repositories?: Array<{
75
- slug: string;
76
- prefix: string;
77
- }>;
74
+ /** The listed directory; empty for the top level. */
75
+ path: string;
76
+ /** False when this process holds no sandbox for the session, so nothing was read. */
77
+ available: boolean;
78
+ entries: WorkspaceTreeEntry[];
78
79
  }
79
80
  export type WorkspaceChangeStatus = 'modified' | 'added' | 'deleted' | 'renamed' | 'copied' | 'untracked' | 'conflicted';
80
81
  export interface WorkspaceChange {
@@ -139,15 +140,22 @@ export interface SessionFsDeps {
139
140
  sessions: {
140
141
  getBySessionId(sessionId: string): Promise<SourceControlSession | null>;
141
142
  };
142
- filesystem: Pick<FilesystemStorage, 'listFiles'>;
143
143
  }
144
- export declare function listSessionFilesystemFiles(filesystem: Pick<FilesystemStorage, 'listFiles'>, session: SourceControlSession, threadId: string): Promise<WorkspaceFilesListing>;
145
144
  /** List an approved rendered root inside a Factory session's sandbox workdir. */
146
145
  export declare function listSessionRenderedPath(session: SourceControlSession, renderedRoot: string): Promise<WorkspaceRenderedListing>;
147
- /** Read a file inside a session's sandbox. Paths outside rendered roots require a persisted-file allowlist check in the route. */
148
- export declare function readSessionWorkspaceFile(session: SourceControlSession, path: string, options?: {
149
- allowUnapprovedPath?: boolean;
150
- }): Promise<WorkspaceFile>;
146
+ /**
147
+ * List one directory of a session's working directory from its sandbox,
148
+ * without git internals or entries a checkout ignores. Passive like every
149
+ * session read: a session with no sandbox in this process comes back
150
+ * `available: false` instead of starting one.
151
+ */
152
+ export declare function listSessionWorkspaceTree(session: SourceControlSession, path?: string): Promise<WorkspaceTreeListing>;
153
+ /**
154
+ * Read a file inside a session's sandbox: anything under an approved rendered
155
+ * root, or anything the tree would list (not git internals, not ignored by its
156
+ * checkout).
157
+ */
158
+ export declare function readSessionWorkspaceFile(session: SourceControlSession, path: string): Promise<WorkspaceFile>;
151
159
  export declare function parseWorkspaceChanges(output: string): WorkspaceChange[];
152
160
  export declare function parseWorkspaceTrackedChanges(output: string): WorkspaceChange[];
153
161
  export declare function parseWorkspaceChangeStats(output: string): Map<string, {
@@ -1 +1 @@
1
- {"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../src/routes/fs.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../src/routes/fs.ts"],"names":[],"mappings":"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"}
package/dist/routes/fs.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { requireExec } from "../sandbox/materialization.js";
2
+ import { SETUP_MARKER_DIR } from "../packages/_internals/workspace/dist/index.js";
2
3
  import { peekSessionSandbox, sessionLayout } from "../sandbox/session-sandbox.js";
3
4
  import { peekSessionEnvironment } from "../session/environment-state-processor.js";
4
- import { waitForPendingFilesystemCapture } from "../session/filesystem-capture.js";
5
5
  import { registerApiRoute } from "@mastra/core/server";
6
6
  import { isAbsolute, join, posix, resolve, sep } from "path";
7
7
  import { SandboxFilesystem } from "@mastra/code-sdk/agents/sandbox-filesystem";
@@ -87,11 +87,10 @@ async function realPathWithinRoot(candidate, root) {
87
87
  }
88
88
  }
89
89
  function assertRelativePath(path, label) {
90
- const trimmed = path.trim();
91
- if (!trimmed) throw new Error(`Missing required query param: ${label}`);
92
- if (isAbsolute(trimmed)) throw new Error(`${label} must be relative`);
93
- if (trimmed.split(/[\\/]+/).includes("..")) throw new Error(`${label} escapes workspace`);
94
- const normalized = resolve("/", trimmed).slice(1);
90
+ if (!path.trim()) throw new Error(`Missing required query param: ${label}`);
91
+ if (isAbsolute(path)) throw new Error(`${label} must be relative`);
92
+ if (path.split(/[\\/]+/).includes("..")) throw new Error(`${label} escapes workspace`);
93
+ const normalized = resolve("/", path).slice(1);
95
94
  if (!normalized || normalized === ".." || normalized.startsWith(`..${sep}`)) throw new Error(`${label} escapes workspace`);
96
95
  return normalized;
97
96
  }
@@ -275,24 +274,6 @@ async function resolveAuthorizedSession(c, deps, workspacePath) {
275
274
  }
276
275
  return session;
277
276
  }
278
- async function listSessionFilesystemFiles(filesystem, session, threadId) {
279
- const safeThreadId = threadId.trim();
280
- if (!safeThreadId) throw new Error("Missing required query param: threadId");
281
- await waitForPendingFilesystemCapture(session.sessionId);
282
- const layout = sessionLayout(peekSessionSandbox(session.id));
283
- return {
284
- workspacePath: session.sessionId,
285
- threadId: safeThreadId,
286
- files: await filesystem.listFiles({
287
- resourceId: session.sessionId,
288
- threadId: safeThreadId
289
- }),
290
- ...layout && layout.repos.length > 1 ? { repositories: layout.repos.map((repo) => ({
291
- slug: repo.slug,
292
- prefix: repo.prefix
293
- })) } : {}
294
- };
295
- }
296
277
  /**
297
278
  * Resolve the session's sandbox from the per-process memo and wrap the
298
279
  * session layout's root in a `SandboxFilesystem`: the checkout for a
@@ -398,26 +379,211 @@ async function listSessionRenderedPath(session, renderedRoot) {
398
379
  entries
399
380
  };
400
381
  }
401
- /** Read a file inside a session's sandbox. Paths outside rendered roots require a persisted-file allowlist check in the route. */
402
- async function readSessionWorkspaceFile(session, path, options = {}) {
382
+ /**
383
+ * Directory entries the file tree never shows or serves: git internals and the
384
+ * sandbox's own setup markers.
385
+ */
386
+ const HIDDEN_TREE_ENTRIES = /* @__PURE__ */ new Set([".git", SETUP_MARKER_DIR]);
387
+ /**
388
+ * Where a tree path lives. Inside a checkout: that checkout and the path within
389
+ * it (a bare repository directory resolves to its root). Elsewhere in the
390
+ * working directory (a multi-repository session's root, `.artifacts`): no
391
+ * checkout, so no ignore rules apply.
392
+ */
393
+ function treeLocation(layout, path) {
394
+ const safePath = path ? assertRelativePath(path, "path") : "";
395
+ if (safePath.split("/").some((segment) => HIDDEN_TREE_ENTRIES.has(segment))) throw new Error("Path is not available in this session");
396
+ if (layout.repos.length === 1) return {
397
+ repo: layout.repos[0],
398
+ relative: safePath
399
+ };
400
+ const repo = layout.repos.filter((candidate) => `${safePath}/`.startsWith(candidate.prefix)).toSorted((a, b) => b.prefix.length - a.prefix.length)[0];
401
+ return repo ? {
402
+ repo,
403
+ relative: safePath.slice(repo.prefix.length)
404
+ } : {
405
+ repo: void 0,
406
+ relative: safePath
407
+ };
408
+ }
409
+ /**
410
+ * The checkout-relative paths git ignores, in one `git check-ignore` call.
411
+ * Ignored files (`.env`, build output, `node_modules`) are neither listed nor
412
+ * served: viewers of a session shouldn't read its local secrets. Fails closed
413
+ * when git can't answer.
414
+ */
415
+ async function ignoredPaths(sandbox, repoDir, paths) {
416
+ if (paths.length === 0) return /* @__PURE__ */ new Set();
417
+ const result = await sandbox.executeCommand("sh", [
418
+ "-c",
419
+ "cd \"$1\" && shift && printf '%s\\0' \"$@\" | git check-ignore -z --stdin",
420
+ "sh",
421
+ repoDir,
422
+ ...paths
423
+ ], {
424
+ timeout: 3e4,
425
+ maxRetainedBytes: Infinity
426
+ });
427
+ if (result.exitCode === 1) return /* @__PURE__ */ new Set();
428
+ if (result.exitCode !== 0) throw new Error(`Unable to check ignored files: ${result.stderr.trim() || "git failed"}`);
429
+ return new Set(result.stdout.split("\0").filter(Boolean));
430
+ }
431
+ /**
432
+ * The real absolute location of each path (relative to `root`), following
433
+ * every symlink, or `''` when it doesn't exist; plus `root`'s own. Paths go in
434
+ * as positional arguments and come back NUL-separated.
435
+ */
436
+ async function realPaths(sandbox, root, paths) {
437
+ const result = await sandbox.executeCommand("sh", [
438
+ "-c",
439
+ "cd \"$1\" && shift && printf '%s\\0' \"$(pwd -P)\" && for p; do printf '%s\\0' \"$([ -e \"$p\" ] && readlink -f -- \"$p\")\"; done",
440
+ "sh",
441
+ root,
442
+ ...paths
443
+ ], {
444
+ timeout: 3e4,
445
+ maxRetainedBytes: Infinity
446
+ });
447
+ if (result.exitCode !== 0) throw new Error(`Unable to resolve symlinks: ${result.stderr.trim() || "failed"}`);
448
+ const [physicalRoot = "", ...resolved] = result.stdout.split("\0");
449
+ return {
450
+ root: physicalRoot,
451
+ paths: resolved.slice(0, paths.length)
452
+ };
453
+ }
454
+ /** The first `bytes` bytes of a file, base64-encoded in the sandbox so binary content survives. */
455
+ async function readFilePrefix(sandbox, path, bytes) {
456
+ const result = await sandbox.executeCommand("sh", [
457
+ "-c",
458
+ "t=$(mktemp) && trap 'rm -f \"$t\"' EXIT && head -c \"$1\" -- \"$2\" >\"$t\" && base64 <\"$t\"",
459
+ "sh",
460
+ String(bytes),
461
+ path
462
+ ], {
463
+ timeout: 3e4,
464
+ maxRetainedBytes: Infinity
465
+ });
466
+ if (result.exitCode !== 0) throw new Error(`Unable to read file: ${result.stderr.trim() || "failed"}`);
467
+ return Buffer.from(result.stdout, "base64");
468
+ }
469
+ /**
470
+ * Where a real absolute path sits in the tree, or `undefined` when the tree
471
+ * can't show it: missing, outside the working directory, or inside git
472
+ * internals or setup markers.
473
+ */
474
+ function treePathOf(layout, physicalRoot, real) {
475
+ if (!real) return void 0;
476
+ const relative = posix.relative(physicalRoot, real);
477
+ if (relative === ".." || relative.startsWith("../") || posix.isAbsolute(relative)) return void 0;
478
+ try {
479
+ treeLocation(layout, relative);
480
+ } catch {
481
+ return;
482
+ }
483
+ return relative;
484
+ }
485
+ /** Which tree paths their checkout ignores: one `git check-ignore` per checkout involved. */
486
+ async function ignoredTreePaths(sandbox, layout, paths) {
487
+ const byRepo = /* @__PURE__ */ new Map();
488
+ for (const path of paths) {
489
+ const { repo, relative } = treeLocation(layout, path);
490
+ if (!repo || !relative) continue;
491
+ const inRepo = byRepo.get(repo) ?? /* @__PURE__ */ new Map();
492
+ inRepo.set(relative, path);
493
+ byRepo.set(repo, inRepo);
494
+ }
495
+ const ignored = /* @__PURE__ */ new Set();
496
+ for (const [repo, inRepo] of byRepo) for (const relative of await ignoredPaths(sandbox, repo.dir, [...inRepo.keys()])) ignored.add(inRepo.get(relative));
497
+ return ignored;
498
+ }
499
+ /**
500
+ * List one directory of a session's working directory from its sandbox,
501
+ * without git internals or entries a checkout ignores. Passive like every
502
+ * session read: a session with no sandbox in this process comes back
503
+ * `available: false` instead of starting one.
504
+ */
505
+ async function listSessionWorkspaceTree(session, path = "") {
506
+ const requested = path.trim() ? path : "";
507
+ const handle = await sessionSandbox(session);
508
+ const listing = {
509
+ workspacePath: session.sessionId,
510
+ path: requested
511
+ };
512
+ if (!handle) return {
513
+ ...listing,
514
+ available: false,
515
+ entries: []
516
+ };
517
+ const { layout, sandbox, filesystem } = handle;
518
+ const location = treeLocation(layout, requested);
519
+ const dir = (location.repo ? location.repo.prefix + location.relative : location.relative).replace(/\/$/, "");
520
+ const children = (await filesystem.readdir(dir || ".")).filter((entry) => !HIDDEN_TREE_ENTRIES.has(entry.name));
521
+ const childPath = (name) => dir ? `${dir}/${name}` : name;
522
+ const links = children.filter((entry) => entry.isSymlink);
523
+ const resolve = [...dir ? [dir] : [], ...links.map((entry) => childPath(entry.name))];
524
+ const real = resolve.length > 0 ? await realPaths(sandbox, layout.root, resolve) : void 0;
525
+ const linkTargets = real ? real.paths.slice(dir ? 1 : 0) : [];
526
+ if (dir && real.paths[0] !== posix.join(real.root, dir)) throw new Error("Path is not available in this session");
527
+ const targets = new Map(links.map((entry, i) => [entry.name, treePathOf(layout, real.root, linkTargets[i])]));
528
+ const ignored = await ignoredTreePaths(sandbox, layout, [
529
+ ...dir ? [dir] : [],
530
+ ...children.map((entry) => childPath(entry.name)),
531
+ ...[...targets.values()].filter((target) => target !== void 0)
532
+ ]);
533
+ if (dir && ignored.has(dir)) throw new Error("Path is not available in this session");
534
+ const entries = children.filter((entry) => {
535
+ if (ignored.has(childPath(entry.name))) return false;
536
+ if (!entry.isSymlink) return true;
537
+ const target = targets.get(entry.name);
538
+ return target !== void 0 && !ignored.has(target);
539
+ }).map((entry) => ({
540
+ name: entry.name,
541
+ path: childPath(entry.name),
542
+ type: entry.type,
543
+ ...entry.type === "file" && entry.size !== void 0 && { size: entry.size },
544
+ ...entry.isSymlink && {
545
+ symlinkTarget: entry.symlinkTarget ?? targets.get(entry.name),
546
+ linkedPath: targets.get(entry.name)
547
+ }
548
+ })).toSorted((a, b) => a.type === b.type ? a.name.localeCompare(b.name) : a.type === "directory" ? -1 : 1);
549
+ return {
550
+ ...listing,
551
+ available: true,
552
+ entries
553
+ };
554
+ }
555
+ /**
556
+ * Read a file inside a session's sandbox: anything under an approved rendered
557
+ * root, or anything the tree would list (not git internals, not ignored by its
558
+ * checkout).
559
+ */
560
+ async function readSessionWorkspaceFile(session, path) {
403
561
  const safePath = assertRelativePath(path, "path");
404
- if (!options.allowUnapprovedPath) assertApprovedRenderedRoot(safePath.split("/")[0] ?? "");
405
562
  const handle = await sessionSandbox(session);
406
563
  if (!handle) throw new Error("Session workspace is not available");
564
+ const { layout, sandbox } = handle;
565
+ treeLocation(layout, safePath);
566
+ const parent = posix.dirname(safePath);
567
+ const real = await realPaths(sandbox, layout.root, parent === "." ? [safePath] : [safePath, parent]);
568
+ const target = treePathOf(layout, real.root, real.paths[0]);
569
+ const throughLink = parent !== "." && real.paths[1] !== posix.join(real.root, parent);
570
+ if (target === void 0 || throughLink) throw new Error("Path is not available in this session");
571
+ const trusted = APPROVED_RENDERED_ROOTS.has(safePath.split("/")[0] ?? "");
572
+ if ((await ignoredTreePaths(sandbox, layout, [.../* @__PURE__ */ new Set([...trusted ? [] : [safePath], ...target === safePath ? [] : [target]])])).size > 0) throw new Error("Path is not available in this session");
407
573
  const { filesystem } = handle;
408
574
  const info = await filesystem.stat(safePath);
409
575
  if (info.type === "directory") throw new Error("Path is a directory");
410
- const buffer = await filesystem.readFile(safePath);
411
- const truncated = buffer.length > MAX_TEXT_FILE_BYTES;
576
+ const truncated = info.size > MAX_TEXT_FILE_BYTES;
577
+ const buffer = truncated ? await readFilePrefix(sandbox, real.paths[0], MAX_TEXT_FILE_BYTES) : await filesystem.readFile(safePath);
412
578
  const base = {
413
579
  workspacePath: session.sessionId,
414
580
  path: safePath,
415
581
  name: posix.basename(safePath),
416
- size: buffer.length,
582
+ size: info.size,
417
583
  updatedAt: info.modifiedAt.toISOString()
418
584
  };
419
585
  try {
420
- const content = TEXT_DECODER.decode(truncated ? buffer.subarray(0, MAX_TEXT_FILE_BYTES) : buffer);
586
+ const content = TEXT_DECODER.decode(buffer);
421
587
  return {
422
588
  ...base,
423
589
  contentType: "text",
@@ -808,57 +974,55 @@ function buildFsRoutes(options = {}) {
808
974
  }
809
975
  }
810
976
  }),
811
- registerApiRoute("/web/workspace/files", {
977
+ registerApiRoute("/web/workspace/changes", {
812
978
  method: "GET",
813
979
  requiresAuth: false,
814
980
  handler: async (c) => {
815
981
  const workspacePath = c.req.query("workspacePath");
816
- const threadId = c.req.query("threadId")?.trim();
817
982
  if (!workspacePath) return c.json({ error: "Missing required query param: workspacePath" }, 400);
818
- if (!threadId) return c.json({ error: "Missing required query param: threadId" }, 400);
819
983
  try {
820
984
  const session = await resolveAuthorizedSession(loose(c), sessionFs, workspacePath);
821
- if (!session || !sessionFs) return c.json({ error: "Session workspace is not available" }, 403);
822
- return c.json(await listSessionFilesystemFiles(sessionFs.filesystem, session, threadId));
985
+ if (!session || !sessionFs) return c.json(unavailableWorkspaceChanges(workspacePath));
986
+ return c.json(await listSessionWorkspaceChanges(session));
823
987
  } catch (error) {
824
988
  const message = error instanceof Error ? error.message : String(error);
825
- const status = message.includes("not available") ? 403 : 500;
826
- return c.json({ error: message }, status);
989
+ return c.json({ error: message }, message.includes("not available") ? 403 : 500);
827
990
  }
828
991
  }
829
992
  }),
830
- registerApiRoute("/web/workspace/changes", {
993
+ registerApiRoute("/web/workspace/changes/diff", {
831
994
  method: "GET",
832
995
  requiresAuth: false,
833
996
  handler: async (c) => {
834
997
  const workspacePath = c.req.query("workspacePath");
998
+ const path = c.req.query("path");
999
+ const previousPath = c.req.query("previousPath");
835
1000
  if (!workspacePath) return c.json({ error: "Missing required query param: workspacePath" }, 400);
1001
+ if (!path) return c.json({ error: "Missing required query param: path" }, 400);
836
1002
  try {
837
1003
  const session = await resolveAuthorizedSession(loose(c), sessionFs, workspacePath);
838
- if (!session || !sessionFs) return c.json(unavailableWorkspaceChanges(workspacePath));
839
- return c.json(await listSessionWorkspaceChanges(session));
1004
+ if (!session || !sessionFs) return c.json({ error: "Session workspace is not available" }, 403);
1005
+ return c.json(await readSessionWorkspaceDiff(session, path, previousPath));
840
1006
  } catch (error) {
841
1007
  const message = error instanceof Error ? error.message : String(error);
842
- return c.json({ error: message }, message.includes("not available") ? 403 : 500);
1008
+ const status = message.includes("relative") || message.includes("escapes") || message.includes("not available") ? 403 : 500;
1009
+ return c.json({ error: message }, status);
843
1010
  }
844
1011
  }
845
1012
  }),
846
- registerApiRoute("/web/workspace/changes/diff", {
1013
+ registerApiRoute("/web/workspace/tree", {
847
1014
  method: "GET",
848
1015
  requiresAuth: false,
849
1016
  handler: async (c) => {
850
1017
  const workspacePath = c.req.query("workspacePath");
851
- const path = c.req.query("path");
852
- const previousPath = c.req.query("previousPath");
853
1018
  if (!workspacePath) return c.json({ error: "Missing required query param: workspacePath" }, 400);
854
- if (!path) return c.json({ error: "Missing required query param: path" }, 400);
855
1019
  try {
856
1020
  const session = await resolveAuthorizedSession(loose(c), sessionFs, workspacePath);
857
1021
  if (!session || !sessionFs) return c.json({ error: "Session workspace is not available" }, 403);
858
- return c.json(await readSessionWorkspaceDiff(session, path, previousPath));
1022
+ return c.json(await listSessionWorkspaceTree(session, c.req.query("path") ?? ""));
859
1023
  } catch (error) {
860
1024
  const message = error instanceof Error ? error.message : String(error);
861
- const status = message.includes("relative") || message.includes("escapes") || message.includes("not available") ? 403 : 500;
1025
+ const status = message.includes("relative") || message.includes("escapes") || message.includes("not available") ? 403 : message.includes("not found") ? 404 : 500;
862
1026
  return c.json({ error: message }, status);
863
1027
  }
864
1028
  }
@@ -869,22 +1033,11 @@ function buildFsRoutes(options = {}) {
869
1033
  handler: async (c) => {
870
1034
  const workspacePath = c.req.query("workspacePath");
871
1035
  const path = c.req.query("path");
872
- const requestedThreadId = c.req.query("threadId");
873
- const threadId = requestedThreadId?.trim();
874
1036
  if (!workspacePath) return c.json({ error: "Missing required query param: workspacePath" }, 400);
875
1037
  if (!path) return c.json({ error: "Missing required query param: path" }, 400);
876
- if (requestedThreadId !== void 0 && !threadId) return c.json({ error: "Missing required query param: threadId" }, 400);
877
1038
  try {
878
1039
  const session = await resolveAuthorizedSession(loose(c), sessionFs, workspacePath);
879
- if (session && sessionFs) {
880
- if (threadId) {
881
- const safePath = assertRelativePath(path, "path");
882
- if (!(await listSessionFilesystemFiles(sessionFs.filesystem, session, threadId)).files.some((file) => file.path === safePath)) return c.json({ error: "Path is not available for this thread" }, 404);
883
- return c.json(await readSessionWorkspaceFile(session, safePath, { allowUnapprovedPath: true }));
884
- }
885
- return c.json(await readSessionWorkspaceFile(session, path));
886
- }
887
- if (threadId) return c.json({ error: "Session workspace is not available" }, 403);
1040
+ if (session && sessionFs) return c.json(await readSessionWorkspaceFile(session, path));
888
1041
  return c.json(await readWorkspaceFile(root, workspacePath, path));
889
1042
  } catch (error) {
890
1043
  const message = error instanceof Error ? error.message : String(error);
@@ -912,6 +1065,6 @@ function buildFsRoutes(options = {}) {
912
1065
  ];
913
1066
  }
914
1067
  //#endregion
915
- export { buildFsRoutes, listArtifacts, listDirectory, listSessionFilesystemFiles, listSessionRenderedPath, listSessionWorkspaceChanges, listWorkspaceRenderedPath, parseWorkspaceChangeStats, parseWorkspaceChanges, parseWorkspaceTrackedChanges, readSessionWorkspaceDiff, readSessionWorkspaceFile, readWorkspaceFile, resolveCodebase, resolveFsRoot };
1068
+ export { buildFsRoutes, listArtifacts, listDirectory, listSessionRenderedPath, listSessionWorkspaceChanges, listSessionWorkspaceTree, listWorkspaceRenderedPath, parseWorkspaceChangeStats, parseWorkspaceChanges, parseWorkspaceTrackedChanges, readSessionWorkspaceDiff, readSessionWorkspaceFile, readWorkspaceFile, resolveCodebase, resolveFsRoot };
916
1069
 
917
1070
  //# sourceMappingURL=fs.js.map