chatroom-cli 1.65.1 → 1.65.2

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/index.js CHANGED
@@ -88419,6 +88419,379 @@ function gunzipBase64Payload(base64, maxBytes) {
88419
88419
  }
88420
88420
  var init_workspace_path_security = () => {};
88421
88421
 
88422
+ // src/infrastructure/services/workspace/workspace-visibility-policy.ts
88423
+ function isAlwaysExcludedDirName(name) {
88424
+ return ALWAYS_EXCLUDE_DIR_NAMES.has(name);
88425
+ }
88426
+ function isSecretPath(relativePath) {
88427
+ const normalized = relativePath.replace(/\\/g, "/");
88428
+ return SECRET_PATH_PATTERNS.some((pattern2) => pattern2.test(normalized));
88429
+ }
88430
+ function hasExcludedDirSegment(relativePath) {
88431
+ const segments = relativePath.split("/");
88432
+ return segments.some((segment) => isAlwaysExcludedDirName(segment));
88433
+ }
88434
+ function isPathVisible(relativePath) {
88435
+ if (!relativePath)
88436
+ return true;
88437
+ return !isSecretPath(relativePath) && !hasExcludedDirSegment(relativePath);
88438
+ }
88439
+ function isPathContentReadable(relativePath) {
88440
+ return isPathVisible(relativePath);
88441
+ }
88442
+ var ALWAYS_EXCLUDE_DIR_NAMES, SECRET_PATH_PATTERNS;
88443
+ var init_workspace_visibility_policy = __esm(() => {
88444
+ ALWAYS_EXCLUDE_DIR_NAMES = new Set([
88445
+ "node_modules",
88446
+ ".git",
88447
+ "dist",
88448
+ "build",
88449
+ ".next",
88450
+ "coverage",
88451
+ "__pycache__",
88452
+ ".turbo",
88453
+ ".cache",
88454
+ ".tmp",
88455
+ "tmp",
88456
+ "_generated",
88457
+ ".vercel"
88458
+ ]);
88459
+ SECRET_PATH_PATTERNS = [
88460
+ /^\.env$/,
88461
+ /^\.env\./,
88462
+ /\.pem$/,
88463
+ /\.key$/,
88464
+ /id_rsa$/,
88465
+ /credentials\.json$/,
88466
+ /^secrets(\/|$)/,
88467
+ /^\.aws(\/|$)/
88468
+ ];
88469
+ });
88470
+
88471
+ // src/commands/machine/daemon-start/file-content-fulfillment.ts
88472
+ import { readFile as readFile7 } from "node:fs/promises";
88473
+ import { gzipSync as gzipSync2 } from "node:zlib";
88474
+ function getErrorCause(error) {
88475
+ if (typeof error === "object" && error !== null && "cause" in error && error.cause !== undefined) {
88476
+ return error.cause;
88477
+ }
88478
+ return error;
88479
+ }
88480
+ function isENOENT(error) {
88481
+ const cause3 = getErrorCause(error);
88482
+ return typeof cause3 === "object" && cause3 !== null && "code" in cause3 && cause3.code === "ENOENT";
88483
+ }
88484
+ function isBinaryFile(path3) {
88485
+ const lastDot = path3.lastIndexOf(".");
88486
+ if (lastDot === -1)
88487
+ return false;
88488
+ return BINARY_EXTENSIONS.has(path3.slice(lastDot).toLowerCase());
88489
+ }
88490
+ function gzipPlainText(text) {
88491
+ return gzipSync2(Buffer.from(text)).toString("base64");
88492
+ }
88493
+ function fulfillGzippedContentEffect(session2, workingDir, filePath, plainText, truncated) {
88494
+ return exports_Effect.catchAll(exports_Effect.tryPromise(() => session2.backend.mutation(api.workspaceFiles.fulfillFileContentV2, {
88495
+ sessionId: session2.sessionId,
88496
+ machineId: session2.machineId,
88497
+ workingDir,
88498
+ filePath,
88499
+ data: { compression: "gzip", content: gzipPlainText(plainText) },
88500
+ encoding: "utf8",
88501
+ truncated
88502
+ })), () => exports_Effect.void);
88503
+ }
88504
+ var MAX_CONTENT_BYTES, BINARY_EXTENSIONS, fulfillFileContentRequestsEffect;
88505
+ var init_file_content_fulfillment = __esm(() => {
88506
+ init_esm();
88507
+ init_daemon_services();
88508
+ init_api3();
88509
+ init_assert_registered_working_dir();
88510
+ init_workspace_path_security();
88511
+ init_workspace_visibility_policy();
88512
+ MAX_CONTENT_BYTES = 500 * 1024;
88513
+ BINARY_EXTENSIONS = new Set([
88514
+ ".png",
88515
+ ".jpg",
88516
+ ".jpeg",
88517
+ ".gif",
88518
+ ".webp",
88519
+ ".ico",
88520
+ ".svg",
88521
+ ".mp3",
88522
+ ".mp4",
88523
+ ".wav",
88524
+ ".ogg",
88525
+ ".webm",
88526
+ ".zip",
88527
+ ".tar",
88528
+ ".gz",
88529
+ ".bz2",
88530
+ ".7z",
88531
+ ".rar",
88532
+ ".pdf",
88533
+ ".doc",
88534
+ ".docx",
88535
+ ".xls",
88536
+ ".xlsx",
88537
+ ".ppt",
88538
+ ".pptx",
88539
+ ".woff",
88540
+ ".woff2",
88541
+ ".ttf",
88542
+ ".otf",
88543
+ ".eot",
88544
+ ".exe",
88545
+ ".dll",
88546
+ ".so",
88547
+ ".dylib",
88548
+ ".bin",
88549
+ ".dat",
88550
+ ".db",
88551
+ ".sqlite"
88552
+ ]);
88553
+ fulfillFileContentRequestsEffect = exports_Effect.gen(function* () {
88554
+ const session2 = yield* DaemonSessionService;
88555
+ const requests = yield* exports_Effect.catchAll(exports_Effect.tryPromise(() => session2.backend.query(api.workspaceFiles.getPendingFileContentRequests, {
88556
+ sessionId: session2.sessionId,
88557
+ machineId: session2.machineId
88558
+ })), () => exports_Effect.succeed([]));
88559
+ if (requests.length === 0)
88560
+ return;
88561
+ console.log(`[${formatTimestamp()}] \uD83D\uDCE5 Received ${requests.length} pending file content request(s): ${requests.map((r) => r.filePath).join(", ")}`);
88562
+ for (const request2 of requests) {
88563
+ const startTime = Date.now();
88564
+ const { workingDir, filePath } = request2;
88565
+ const registered = yield* exports_Effect.catchAll(exports_Effect.tryPromise(() => assertRegisteredWorkingDir(session2, workingDir)), () => exports_Effect.succeed({ ok: false, error: "Workspace check failed" }));
88566
+ if (!registered.ok) {
88567
+ console.warn(`[${formatTimestamp()}] ⚠️ Rejected unregistered workspace: ${workingDir} (${registered.error})`);
88568
+ yield* fulfillGzippedContentEffect(session2, workingDir, filePath, "[Error: workspace not registered]", false);
88569
+ continue;
88570
+ }
88571
+ const resolved = yield* exports_Effect.catchAll(exports_Effect.tryPromise(() => resolvePathWithinWorkspace(workingDir, filePath)), () => exports_Effect.succeed({ ok: false, error: "Invalid file path" }));
88572
+ if (!resolved.ok) {
88573
+ console.warn(`[${formatTimestamp()}] ⚠️ Rejected file path: ${filePath} (${resolved.error})`);
88574
+ yield* fulfillGzippedContentEffect(session2, workingDir, filePath, "[Error reading file]", false);
88575
+ continue;
88576
+ }
88577
+ const absolutePath = resolved.absolutePath;
88578
+ if (isBinaryFile(filePath)) {
88579
+ yield* fulfillGzippedContentEffect(session2, workingDir, filePath, "[Binary file]", false);
88580
+ const elapsed4 = Date.now() - startTime;
88581
+ console.log(`[${formatTimestamp()}] \uD83D\uDCC4 File content synced to Convex: ${filePath} [binary] (${elapsed4}ms)`);
88582
+ continue;
88583
+ }
88584
+ if (!isPathContentReadable(filePath)) {
88585
+ yield* fulfillGzippedContentEffect(session2, workingDir, filePath, "[File blocked: cannot open sensitive path in remote explorer]", false);
88586
+ const elapsed4 = Date.now() - startTime;
88587
+ console.log(`[${formatTimestamp()}] \uD83D\uDCC4 File content blocked: ${filePath} [secret] (${elapsed4}ms)`);
88588
+ continue;
88589
+ }
88590
+ const readOutcome = yield* exports_Effect.catchAll(exports_Effect.tryPromise(() => readFile7(absolutePath)).pipe(exports_Effect.map((buffer) => {
88591
+ if (buffer.length > MAX_CONTENT_BYTES) {
88592
+ return {
88593
+ kind: "ok",
88594
+ content: buffer.subarray(0, MAX_CONTENT_BYTES).toString("utf8"),
88595
+ truncated: true
88596
+ };
88597
+ }
88598
+ return {
88599
+ kind: "ok",
88600
+ content: buffer.toString("utf8"),
88601
+ truncated: false
88602
+ };
88603
+ })), (error) => isENOENT(error) ? exports_Effect.succeed({ kind: "missing" }) : exports_Effect.succeed({
88604
+ kind: "error",
88605
+ content: "[Error reading file]",
88606
+ truncated: false
88607
+ }));
88608
+ if (readOutcome.kind === "missing") {
88609
+ console.log(`[${formatTimestamp()}] ⏳ File not on disk yet, deferring content sync: ${filePath}`);
88610
+ continue;
88611
+ }
88612
+ const { content, truncated } = readOutcome.kind === "ok" ? readOutcome : { content: readOutcome.content, truncated: readOutcome.truncated };
88613
+ const compressed = gzipSync2(Buffer.from(content));
88614
+ const contentCompressed = compressed.toString("base64");
88615
+ yield* exports_Effect.catchAll(exports_Effect.tryPromise(() => session2.backend.mutation(api.workspaceFiles.fulfillFileContentV2, {
88616
+ sessionId: session2.sessionId,
88617
+ machineId: session2.machineId,
88618
+ workingDir,
88619
+ filePath,
88620
+ data: { compression: "gzip", content: contentCompressed },
88621
+ encoding: "utf8",
88622
+ truncated
88623
+ })), () => exports_Effect.void);
88624
+ const elapsed3 = Date.now() - startTime;
88625
+ console.log(`[${formatTimestamp()}] \uD83D\uDCC4 File content synced to Convex: ${filePath} (${(Buffer.byteLength(content) / 1024).toFixed(1)}KB → ${(compressed.length / 1024).toFixed(1)}KB gzip, ${elapsed3}ms)`);
88626
+ }
88627
+ });
88628
+ });
88629
+
88630
+ // src/commands/machine/daemon-start/file-content-subscription.ts
88631
+ var startFileContentSubscriptionEffect = (wsClient2) => exports_Effect.gen(function* () {
88632
+ const session2 = yield* DaemonSessionService;
88633
+ let processing = false;
88634
+ const unsubscribe = wsClient2.onUpdate(api.workspaceFiles.getPendingFileContentRequests, {
88635
+ sessionId: session2.sessionId,
88636
+ machineId: session2.machineId
88637
+ }, (requests) => {
88638
+ if (!requests || requests.length === 0)
88639
+ return;
88640
+ if (processing)
88641
+ return;
88642
+ processing = true;
88643
+ exports_Effect.runPromise(fulfillFileContentRequestsEffect.pipe(exports_Effect.provideService(DaemonSessionService, session2))).catch((err) => {
88644
+ console.warn(`[${formatTimestamp()}] ⚠️ File content subscription processing failed: ${getErrorMessage2(err)}`);
88645
+ }).finally(() => {
88646
+ processing = false;
88647
+ });
88648
+ }, (err) => {
88649
+ console.warn(`[${formatTimestamp()}] ⚠️ File content subscription error: ${getErrorMessage2(err)}`);
88650
+ });
88651
+ console.log(`[${formatTimestamp()}] \uD83D\uDCC2 File content subscription started (reactive)`);
88652
+ return {
88653
+ stop: () => {
88654
+ unsubscribe();
88655
+ console.log(`[${formatTimestamp()}] \uD83D\uDCC2 File content subscription stopped`);
88656
+ }
88657
+ };
88658
+ });
88659
+ var init_file_content_subscription = __esm(() => {
88660
+ init_esm();
88661
+ init_daemon_services();
88662
+ init_file_content_fulfillment();
88663
+ init_api3();
88664
+ init_convex_error();
88665
+ });
88666
+
88667
+ // src/infrastructure/services/workspace/file-tree-data-hash.ts
88668
+ import { createHash as createHash4 } from "node:crypto";
88669
+ function computeFileTreeDataHash(tree) {
88670
+ return createHash4("md5").update(JSON.stringify(tree)).digest("hex");
88671
+ }
88672
+ var init_file_tree_data_hash = () => {};
88673
+
88674
+ // src/infrastructure/services/workspace/file-tree-partition.ts
88675
+ import { createHash as createHash5 } from "node:crypto";
88676
+ import { gzipSync as gzipSync3 } from "node:zlib";
88677
+ function computeShardDataHash(payload) {
88678
+ return createHash5("md5").update(JSON.stringify(payload)).digest("hex");
88679
+ }
88680
+ function shardIdForPath(path3) {
88681
+ const slash = path3.indexOf("/");
88682
+ return slash === -1 ? "__root__" : path3.slice(0, slash);
88683
+ }
88684
+ function shouldUseV3Upload(tree) {
88685
+ return Buffer.byteLength(JSON.stringify(tree), "utf8") > MAX_TREE_JSON_BYTES;
88686
+ }
88687
+ function childShardId(path3, parentShardId) {
88688
+ if (parentShardId === "__root__") {
88689
+ return shardIdForPath(path3);
88690
+ }
88691
+ if (path3 === parentShardId) {
88692
+ return parentShardId;
88693
+ }
88694
+ const prefix = `${parentShardId}/`;
88695
+ if (!path3.startsWith(prefix)) {
88696
+ return parentShardId;
88697
+ }
88698
+ const remainder = path3.slice(prefix.length);
88699
+ const slash = remainder.indexOf("/");
88700
+ return slash === -1 ? parentShardId : `${parentShardId}/${remainder.slice(0, slash)}`;
88701
+ }
88702
+ function groupEntriesByShardId(entries2, parentShardId) {
88703
+ const groups = new Map;
88704
+ for (const entry of entries2) {
88705
+ const shardId = parentShardId === "__root__" ? shardIdForPath(entry.path) : childShardId(entry.path, parentShardId);
88706
+ const group = groups.get(shardId) ?? [];
88707
+ group.push(entry);
88708
+ groups.set(shardId, group);
88709
+ }
88710
+ return groups;
88711
+ }
88712
+ function buildPreparedShard(shardId, payload) {
88713
+ const compressed = gzipSync3(Buffer.from(JSON.stringify(payload))).toString("base64");
88714
+ return {
88715
+ shardId,
88716
+ payload,
88717
+ dataHash: computeShardDataHash(payload),
88718
+ entryCount: payload.entries.length,
88719
+ data: { compression: "gzip", content: compressed }
88720
+ };
88721
+ }
88722
+ function prepareShardGroup(entries2, shardId, tree) {
88723
+ const payload = {
88724
+ entries: entries2,
88725
+ scannedAt: tree.scannedAt,
88726
+ rootDir: tree.rootDir
88727
+ };
88728
+ const compressed = gzipSync3(Buffer.from(JSON.stringify(payload))).toString("base64");
88729
+ if (Buffer.byteLength(compressed, "utf8") <= MAX_SHARD_JSON_BYTES) {
88730
+ return [buildPreparedShard(shardId, payload)];
88731
+ }
88732
+ const subgroups = groupEntriesByShardId(entries2, shardId);
88733
+ if (subgroups.size <= 1) {
88734
+ return [buildPreparedShard(shardId, payload)];
88735
+ }
88736
+ const shards = [];
88737
+ for (const [subId, subEntries] of subgroups) {
88738
+ shards.push(...prepareShardGroup(subEntries, subId, tree));
88739
+ }
88740
+ return shards;
88741
+ }
88742
+ function partitionFileTree(tree) {
88743
+ const topGroups = groupEntriesByShardId(tree.entries, "__root__");
88744
+ const shards = [];
88745
+ for (const [shardId, entries2] of topGroups) {
88746
+ shards.push(...prepareShardGroup(entries2, shardId, tree));
88747
+ }
88748
+ return shards;
88749
+ }
88750
+ var MAX_TREE_JSON_BYTES, MAX_SHARD_JSON_BYTES, MAX_SHARD_BATCH_SIZE = 8;
88751
+ var init_file_tree_partition = __esm(() => {
88752
+ MAX_TREE_JSON_BYTES = 900 * 1024;
88753
+ MAX_SHARD_JSON_BYTES = 800 * 1024;
88754
+ });
88755
+
88756
+ // src/infrastructure/services/workspace/file-tree-v3-upload.ts
88757
+ async function uploadFileTreeV3(session2, workingDir, tree, syncGeneration) {
88758
+ const shards = partitionFileTree(tree);
88759
+ const shardIds = [];
88760
+ for (let i2 = 0;i2 < shards.length; i2 += MAX_SHARD_BATCH_SIZE) {
88761
+ const batch = shards.slice(i2, i2 + MAX_SHARD_BATCH_SIZE);
88762
+ await session2.backend.mutation(api.workspaceFiles.syncFileTreeShardV3Batch, {
88763
+ sessionId: session2.sessionId,
88764
+ machineId: session2.machineId,
88765
+ workingDir,
88766
+ syncGeneration,
88767
+ items: batch.map((s) => ({
88768
+ shardId: s.shardId,
88769
+ data: s.data,
88770
+ dataHash: s.dataHash,
88771
+ scannedAt: tree.scannedAt,
88772
+ entryCount: s.entryCount
88773
+ }))
88774
+ });
88775
+ for (const s of batch)
88776
+ shardIds.push(s.shardId);
88777
+ }
88778
+ await session2.backend.mutation(api.workspaceFiles.syncFileTreeManifestV3, {
88779
+ sessionId: session2.sessionId,
88780
+ machineId: session2.machineId,
88781
+ workingDir,
88782
+ syncGeneration,
88783
+ shardIds,
88784
+ totalEntryCount: tree.entries.length,
88785
+ complete: true,
88786
+ scannedAt: tree.scannedAt
88787
+ });
88788
+ return { shardIds, totalEntryCount: tree.entries.length };
88789
+ }
88790
+ var init_file_tree_v3_upload = __esm(() => {
88791
+ init_file_tree_partition();
88792
+ init_api3();
88793
+ });
88794
+
88422
88795
  // ../../node_modules/.pnpm/ignore@7.0.5/node_modules/ignore/index.js
88423
88796
  var require_ignore = __commonJS((exports, module) => {
88424
88797
  function makeArray(subject) {
@@ -88747,574 +89120,1839 @@ var require_ignore = __commonJS((exports, module) => {
88747
89120
  });
88748
89121
 
88749
89122
  // src/infrastructure/services/workspace/workspace-ignore.ts
88750
- import { readFile as readFile7 } from "node:fs/promises";
89123
+ import { readFile as readFile8 } from "node:fs/promises";
88751
89124
  import path3 from "node:path";
88752
- async function loadWorkspaceIgnore(rootDir) {
88753
- const ig = import_ignore.default();
88754
- for (const name of IGNORE_FILES) {
88755
- try {
88756
- const content = await readFile7(path3.join(rootDir, name), "utf-8");
88757
- ig.add(content);
88758
- } catch {}
88759
- }
88760
- return ig;
88761
- }
88762
- function isPathIgnoredByRules(ig, relativePath) {
88763
- const normalized = relativePath.replace(/\\/g, "/");
88764
- return ig.ignores(normalized);
88765
- }
88766
- var import_ignore, IGNORE_FILES;
88767
- var init_workspace_ignore = __esm(() => {
88768
- import_ignore = __toESM(require_ignore(), 1);
88769
- IGNORE_FILES = [".gitignore", ".cursorignore"];
88770
- });
88771
-
88772
- // src/infrastructure/services/workspace/workspace-visibility-policy.ts
88773
- import { spawn as spawn6 } from "node:child_process";
88774
- function isAlwaysExcludedDirName(name) {
88775
- return ALWAYS_EXCLUDE_DIR_NAMES.has(name);
89125
+ function normalizeRelativePath(relativePath) {
89126
+ return relativePath.replace(/\\/g, "/").replace(/^\.?\//, "").replace(/\/+$/, "");
88776
89127
  }
88777
- function isSecretPath(relativePath) {
88778
- const normalized = relativePath.replace(/\\/g, "/");
88779
- return SECRET_PATH_PATTERNS.some((pattern2) => pattern2.test(normalized));
88780
- }
88781
- function hasExcludedDirSegment(relativePath) {
88782
- const segments = relativePath.split("/");
88783
- return segments.some((segment) => isAlwaysExcludedDirName(segment));
88784
- }
88785
- function isPathVisible(relativePath) {
88786
- if (!relativePath)
88787
- return true;
88788
- return !isSecretPath(relativePath) && !hasExcludedDirSegment(relativePath);
89128
+ async function readIgnoreFile(filePath) {
89129
+ try {
89130
+ return await readFile8(filePath, "utf-8");
89131
+ } catch {
89132
+ return null;
89133
+ }
88789
89134
  }
88790
- function isPathContentReadable(relativePath) {
88791
- return isPathVisible(relativePath);
89135
+ async function loadDirectoryIgnoreRuleSets(rootDir, relativeDir) {
89136
+ const normalizedDir = normalizeRelativePath(relativeDir);
89137
+ const absoluteDir = normalizedDir ? path3.join(rootDir, normalizedDir) : rootDir;
89138
+ const names = normalizedDir ? [".gitignore"] : IGNORE_FILES;
89139
+ const result = [];
89140
+ for (const name of names) {
89141
+ const content = await readIgnoreFile(path3.join(absoluteDir, name));
89142
+ if (content === null)
89143
+ continue;
89144
+ result.push({ baseDir: normalizedDir, matcher: import_ignore.default().add(content) });
89145
+ }
89146
+ return result;
88792
89147
  }
88793
- async function filterIgnoredPaths(rootDir, relativePaths) {
88794
- if (relativePaths.length === 0)
88795
- return new Set;
88796
- const inRepo = await isGitRepo(rootDir);
88797
- if (inRepo)
88798
- return filterGitIgnored(rootDir, relativePaths);
88799
- const ig = await loadWorkspaceIgnore(rootDir);
88800
- const ignored = new Set;
88801
- for (const p of relativePaths) {
88802
- if (isPathIgnoredByRules(ig, p))
88803
- ignored.add(p);
89148
+ function isPathIgnoredByRuleSets(ruleSets, relativePath) {
89149
+ const normalized = normalizeRelativePath(relativePath);
89150
+ let ignored = false;
89151
+ for (const ruleSet of ruleSets) {
89152
+ if (ruleSet.baseDir && normalized !== ruleSet.baseDir && !normalized.startsWith(`${ruleSet.baseDir}/`)) {
89153
+ continue;
89154
+ }
89155
+ const localPath = ruleSet.baseDir ? normalized.slice(ruleSet.baseDir.length).replace(/^\/+/, "") : normalized;
89156
+ if (!localPath)
89157
+ continue;
89158
+ const result = ruleSet.matcher.test(localPath);
89159
+ if (result.ignored)
89160
+ ignored = true;
89161
+ else if (result.unignored)
89162
+ ignored = false;
88804
89163
  }
88805
89164
  return ignored;
88806
89165
  }
88807
- async function filterGitIgnored(rootDir, relativePaths) {
88808
- if (relativePaths.length === 0)
88809
- return new Set;
88810
- const inRepo = await isGitRepo(rootDir);
88811
- if (!inRepo)
88812
- return new Set;
88813
- try {
88814
- const stdout = await new Promise((resolve5, reject) => {
88815
- const child = spawn6("git", ["check-ignore", "--stdin", "-z"], {
88816
- cwd: rootDir,
88817
- env: { ...process.env, GIT_TERMINAL_PROMPT: "0", GIT_PAGER: "cat", NO_COLOR: "1" }
88818
- });
88819
- let output = "";
88820
- child.stdout.on("data", (chunk2) => {
88821
- output += chunk2.toString();
88822
- });
88823
- child.on("error", reject);
88824
- child.on("close", () => resolve5(output));
88825
- child.stdin.write(relativePaths.join(`
88826
- `));
88827
- child.stdin.end();
88828
- });
88829
- const ignored = new Set;
88830
- if (!stdout)
88831
- return ignored;
88832
- for (const entry of stdout.split("\x00")) {
88833
- const trimmed = entry.trim();
88834
- if (trimmed)
88835
- ignored.add(trimmed);
88836
- }
88837
- return ignored;
88838
- } catch {
88839
- return new Set;
89166
+ async function loadApplicableIgnoreRuleSets(rootDir, relativePath) {
89167
+ const normalized = normalizeRelativePath(relativePath);
89168
+ const parentParts = normalized.split("/").slice(0, -1);
89169
+ const ruleSets = [];
89170
+ ruleSets.push(...await loadDirectoryIgnoreRuleSets(rootDir, ""));
89171
+ let currentDir = "";
89172
+ for (const part of parentParts) {
89173
+ currentDir = currentDir ? `${currentDir}/${part}` : part;
89174
+ ruleSets.push(...await loadDirectoryIgnoreRuleSets(rootDir, currentDir));
88840
89175
  }
89176
+ return ruleSets;
88841
89177
  }
88842
- var ALWAYS_EXCLUDE_DIR_NAMES, SECRET_PATH_PATTERNS;
88843
- var init_workspace_visibility_policy = __esm(() => {
88844
- init_workspace_ignore();
88845
- init_git_reader();
88846
- ALWAYS_EXCLUDE_DIR_NAMES = new Set([
88847
- "node_modules",
88848
- ".git",
88849
- "dist",
88850
- "build",
88851
- ".next",
88852
- "coverage",
88853
- "__pycache__",
88854
- ".turbo",
88855
- ".cache",
88856
- ".tmp",
88857
- "tmp",
88858
- "_generated",
88859
- ".vercel"
88860
- ]);
88861
- SECRET_PATH_PATTERNS = [
88862
- /^\.env$/,
88863
- /^\.env\./,
88864
- /\.pem$/,
88865
- /\.key$/,
88866
- /id_rsa$/,
88867
- /credentials\.json$/,
88868
- /^secrets(\/|$)/,
88869
- /^\.aws(\/|$)/
88870
- ];
89178
+ async function isWorkspacePathIgnored(rootDir, relativePath) {
89179
+ const ruleSets = await loadApplicableIgnoreRuleSets(rootDir, relativePath);
89180
+ return isPathIgnoredByRuleSets(ruleSets, relativePath);
89181
+ }
89182
+ var import_ignore, IGNORE_FILES;
89183
+ var init_workspace_ignore = __esm(() => {
89184
+ import_ignore = __toESM(require_ignore(), 1);
89185
+ IGNORE_FILES = [".gitignore", ".cursorignore"];
88871
89186
  });
88872
89187
 
88873
- // src/commands/machine/daemon-start/file-content-fulfillment.ts
88874
- import { readFile as readFile8 } from "node:fs/promises";
88875
- import { gzipSync as gzipSync2 } from "node:zlib";
88876
- function getErrorCause(error) {
88877
- if (typeof error === "object" && error !== null && "cause" in error && error.cause !== undefined) {
88878
- return error.cause;
89188
+ // src/infrastructure/services/workspace/workspace-file-walk.ts
89189
+ import { promises as fsPromises } from "node:fs";
89190
+ import path4 from "node:path";
89191
+ async function walkWorkspaceFiles(rootDir, options) {
89192
+ const maxFilePaths = options?.maxFilePaths ?? 1e4;
89193
+ const filePaths = [];
89194
+ let truncated = false;
89195
+ async function visitDir(relDir, inheritedRuleSets) {
89196
+ if (truncated || filePaths.length >= maxFilePaths) {
89197
+ truncated = true;
89198
+ return;
89199
+ }
89200
+ const localRuleSets = await loadDirectoryIgnoreRuleSets(rootDir, relDir);
89201
+ const ruleSets = localRuleSets.length === 0 ? inheritedRuleSets : [...inheritedRuleSets, ...localRuleSets];
89202
+ const absDir = relDir ? path4.join(rootDir, relDir) : rootDir;
89203
+ let dirents;
89204
+ try {
89205
+ dirents = await fsPromises.readdir(absDir, { withFileTypes: true });
89206
+ } catch {
89207
+ return;
89208
+ }
89209
+ for (const ent of dirents) {
89210
+ if (truncated || filePaths.length >= maxFilePaths) {
89211
+ truncated = true;
89212
+ return;
89213
+ }
89214
+ if (isAlwaysExcludedDirName(ent.name))
89215
+ continue;
89216
+ const relativePath = relDir ? `${relDir}/${ent.name}` : ent.name;
89217
+ if (!isPathVisible(relativePath))
89218
+ continue;
89219
+ if (ent.isDirectory()) {
89220
+ if (isPathIgnoredByRuleSets(ruleSets, relativePath))
89221
+ continue;
89222
+ await visitDir(relativePath, ruleSets);
89223
+ } else if (ent.isFile()) {
89224
+ if (isPathIgnoredByRuleSets(ruleSets, relativePath))
89225
+ continue;
89226
+ filePaths.push(relativePath);
89227
+ if (filePaths.length >= maxFilePaths)
89228
+ truncated = true;
89229
+ }
89230
+ }
88879
89231
  }
88880
- return error;
88881
- }
88882
- function isENOENT(error) {
88883
- const cause3 = getErrorCause(error);
88884
- return typeof cause3 === "object" && cause3 !== null && "code" in cause3 && cause3.code === "ENOENT";
89232
+ await visitDir("", []);
89233
+ return { filePaths, truncated };
88885
89234
  }
88886
- function isBinaryFile(path4) {
88887
- const lastDot = path4.lastIndexOf(".");
88888
- if (lastDot === -1)
88889
- return false;
88890
- return BINARY_EXTENSIONS.has(path4.slice(lastDot).toLowerCase());
89235
+ var init_workspace_file_walk = __esm(() => {
89236
+ init_workspace_ignore();
89237
+ init_workspace_visibility_policy();
89238
+ });
89239
+
89240
+ // src/infrastructure/services/workspace/file-tree-scanner.ts
89241
+ async function scanFileTree(rootDir, options) {
89242
+ const maxEntries = options?.maxEntries ?? DEFAULT_MAX_ENTRIES;
89243
+ const scannedAt = Date.now();
89244
+ const walk = await walkWorkspaceFiles(rootDir, { maxFilePaths: maxEntries });
89245
+ const filteredPaths = walk.filePaths.filter((p) => !isExcluded(p));
89246
+ const entries2 = buildEntries(filteredPaths, maxEntries);
89247
+ return {
89248
+ entries: entries2,
89249
+ scannedAt,
89250
+ rootDir
89251
+ };
88891
89252
  }
88892
- function gzipPlainText(text) {
88893
- return gzipSync2(Buffer.from(text)).toString("base64");
89253
+ function isExcluded(filePath) {
89254
+ return hasExcludedDirSegment(filePath);
88894
89255
  }
88895
- function fulfillGzippedContentEffect(session2, workingDir, filePath, plainText, truncated) {
88896
- return exports_Effect.catchAll(exports_Effect.tryPromise(() => session2.backend.mutation(api.workspaceFiles.fulfillFileContentV2, {
88897
- sessionId: session2.sessionId,
88898
- machineId: session2.machineId,
88899
- workingDir,
88900
- filePath,
88901
- data: { compression: "gzip", content: gzipPlainText(plainText) },
88902
- encoding: "utf8",
88903
- truncated
88904
- })), () => exports_Effect.void);
89256
+ function buildEntries(filePaths, maxEntries) {
89257
+ const directories = new Set;
89258
+ for (const filePath of filePaths) {
89259
+ const parts2 = filePath.split("/");
89260
+ for (let i2 = 1;i2 < parts2.length; i2++) {
89261
+ directories.add(parts2.slice(0, i2).join("/"));
89262
+ }
89263
+ }
89264
+ const entries2 = [];
89265
+ const sortedDirs = Array.from(directories).sort();
89266
+ for (const dir of sortedDirs) {
89267
+ if (entries2.length >= maxEntries)
89268
+ break;
89269
+ entries2.push({ path: dir, type: "directory" });
89270
+ }
89271
+ const sortedFiles = filePaths.slice().sort();
89272
+ for (const file of sortedFiles) {
89273
+ if (entries2.length >= maxEntries)
89274
+ break;
89275
+ entries2.push({ path: file, type: "file" });
89276
+ }
89277
+ return entries2;
88905
89278
  }
88906
- var MAX_CONTENT_BYTES, BINARY_EXTENSIONS, fulfillFileContentRequestsEffect;
88907
- var init_file_content_fulfillment = __esm(() => {
88908
- init_esm();
88909
- init_daemon_services();
88910
- init_api3();
88911
- init_assert_registered_working_dir();
88912
- init_workspace_path_security();
89279
+ var DEFAULT_MAX_ENTRIES = 1e4;
89280
+ var init_file_tree_scanner = __esm(() => {
89281
+ init_workspace_file_walk();
88913
89282
  init_workspace_visibility_policy();
88914
- MAX_CONTENT_BYTES = 500 * 1024;
88915
- BINARY_EXTENSIONS = new Set([
88916
- ".png",
88917
- ".jpg",
88918
- ".jpeg",
88919
- ".gif",
88920
- ".webp",
88921
- ".ico",
88922
- ".svg",
88923
- ".mp3",
88924
- ".mp4",
88925
- ".wav",
88926
- ".ogg",
88927
- ".webm",
88928
- ".zip",
88929
- ".tar",
88930
- ".gz",
88931
- ".bz2",
88932
- ".7z",
88933
- ".rar",
88934
- ".pdf",
88935
- ".doc",
88936
- ".docx",
88937
- ".xls",
88938
- ".xlsx",
88939
- ".ppt",
88940
- ".pptx",
88941
- ".woff",
88942
- ".woff2",
88943
- ".ttf",
88944
- ".otf",
88945
- ".eot",
88946
- ".exe",
88947
- ".dll",
88948
- ".so",
88949
- ".dylib",
88950
- ".bin",
88951
- ".dat",
88952
- ".db",
88953
- ".sqlite"
89283
+ });
89284
+
89285
+ // ../../node_modules/.pnpm/readdirp@5.0.0/node_modules/readdirp/index.js
89286
+ import { lstat, readdir, realpath as realpath3, stat as stat2 } from "node:fs/promises";
89287
+ import { join as pjoin, relative as prelative, resolve as presolve, sep as psep } from "node:path";
89288
+ import { Readable as Readable3 } from "node:stream";
89289
+ function readdirp(root, options = {}) {
89290
+ let type = options.entryType || options.type;
89291
+ if (type === "both")
89292
+ type = EntryTypes.FILE_DIR_TYPE;
89293
+ if (type)
89294
+ options.type = type;
89295
+ if (!root) {
89296
+ throw new Error("readdirp: root argument is required. Usage: readdirp(root, options)");
89297
+ } else if (typeof root !== "string") {
89298
+ throw new TypeError("readdirp: root argument must be a string. Usage: readdirp(root, options)");
89299
+ } else if (type && !ALL_TYPES.includes(type)) {
89300
+ throw new Error(`readdirp: Invalid type passed. Use one of ${ALL_TYPES.join(", ")}`);
89301
+ }
89302
+ options.root = root;
89303
+ return new ReaddirpStream(options);
89304
+ }
89305
+ var EntryTypes, defaultOptions, RECURSIVE_ERROR_CODE = "READDIRP_RECURSIVE_ERROR", NORMAL_FLOW_ERRORS, ALL_TYPES, DIR_TYPES, FILE_TYPES, isNormalFlowError = (error) => NORMAL_FLOW_ERRORS.has(error.code), wantBigintFsStats, emptyFn = (_entryInfo) => true, normalizeFilter = (filter11) => {
89306
+ if (filter11 === undefined)
89307
+ return emptyFn;
89308
+ if (typeof filter11 === "function")
89309
+ return filter11;
89310
+ if (typeof filter11 === "string") {
89311
+ const fl = filter11.trim();
89312
+ return (entry) => entry.basename === fl;
89313
+ }
89314
+ if (Array.isArray(filter11)) {
89315
+ const trItems = filter11.map((item) => item.trim());
89316
+ return (entry) => trItems.some((f) => entry.basename === f);
89317
+ }
89318
+ return emptyFn;
89319
+ }, ReaddirpStream;
89320
+ var init_readdirp = __esm(() => {
89321
+ EntryTypes = {
89322
+ FILE_TYPE: "files",
89323
+ DIR_TYPE: "directories",
89324
+ FILE_DIR_TYPE: "files_directories",
89325
+ EVERYTHING_TYPE: "all"
89326
+ };
89327
+ defaultOptions = {
89328
+ root: ".",
89329
+ fileFilter: (_entryInfo) => true,
89330
+ directoryFilter: (_entryInfo) => true,
89331
+ type: EntryTypes.FILE_TYPE,
89332
+ lstat: false,
89333
+ depth: 2147483648,
89334
+ alwaysStat: false,
89335
+ highWaterMark: 4096
89336
+ };
89337
+ Object.freeze(defaultOptions);
89338
+ NORMAL_FLOW_ERRORS = new Set(["ENOENT", "EPERM", "EACCES", "ELOOP", RECURSIVE_ERROR_CODE]);
89339
+ ALL_TYPES = [
89340
+ EntryTypes.DIR_TYPE,
89341
+ EntryTypes.EVERYTHING_TYPE,
89342
+ EntryTypes.FILE_DIR_TYPE,
89343
+ EntryTypes.FILE_TYPE
89344
+ ];
89345
+ DIR_TYPES = new Set([
89346
+ EntryTypes.DIR_TYPE,
89347
+ EntryTypes.EVERYTHING_TYPE,
89348
+ EntryTypes.FILE_DIR_TYPE
88954
89349
  ]);
88955
- fulfillFileContentRequestsEffect = exports_Effect.gen(function* () {
88956
- const session2 = yield* DaemonSessionService;
88957
- const requests = yield* exports_Effect.catchAll(exports_Effect.tryPromise(() => session2.backend.query(api.workspaceFiles.getPendingFileContentRequests, {
88958
- sessionId: session2.sessionId,
88959
- machineId: session2.machineId
88960
- })), () => exports_Effect.succeed([]));
88961
- if (requests.length === 0)
89350
+ FILE_TYPES = new Set([
89351
+ EntryTypes.EVERYTHING_TYPE,
89352
+ EntryTypes.FILE_DIR_TYPE,
89353
+ EntryTypes.FILE_TYPE
89354
+ ]);
89355
+ wantBigintFsStats = process.platform === "win32";
89356
+ ReaddirpStream = class ReaddirpStream extends Readable3 {
89357
+ parents;
89358
+ reading;
89359
+ parent;
89360
+ _stat;
89361
+ _maxDepth;
89362
+ _wantsDir;
89363
+ _wantsFile;
89364
+ _wantsEverything;
89365
+ _root;
89366
+ _isDirent;
89367
+ _statsProp;
89368
+ _rdOptions;
89369
+ _fileFilter;
89370
+ _directoryFilter;
89371
+ constructor(options = {}) {
89372
+ super({
89373
+ objectMode: true,
89374
+ autoDestroy: true,
89375
+ highWaterMark: options.highWaterMark
89376
+ });
89377
+ const opts = { ...defaultOptions, ...options };
89378
+ const { root, type } = opts;
89379
+ this._fileFilter = normalizeFilter(opts.fileFilter);
89380
+ this._directoryFilter = normalizeFilter(opts.directoryFilter);
89381
+ const statMethod = opts.lstat ? lstat : stat2;
89382
+ if (wantBigintFsStats) {
89383
+ this._stat = (path5) => statMethod(path5, { bigint: true });
89384
+ } else {
89385
+ this._stat = statMethod;
89386
+ }
89387
+ this._maxDepth = opts.depth != null && Number.isSafeInteger(opts.depth) ? opts.depth : defaultOptions.depth;
89388
+ this._wantsDir = type ? DIR_TYPES.has(type) : false;
89389
+ this._wantsFile = type ? FILE_TYPES.has(type) : false;
89390
+ this._wantsEverything = type === EntryTypes.EVERYTHING_TYPE;
89391
+ this._root = presolve(root);
89392
+ this._isDirent = !opts.alwaysStat;
89393
+ this._statsProp = this._isDirent ? "dirent" : "stats";
89394
+ this._rdOptions = { encoding: "utf8", withFileTypes: this._isDirent };
89395
+ this.parents = [this._exploreDir(root, 1)];
89396
+ this.reading = false;
89397
+ this.parent = undefined;
89398
+ }
89399
+ async _read(batch) {
89400
+ if (this.reading)
89401
+ return;
89402
+ this.reading = true;
89403
+ try {
89404
+ while (!this.destroyed && batch > 0) {
89405
+ const par2 = this.parent;
89406
+ const fil = par2 && par2.files;
89407
+ if (fil && fil.length > 0) {
89408
+ const { path: path5, depth } = par2;
89409
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path5));
89410
+ const awaited = await Promise.all(slice);
89411
+ for (const entry of awaited) {
89412
+ if (!entry)
89413
+ continue;
89414
+ if (this.destroyed)
89415
+ return;
89416
+ const entryType = await this._getEntryType(entry);
89417
+ if (entryType === "directory" && this._directoryFilter(entry)) {
89418
+ if (depth <= this._maxDepth) {
89419
+ this.parents.push(this._exploreDir(entry.fullPath, depth + 1));
89420
+ }
89421
+ if (this._wantsDir) {
89422
+ this.push(entry);
89423
+ batch--;
89424
+ }
89425
+ } else if ((entryType === "file" || this._includeAsFile(entry)) && this._fileFilter(entry)) {
89426
+ if (this._wantsFile) {
89427
+ this.push(entry);
89428
+ batch--;
89429
+ }
89430
+ }
89431
+ }
89432
+ } else {
89433
+ const parent = this.parents.pop();
89434
+ if (!parent) {
89435
+ this.push(null);
89436
+ break;
89437
+ }
89438
+ this.parent = await parent;
89439
+ if (this.destroyed)
89440
+ return;
89441
+ }
89442
+ }
89443
+ } catch (error) {
89444
+ this.destroy(error);
89445
+ } finally {
89446
+ this.reading = false;
89447
+ }
89448
+ }
89449
+ async _exploreDir(path5, depth) {
89450
+ let files;
89451
+ try {
89452
+ files = await readdir(path5, this._rdOptions);
89453
+ } catch (error) {
89454
+ this._onError(error);
89455
+ }
89456
+ return { files, depth, path: path5 };
89457
+ }
89458
+ async _formatEntry(dirent, path5) {
89459
+ let entry;
89460
+ const basename2 = this._isDirent ? dirent.name : dirent;
89461
+ try {
89462
+ const fullPath = presolve(pjoin(path5, basename2));
89463
+ entry = { path: prelative(this._root, fullPath), fullPath, basename: basename2 };
89464
+ entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
89465
+ } catch (err) {
89466
+ this._onError(err);
89467
+ return;
89468
+ }
89469
+ return entry;
89470
+ }
89471
+ _onError(err) {
89472
+ if (isNormalFlowError(err) && !this.destroyed) {
89473
+ this.emit("warn", err);
89474
+ } else {
89475
+ this.destroy(err);
89476
+ }
89477
+ }
89478
+ async _getEntryType(entry) {
89479
+ if (!entry && this._statsProp in entry) {
89480
+ return "";
89481
+ }
89482
+ const stats = entry[this._statsProp];
89483
+ if (stats.isFile())
89484
+ return "file";
89485
+ if (stats.isDirectory())
89486
+ return "directory";
89487
+ if (stats && stats.isSymbolicLink()) {
89488
+ const full = entry.fullPath;
89489
+ try {
89490
+ const entryRealPath = await realpath3(full);
89491
+ const entryRealPathStats = await lstat(entryRealPath);
89492
+ if (entryRealPathStats.isFile()) {
89493
+ return "file";
89494
+ }
89495
+ if (entryRealPathStats.isDirectory()) {
89496
+ const len2 = entryRealPath.length;
89497
+ if (full.startsWith(entryRealPath) && full.substr(len2, 1) === psep) {
89498
+ const recursiveError = new Error(`Circular symlink detected: "${full}" points to "${entryRealPath}"`);
89499
+ recursiveError.code = RECURSIVE_ERROR_CODE;
89500
+ return this._onError(recursiveError);
89501
+ }
89502
+ return "directory";
89503
+ }
89504
+ } catch (error) {
89505
+ this._onError(error);
89506
+ return "";
89507
+ }
89508
+ }
89509
+ }
89510
+ _includeAsFile(entry) {
89511
+ const stats = entry && entry[this._statsProp];
89512
+ return stats && this._wantsEverything && !stats.isDirectory();
89513
+ }
89514
+ };
89515
+ });
89516
+
89517
+ // ../../node_modules/.pnpm/chokidar@5.0.0/node_modules/chokidar/handler.js
89518
+ import { watch as fs_watch, unwatchFile, watchFile } from "node:fs";
89519
+ import { realpath as fsrealpath, lstat as lstat2, open, stat as stat3 } from "node:fs/promises";
89520
+ import { type as osType } from "node:os";
89521
+ import * as sp from "node:path";
89522
+ function createFsWatchInstance(path5, options, listener, errHandler, emitRaw) {
89523
+ const handleEvent = (rawEvent, evPath) => {
89524
+ listener(path5);
89525
+ emitRaw(rawEvent, evPath, { watchedPath: path5 });
89526
+ if (evPath && path5 !== evPath) {
89527
+ fsWatchBroadcast(sp.resolve(path5, evPath), KEY_LISTENERS, sp.join(path5, evPath));
89528
+ }
89529
+ };
89530
+ try {
89531
+ return fs_watch(path5, {
89532
+ persistent: options.persistent
89533
+ }, handleEvent);
89534
+ } catch (error) {
89535
+ errHandler(error);
89536
+ return;
89537
+ }
89538
+ }
89539
+
89540
+ class NodeFsHandler {
89541
+ fsw;
89542
+ _boundHandleError;
89543
+ constructor(fsW) {
89544
+ this.fsw = fsW;
89545
+ this._boundHandleError = (error) => fsW._handleError(error);
89546
+ }
89547
+ _watchWithNodeFs(path5, listener) {
89548
+ const opts = this.fsw.options;
89549
+ const directory = sp.dirname(path5);
89550
+ const basename3 = sp.basename(path5);
89551
+ const parent = this.fsw._getWatchedDir(directory);
89552
+ parent.add(basename3);
89553
+ const absolutePath = sp.resolve(path5);
89554
+ const options = {
89555
+ persistent: opts.persistent
89556
+ };
89557
+ if (!listener)
89558
+ listener = EMPTY_FN;
89559
+ let closer;
89560
+ if (opts.usePolling) {
89561
+ const enableBin = opts.interval !== opts.binaryInterval;
89562
+ options.interval = enableBin && isBinaryPath(basename3) ? opts.binaryInterval : opts.interval;
89563
+ closer = setFsWatchFileListener(path5, absolutePath, options, {
89564
+ listener,
89565
+ rawEmitter: this.fsw._emitRaw
89566
+ });
89567
+ } else {
89568
+ closer = setFsWatchListener(path5, absolutePath, options, {
89569
+ listener,
89570
+ errHandler: this._boundHandleError,
89571
+ rawEmitter: this.fsw._emitRaw
89572
+ });
89573
+ }
89574
+ return closer;
89575
+ }
89576
+ _handleFile(file, stats, initialAdd) {
89577
+ if (this.fsw.closed) {
88962
89578
  return;
88963
- console.log(`[${formatTimestamp()}] \uD83D\uDCE5 Received ${requests.length} pending file content request(s): ${requests.map((r) => r.filePath).join(", ")}`);
88964
- for (const request2 of requests) {
88965
- const startTime = Date.now();
88966
- const { workingDir, filePath } = request2;
88967
- const registered = yield* exports_Effect.catchAll(exports_Effect.tryPromise(() => assertRegisteredWorkingDir(session2, workingDir)), () => exports_Effect.succeed({ ok: false, error: "Workspace check failed" }));
88968
- if (!registered.ok) {
88969
- console.warn(`[${formatTimestamp()}] ⚠️ Rejected unregistered workspace: ${workingDir} (${registered.error})`);
88970
- yield* fulfillGzippedContentEffect(session2, workingDir, filePath, "[Error: workspace not registered]", false);
88971
- continue;
89579
+ }
89580
+ const dirname10 = sp.dirname(file);
89581
+ const basename3 = sp.basename(file);
89582
+ const parent = this.fsw._getWatchedDir(dirname10);
89583
+ let prevStats = stats;
89584
+ if (parent.has(basename3))
89585
+ return;
89586
+ const listener = async (path5, newStats) => {
89587
+ if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
89588
+ return;
89589
+ if (!newStats || newStats.mtimeMs === 0) {
89590
+ try {
89591
+ const newStats2 = await stat3(file);
89592
+ if (this.fsw.closed)
89593
+ return;
89594
+ const at = newStats2.atimeMs;
89595
+ const mt = newStats2.mtimeMs;
89596
+ if (!at || at <= mt || mt !== prevStats.mtimeMs) {
89597
+ this.fsw._emit(EV.CHANGE, file, newStats2);
89598
+ }
89599
+ if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
89600
+ this.fsw._closeFile(path5);
89601
+ prevStats = newStats2;
89602
+ const closer2 = this._watchWithNodeFs(file, listener);
89603
+ if (closer2)
89604
+ this.fsw._addPathCloser(path5, closer2);
89605
+ } else {
89606
+ prevStats = newStats2;
89607
+ }
89608
+ } catch (error) {
89609
+ this.fsw._remove(dirname10, basename3);
89610
+ }
89611
+ } else if (parent.has(basename3)) {
89612
+ const at = newStats.atimeMs;
89613
+ const mt = newStats.mtimeMs;
89614
+ if (!at || at <= mt || mt !== prevStats.mtimeMs) {
89615
+ this.fsw._emit(EV.CHANGE, file, newStats);
89616
+ }
89617
+ prevStats = newStats;
88972
89618
  }
88973
- const resolved = yield* exports_Effect.catchAll(exports_Effect.tryPromise(() => resolvePathWithinWorkspace(workingDir, filePath)), () => exports_Effect.succeed({ ok: false, error: "Invalid file path" }));
88974
- if (!resolved.ok) {
88975
- console.warn(`[${formatTimestamp()}] ⚠️ Rejected file path: ${filePath} (${resolved.error})`);
88976
- yield* fulfillGzippedContentEffect(session2, workingDir, filePath, "[Error reading file]", false);
88977
- continue;
89619
+ };
89620
+ const closer = this._watchWithNodeFs(file, listener);
89621
+ if (!(initialAdd && this.fsw.options.ignoreInitial) && this.fsw._isntIgnored(file)) {
89622
+ if (!this.fsw._throttle(EV.ADD, file, 0))
89623
+ return;
89624
+ this.fsw._emit(EV.ADD, file, stats);
89625
+ }
89626
+ return closer;
89627
+ }
89628
+ async _handleSymlink(entry, directory, path5, item) {
89629
+ if (this.fsw.closed) {
89630
+ return;
89631
+ }
89632
+ const full = entry.fullPath;
89633
+ const dir = this.fsw._getWatchedDir(directory);
89634
+ if (!this.fsw.options.followSymlinks) {
89635
+ this.fsw._incrReadyCount();
89636
+ let linkPath;
89637
+ try {
89638
+ linkPath = await fsrealpath(path5);
89639
+ } catch (e) {
89640
+ this.fsw._emitReady();
89641
+ return true;
88978
89642
  }
88979
- const absolutePath = resolved.absolutePath;
88980
- if (isBinaryFile(filePath)) {
88981
- yield* fulfillGzippedContentEffect(session2, workingDir, filePath, "[Binary file]", false);
88982
- const elapsed4 = Date.now() - startTime;
88983
- console.log(`[${formatTimestamp()}] \uD83D\uDCC4 File content synced to Convex: ${filePath} [binary] (${elapsed4}ms)`);
88984
- continue;
89643
+ if (this.fsw.closed)
89644
+ return;
89645
+ if (dir.has(item)) {
89646
+ if (this.fsw._symlinkPaths.get(full) !== linkPath) {
89647
+ this.fsw._symlinkPaths.set(full, linkPath);
89648
+ this.fsw._emit(EV.CHANGE, path5, entry.stats);
89649
+ }
89650
+ } else {
89651
+ dir.add(item);
89652
+ this.fsw._symlinkPaths.set(full, linkPath);
89653
+ this.fsw._emit(EV.ADD, path5, entry.stats);
88985
89654
  }
88986
- if (!isPathContentReadable(filePath)) {
88987
- yield* fulfillGzippedContentEffect(session2, workingDir, filePath, "[File blocked: cannot open sensitive path in remote explorer]", false);
88988
- const elapsed4 = Date.now() - startTime;
88989
- console.log(`[${formatTimestamp()}] \uD83D\uDCC4 File content blocked: ${filePath} [secret] (${elapsed4}ms)`);
88990
- continue;
89655
+ this.fsw._emitReady();
89656
+ return true;
89657
+ }
89658
+ if (this.fsw._symlinkPaths.has(full)) {
89659
+ return true;
89660
+ }
89661
+ this.fsw._symlinkPaths.set(full, true);
89662
+ }
89663
+ _handleRead(directory, initialAdd, wh, target, dir, depth, throttler) {
89664
+ directory = sp.join(directory, "");
89665
+ const throttleKey = target ? `${directory}:${target}` : directory;
89666
+ throttler = this.fsw._throttle("readdir", throttleKey, 1000);
89667
+ if (!throttler)
89668
+ return;
89669
+ const previous = this.fsw._getWatchedDir(wh.path);
89670
+ const current = new Set;
89671
+ let stream4 = this.fsw._readdirp(directory, {
89672
+ fileFilter: (entry) => wh.filterPath(entry),
89673
+ directoryFilter: (entry) => wh.filterDir(entry)
89674
+ });
89675
+ if (!stream4)
89676
+ return;
89677
+ stream4.on(STR_DATA, async (entry) => {
89678
+ if (this.fsw.closed) {
89679
+ stream4 = undefined;
89680
+ return;
88991
89681
  }
88992
- const readOutcome = yield* exports_Effect.catchAll(exports_Effect.tryPromise(() => readFile8(absolutePath)).pipe(exports_Effect.map((buffer) => {
88993
- if (buffer.length > MAX_CONTENT_BYTES) {
88994
- return {
88995
- kind: "ok",
88996
- content: buffer.subarray(0, MAX_CONTENT_BYTES).toString("utf8"),
88997
- truncated: true
88998
- };
89682
+ const item = entry.path;
89683
+ let path5 = sp.join(directory, item);
89684
+ current.add(item);
89685
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path5, item)) {
89686
+ return;
89687
+ }
89688
+ if (this.fsw.closed) {
89689
+ stream4 = undefined;
89690
+ return;
89691
+ }
89692
+ if (item === target || !target && !previous.has(item)) {
89693
+ this.fsw._incrReadyCount();
89694
+ path5 = sp.join(dir, sp.relative(dir, path5));
89695
+ this._addToNodeFs(path5, initialAdd, wh, depth + 1);
89696
+ }
89697
+ }).on(EV.ERROR, this._boundHandleError);
89698
+ return new Promise((resolve6, reject) => {
89699
+ if (!stream4)
89700
+ return reject();
89701
+ stream4.once(STR_END, () => {
89702
+ if (this.fsw.closed) {
89703
+ stream4 = undefined;
89704
+ return;
88999
89705
  }
89000
- return {
89001
- kind: "ok",
89002
- content: buffer.toString("utf8"),
89003
- truncated: false
89004
- };
89005
- })), (error) => isENOENT(error) ? exports_Effect.succeed({ kind: "missing" }) : exports_Effect.succeed({
89006
- kind: "error",
89007
- content: "[Error reading file]",
89008
- truncated: false
89009
- }));
89010
- if (readOutcome.kind === "missing") {
89011
- console.log(`[${formatTimestamp()}] ⏳ File not on disk yet, deferring content sync: ${filePath}`);
89012
- continue;
89706
+ const wasThrottled = throttler ? throttler.clear() : false;
89707
+ resolve6(undefined);
89708
+ previous.getChildren().filter((item) => {
89709
+ return item !== directory && !current.has(item);
89710
+ }).forEach((item) => {
89711
+ this.fsw._remove(directory, item);
89712
+ });
89713
+ stream4 = undefined;
89714
+ if (wasThrottled)
89715
+ this._handleRead(directory, false, wh, target, dir, depth, throttler);
89716
+ });
89717
+ });
89718
+ }
89719
+ async _handleDir(dir, stats, initialAdd, depth, target, wh, realpath4) {
89720
+ const parentDir = this.fsw._getWatchedDir(sp.dirname(dir));
89721
+ const tracked = parentDir.has(sp.basename(dir));
89722
+ if (!(initialAdd && this.fsw.options.ignoreInitial) && !target && !tracked) {
89723
+ this.fsw._emit(EV.ADD_DIR, dir, stats);
89724
+ }
89725
+ parentDir.add(sp.basename(dir));
89726
+ this.fsw._getWatchedDir(dir);
89727
+ let throttler;
89728
+ let closer;
89729
+ const oDepth = this.fsw.options.depth;
89730
+ if ((oDepth == null || depth <= oDepth) && !this.fsw._symlinkPaths.has(realpath4)) {
89731
+ if (!target) {
89732
+ await this._handleRead(dir, initialAdd, wh, target, dir, depth, throttler);
89733
+ if (this.fsw.closed)
89734
+ return;
89735
+ }
89736
+ closer = this._watchWithNodeFs(dir, (dirPath, stats2) => {
89737
+ if (stats2 && stats2.mtimeMs === 0)
89738
+ return;
89739
+ this._handleRead(dirPath, false, wh, target, dir, depth, throttler);
89740
+ });
89741
+ }
89742
+ return closer;
89743
+ }
89744
+ async _addToNodeFs(path5, initialAdd, priorWh, depth, target) {
89745
+ const ready = this.fsw._emitReady;
89746
+ if (this.fsw._isIgnored(path5) || this.fsw.closed) {
89747
+ ready();
89748
+ return false;
89749
+ }
89750
+ const wh = this.fsw._getWatchHelpers(path5);
89751
+ if (priorWh) {
89752
+ wh.filterPath = (entry) => priorWh.filterPath(entry);
89753
+ wh.filterDir = (entry) => priorWh.filterDir(entry);
89754
+ }
89755
+ try {
89756
+ const stats = await statMethods[wh.statMethod](wh.watchPath);
89757
+ if (this.fsw.closed)
89758
+ return;
89759
+ if (this.fsw._isIgnored(wh.watchPath, stats)) {
89760
+ ready();
89761
+ return false;
89762
+ }
89763
+ const follow = this.fsw.options.followSymlinks;
89764
+ let closer;
89765
+ if (stats.isDirectory()) {
89766
+ const absPath = sp.resolve(path5);
89767
+ const targetPath = follow ? await fsrealpath(path5) : path5;
89768
+ if (this.fsw.closed)
89769
+ return;
89770
+ closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
89771
+ if (this.fsw.closed)
89772
+ return;
89773
+ if (absPath !== targetPath && targetPath !== undefined) {
89774
+ this.fsw._symlinkPaths.set(absPath, targetPath);
89775
+ }
89776
+ } else if (stats.isSymbolicLink()) {
89777
+ const targetPath = follow ? await fsrealpath(path5) : path5;
89778
+ if (this.fsw.closed)
89779
+ return;
89780
+ const parent = sp.dirname(wh.watchPath);
89781
+ this.fsw._getWatchedDir(parent).add(wh.watchPath);
89782
+ this.fsw._emit(EV.ADD, wh.watchPath, stats);
89783
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path5, wh, targetPath);
89784
+ if (this.fsw.closed)
89785
+ return;
89786
+ if (targetPath !== undefined) {
89787
+ this.fsw._symlinkPaths.set(sp.resolve(path5), targetPath);
89788
+ }
89789
+ } else {
89790
+ closer = this._handleFile(wh.watchPath, stats, initialAdd);
89791
+ }
89792
+ ready();
89793
+ if (closer)
89794
+ this.fsw._addPathCloser(path5, closer);
89795
+ return false;
89796
+ } catch (error) {
89797
+ if (this.fsw._handleError(error)) {
89798
+ ready();
89799
+ return path5;
89800
+ }
89801
+ }
89802
+ }
89803
+ }
89804
+ var STR_DATA = "data", STR_END = "end", STR_CLOSE = "close", EMPTY_FN = () => {}, pl, isWindows, isMacos, isLinux, isFreeBSD, isIBMi, EVENTS, EV, THROTTLE_MODE_WATCH = "watch", statMethods, KEY_LISTENERS = "listeners", KEY_ERR = "errHandlers", KEY_RAW = "rawEmitters", HANDLER_KEYS, binaryExtensions, isBinaryPath = (filePath) => binaryExtensions.has(sp.extname(filePath).slice(1).toLowerCase()), foreach = (val, fn2) => {
89805
+ if (val instanceof Set) {
89806
+ val.forEach(fn2);
89807
+ } else {
89808
+ fn2(val);
89809
+ }
89810
+ }, addAndConvert = (main, prop, item) => {
89811
+ let container = main[prop];
89812
+ if (!(container instanceof Set)) {
89813
+ main[prop] = container = new Set([container]);
89814
+ }
89815
+ container.add(item);
89816
+ }, clearItem = (cont) => (key) => {
89817
+ const set7 = cont[key];
89818
+ if (set7 instanceof Set) {
89819
+ set7.clear();
89820
+ } else {
89821
+ delete cont[key];
89822
+ }
89823
+ }, delFromSet = (main, prop, item) => {
89824
+ const container = main[prop];
89825
+ if (container instanceof Set) {
89826
+ container.delete(item);
89827
+ } else if (container === item) {
89828
+ delete main[prop];
89829
+ }
89830
+ }, isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val, FsWatchInstances, fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
89831
+ const cont = FsWatchInstances.get(fullPath);
89832
+ if (!cont)
89833
+ return;
89834
+ foreach(cont[listenerType], (listener) => {
89835
+ listener(val1, val2, val3);
89836
+ });
89837
+ }, setFsWatchListener = (path5, fullPath, options, handlers) => {
89838
+ const { listener, errHandler, rawEmitter } = handlers;
89839
+ let cont = FsWatchInstances.get(fullPath);
89840
+ let watcher;
89841
+ if (!options.persistent) {
89842
+ watcher = createFsWatchInstance(path5, options, listener, errHandler, rawEmitter);
89843
+ if (!watcher)
89844
+ return;
89845
+ return watcher.close.bind(watcher);
89846
+ }
89847
+ if (cont) {
89848
+ addAndConvert(cont, KEY_LISTENERS, listener);
89849
+ addAndConvert(cont, KEY_ERR, errHandler);
89850
+ addAndConvert(cont, KEY_RAW, rawEmitter);
89851
+ } else {
89852
+ watcher = createFsWatchInstance(path5, options, fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS), errHandler, fsWatchBroadcast.bind(null, fullPath, KEY_RAW));
89853
+ if (!watcher)
89854
+ return;
89855
+ watcher.on(EV.ERROR, async (error) => {
89856
+ const broadcastErr = fsWatchBroadcast.bind(null, fullPath, KEY_ERR);
89857
+ if (cont)
89858
+ cont.watcherUnusable = true;
89859
+ if (isWindows && error.code === "EPERM") {
89860
+ try {
89861
+ const fd = await open(path5, "r");
89862
+ await fd.close();
89863
+ broadcastErr(error);
89864
+ } catch (err) {}
89865
+ } else {
89866
+ broadcastErr(error);
89867
+ }
89868
+ });
89869
+ cont = {
89870
+ listeners: listener,
89871
+ errHandlers: errHandler,
89872
+ rawEmitters: rawEmitter,
89873
+ watcher
89874
+ };
89875
+ FsWatchInstances.set(fullPath, cont);
89876
+ }
89877
+ return () => {
89878
+ delFromSet(cont, KEY_LISTENERS, listener);
89879
+ delFromSet(cont, KEY_ERR, errHandler);
89880
+ delFromSet(cont, KEY_RAW, rawEmitter);
89881
+ if (isEmptySet(cont.listeners)) {
89882
+ cont.watcher.close();
89883
+ FsWatchInstances.delete(fullPath);
89884
+ HANDLER_KEYS.forEach(clearItem(cont));
89885
+ cont.watcher = undefined;
89886
+ Object.freeze(cont);
89887
+ }
89888
+ };
89889
+ }, FsWatchFileInstances, setFsWatchFileListener = (path5, fullPath, options, handlers) => {
89890
+ const { listener, rawEmitter } = handlers;
89891
+ let cont = FsWatchFileInstances.get(fullPath);
89892
+ const copts = cont && cont.options;
89893
+ if (copts && (copts.persistent < options.persistent || copts.interval > options.interval)) {
89894
+ unwatchFile(fullPath);
89895
+ cont = undefined;
89896
+ }
89897
+ if (cont) {
89898
+ addAndConvert(cont, KEY_LISTENERS, listener);
89899
+ addAndConvert(cont, KEY_RAW, rawEmitter);
89900
+ } else {
89901
+ cont = {
89902
+ listeners: listener,
89903
+ rawEmitters: rawEmitter,
89904
+ options,
89905
+ watcher: watchFile(fullPath, options, (curr, prev) => {
89906
+ foreach(cont.rawEmitters, (rawEmitter2) => {
89907
+ rawEmitter2(EV.CHANGE, fullPath, { curr, prev });
89908
+ });
89909
+ const currmtime = curr.mtimeMs;
89910
+ if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
89911
+ foreach(cont.listeners, (listener2) => listener2(path5, curr));
89912
+ }
89913
+ })
89914
+ };
89915
+ FsWatchFileInstances.set(fullPath, cont);
89916
+ }
89917
+ return () => {
89918
+ delFromSet(cont, KEY_LISTENERS, listener);
89919
+ delFromSet(cont, KEY_RAW, rawEmitter);
89920
+ if (isEmptySet(cont.listeners)) {
89921
+ FsWatchFileInstances.delete(fullPath);
89922
+ unwatchFile(fullPath);
89923
+ cont.options = cont.watcher = undefined;
89924
+ Object.freeze(cont);
89925
+ }
89926
+ };
89927
+ };
89928
+ var init_handler = __esm(() => {
89929
+ pl = process.platform;
89930
+ isWindows = pl === "win32";
89931
+ isMacos = pl === "darwin";
89932
+ isLinux = pl === "linux";
89933
+ isFreeBSD = pl === "freebsd";
89934
+ isIBMi = osType() === "OS400";
89935
+ EVENTS = {
89936
+ ALL: "all",
89937
+ READY: "ready",
89938
+ ADD: "add",
89939
+ CHANGE: "change",
89940
+ ADD_DIR: "addDir",
89941
+ UNLINK: "unlink",
89942
+ UNLINK_DIR: "unlinkDir",
89943
+ RAW: "raw",
89944
+ ERROR: "error"
89945
+ };
89946
+ EV = EVENTS;
89947
+ statMethods = { lstat: lstat2, stat: stat3 };
89948
+ HANDLER_KEYS = [KEY_LISTENERS, KEY_ERR, KEY_RAW];
89949
+ binaryExtensions = new Set([
89950
+ "3dm",
89951
+ "3ds",
89952
+ "3g2",
89953
+ "3gp",
89954
+ "7z",
89955
+ "a",
89956
+ "aac",
89957
+ "adp",
89958
+ "afdesign",
89959
+ "afphoto",
89960
+ "afpub",
89961
+ "ai",
89962
+ "aif",
89963
+ "aiff",
89964
+ "alz",
89965
+ "ape",
89966
+ "apk",
89967
+ "appimage",
89968
+ "ar",
89969
+ "arj",
89970
+ "asf",
89971
+ "au",
89972
+ "avi",
89973
+ "bak",
89974
+ "baml",
89975
+ "bh",
89976
+ "bin",
89977
+ "bk",
89978
+ "bmp",
89979
+ "btif",
89980
+ "bz2",
89981
+ "bzip2",
89982
+ "cab",
89983
+ "caf",
89984
+ "cgm",
89985
+ "class",
89986
+ "cmx",
89987
+ "cpio",
89988
+ "cr2",
89989
+ "cur",
89990
+ "dat",
89991
+ "dcm",
89992
+ "deb",
89993
+ "dex",
89994
+ "djvu",
89995
+ "dll",
89996
+ "dmg",
89997
+ "dng",
89998
+ "doc",
89999
+ "docm",
90000
+ "docx",
90001
+ "dot",
90002
+ "dotm",
90003
+ "dra",
90004
+ "DS_Store",
90005
+ "dsk",
90006
+ "dts",
90007
+ "dtshd",
90008
+ "dvb",
90009
+ "dwg",
90010
+ "dxf",
90011
+ "ecelp4800",
90012
+ "ecelp7470",
90013
+ "ecelp9600",
90014
+ "egg",
90015
+ "eol",
90016
+ "eot",
90017
+ "epub",
90018
+ "exe",
90019
+ "f4v",
90020
+ "fbs",
90021
+ "fh",
90022
+ "fla",
90023
+ "flac",
90024
+ "flatpak",
90025
+ "fli",
90026
+ "flv",
90027
+ "fpx",
90028
+ "fst",
90029
+ "fvt",
90030
+ "g3",
90031
+ "gh",
90032
+ "gif",
90033
+ "graffle",
90034
+ "gz",
90035
+ "gzip",
90036
+ "h261",
90037
+ "h263",
90038
+ "h264",
90039
+ "icns",
90040
+ "ico",
90041
+ "ief",
90042
+ "img",
90043
+ "ipa",
90044
+ "iso",
90045
+ "jar",
90046
+ "jpeg",
90047
+ "jpg",
90048
+ "jpgv",
90049
+ "jpm",
90050
+ "jxr",
90051
+ "key",
90052
+ "ktx",
90053
+ "lha",
90054
+ "lib",
90055
+ "lvp",
90056
+ "lz",
90057
+ "lzh",
90058
+ "lzma",
90059
+ "lzo",
90060
+ "m3u",
90061
+ "m4a",
90062
+ "m4v",
90063
+ "mar",
90064
+ "mdi",
90065
+ "mht",
90066
+ "mid",
90067
+ "midi",
90068
+ "mj2",
90069
+ "mka",
90070
+ "mkv",
90071
+ "mmr",
90072
+ "mng",
90073
+ "mobi",
90074
+ "mov",
90075
+ "movie",
90076
+ "mp3",
90077
+ "mp4",
90078
+ "mp4a",
90079
+ "mpeg",
90080
+ "mpg",
90081
+ "mpga",
90082
+ "mxu",
90083
+ "nef",
90084
+ "npx",
90085
+ "numbers",
90086
+ "nupkg",
90087
+ "o",
90088
+ "odp",
90089
+ "ods",
90090
+ "odt",
90091
+ "oga",
90092
+ "ogg",
90093
+ "ogv",
90094
+ "otf",
90095
+ "ott",
90096
+ "pages",
90097
+ "pbm",
90098
+ "pcx",
90099
+ "pdb",
90100
+ "pdf",
90101
+ "pea",
90102
+ "pgm",
90103
+ "pic",
90104
+ "png",
90105
+ "pnm",
90106
+ "pot",
90107
+ "potm",
90108
+ "potx",
90109
+ "ppa",
90110
+ "ppam",
90111
+ "ppm",
90112
+ "pps",
90113
+ "ppsm",
90114
+ "ppsx",
90115
+ "ppt",
90116
+ "pptm",
90117
+ "pptx",
90118
+ "psd",
90119
+ "pya",
90120
+ "pyc",
90121
+ "pyo",
90122
+ "pyv",
90123
+ "qt",
90124
+ "rar",
90125
+ "ras",
90126
+ "raw",
90127
+ "resources",
90128
+ "rgb",
90129
+ "rip",
90130
+ "rlc",
90131
+ "rmf",
90132
+ "rmvb",
90133
+ "rpm",
90134
+ "rtf",
90135
+ "rz",
90136
+ "s3m",
90137
+ "s7z",
90138
+ "scpt",
90139
+ "sgi",
90140
+ "shar",
90141
+ "snap",
90142
+ "sil",
90143
+ "sketch",
90144
+ "slk",
90145
+ "smv",
90146
+ "snk",
90147
+ "so",
90148
+ "stl",
90149
+ "suo",
90150
+ "sub",
90151
+ "swf",
90152
+ "tar",
90153
+ "tbz",
90154
+ "tbz2",
90155
+ "tga",
90156
+ "tgz",
90157
+ "thmx",
90158
+ "tif",
90159
+ "tiff",
90160
+ "tlz",
90161
+ "ttc",
90162
+ "ttf",
90163
+ "txz",
90164
+ "udf",
90165
+ "uvh",
90166
+ "uvi",
90167
+ "uvm",
90168
+ "uvp",
90169
+ "uvs",
90170
+ "uvu",
90171
+ "viv",
90172
+ "vob",
90173
+ "war",
90174
+ "wav",
90175
+ "wax",
90176
+ "wbmp",
90177
+ "wdp",
90178
+ "weba",
90179
+ "webm",
90180
+ "webp",
90181
+ "whl",
90182
+ "wim",
90183
+ "wm",
90184
+ "wma",
90185
+ "wmv",
90186
+ "wmx",
90187
+ "woff",
90188
+ "woff2",
90189
+ "wrm",
90190
+ "wvx",
90191
+ "xbm",
90192
+ "xif",
90193
+ "xla",
90194
+ "xlam",
90195
+ "xls",
90196
+ "xlsb",
90197
+ "xlsm",
90198
+ "xlsx",
90199
+ "xlt",
90200
+ "xltm",
90201
+ "xltx",
90202
+ "xm",
90203
+ "xmind",
90204
+ "xpi",
90205
+ "xpm",
90206
+ "xwd",
90207
+ "xz",
90208
+ "z",
90209
+ "zip",
90210
+ "zipx"
90211
+ ]);
90212
+ FsWatchInstances = new Map;
90213
+ FsWatchFileInstances = new Map;
90214
+ });
90215
+
90216
+ // ../../node_modules/.pnpm/chokidar@5.0.0/node_modules/chokidar/index.js
90217
+ import { EventEmitter as EventEmitter2 } from "node:events";
90218
+ import { stat as statcb, Stats } from "node:fs";
90219
+ import { readdir as readdir2, stat as stat4 } from "node:fs/promises";
90220
+ import * as sp2 from "node:path";
90221
+ function arrify(item) {
90222
+ return Array.isArray(item) ? item : [item];
90223
+ }
90224
+ function createPattern(matcher) {
90225
+ if (typeof matcher === "function")
90226
+ return matcher;
90227
+ if (typeof matcher === "string")
90228
+ return (string2) => matcher === string2;
90229
+ if (matcher instanceof RegExp)
90230
+ return (string2) => matcher.test(string2);
90231
+ if (typeof matcher === "object" && matcher !== null) {
90232
+ return (string2) => {
90233
+ if (matcher.path === string2)
90234
+ return true;
90235
+ if (matcher.recursive) {
90236
+ const relative4 = sp2.relative(matcher.path, string2);
90237
+ if (!relative4) {
90238
+ return false;
90239
+ }
90240
+ return !relative4.startsWith("..") && !sp2.isAbsolute(relative4);
89013
90241
  }
89014
- const { content, truncated } = readOutcome.kind === "ok" ? readOutcome : { content: readOutcome.content, truncated: readOutcome.truncated };
89015
- const compressed = gzipSync2(Buffer.from(content));
89016
- const contentCompressed = compressed.toString("base64");
89017
- yield* exports_Effect.catchAll(exports_Effect.tryPromise(() => session2.backend.mutation(api.workspaceFiles.fulfillFileContentV2, {
89018
- sessionId: session2.sessionId,
89019
- machineId: session2.machineId,
89020
- workingDir,
89021
- filePath,
89022
- data: { compression: "gzip", content: contentCompressed },
89023
- encoding: "utf8",
89024
- truncated
89025
- })), () => exports_Effect.void);
89026
- const elapsed3 = Date.now() - startTime;
89027
- console.log(`[${formatTimestamp()}] \uD83D\uDCC4 File content synced to Convex: ${filePath} (${(Buffer.byteLength(content) / 1024).toFixed(1)}KB → ${(compressed.length / 1024).toFixed(1)}KB gzip, ${elapsed3}ms)`);
90242
+ return false;
90243
+ };
90244
+ }
90245
+ return () => false;
90246
+ }
90247
+ function normalizePath(path5) {
90248
+ if (typeof path5 !== "string")
90249
+ throw new Error("string expected");
90250
+ path5 = sp2.normalize(path5);
90251
+ path5 = path5.replace(/\\/g, "/");
90252
+ let prepend4 = false;
90253
+ if (path5.startsWith("//"))
90254
+ prepend4 = true;
90255
+ path5 = path5.replace(DOUBLE_SLASH_RE, "/");
90256
+ if (prepend4)
90257
+ path5 = "/" + path5;
90258
+ return path5;
90259
+ }
90260
+ function matchPatterns(patterns, testString, stats) {
90261
+ const path5 = normalizePath(testString);
90262
+ for (let index = 0;index < patterns.length; index++) {
90263
+ const pattern2 = patterns[index];
90264
+ if (pattern2(path5, stats)) {
90265
+ return true;
89028
90266
  }
89029
- });
89030
- });
90267
+ }
90268
+ return false;
90269
+ }
90270
+ function anymatch(matchers, testString) {
90271
+ if (matchers == null) {
90272
+ throw new TypeError("anymatch: specify first argument");
90273
+ }
90274
+ const matchersArray = arrify(matchers);
90275
+ const patterns = matchersArray.map((matcher) => createPattern(matcher));
90276
+ if (testString == null) {
90277
+ return (testString2, stats) => {
90278
+ return matchPatterns(patterns, testString2, stats);
90279
+ };
90280
+ }
90281
+ return matchPatterns(patterns, testString);
90282
+ }
89031
90283
 
89032
- // src/commands/machine/daemon-start/file-content-subscription.ts
89033
- var startFileContentSubscriptionEffect = (wsClient2) => exports_Effect.gen(function* () {
89034
- const session2 = yield* DaemonSessionService;
89035
- let processing = false;
89036
- const unsubscribe = wsClient2.onUpdate(api.workspaceFiles.getPendingFileContentRequests, {
89037
- sessionId: session2.sessionId,
89038
- machineId: session2.machineId
89039
- }, (requests) => {
89040
- if (!requests || requests.length === 0)
90284
+ class DirEntry {
90285
+ path;
90286
+ _removeWatcher;
90287
+ items;
90288
+ constructor(dir, removeWatcher) {
90289
+ this.path = dir;
90290
+ this._removeWatcher = removeWatcher;
90291
+ this.items = new Set;
90292
+ }
90293
+ add(item) {
90294
+ const { items } = this;
90295
+ if (!items)
89041
90296
  return;
89042
- if (processing)
90297
+ if (item !== ONE_DOT && item !== TWO_DOTS)
90298
+ items.add(item);
90299
+ }
90300
+ async remove(item) {
90301
+ const { items } = this;
90302
+ if (!items)
89043
90303
  return;
89044
- processing = true;
89045
- exports_Effect.runPromise(fulfillFileContentRequestsEffect.pipe(exports_Effect.provideService(DaemonSessionService, session2))).catch((err) => {
89046
- console.warn(`[${formatTimestamp()}] ⚠️ File content subscription processing failed: ${getErrorMessage2(err)}`);
89047
- }).finally(() => {
89048
- processing = false;
89049
- });
89050
- }, (err) => {
89051
- console.warn(`[${formatTimestamp()}] ⚠️ File content subscription error: ${getErrorMessage2(err)}`);
89052
- });
89053
- console.log(`[${formatTimestamp()}] \uD83D\uDCC2 File content subscription started (reactive)`);
89054
- return {
89055
- stop: () => {
89056
- unsubscribe();
89057
- console.log(`[${formatTimestamp()}] \uD83D\uDCC2 File content subscription stopped`);
90304
+ items.delete(item);
90305
+ if (items.size > 0)
90306
+ return;
90307
+ const dir = this.path;
90308
+ try {
90309
+ await readdir2(dir);
90310
+ } catch (err) {
90311
+ if (this._removeWatcher) {
90312
+ this._removeWatcher(sp2.dirname(dir), sp2.basename(dir));
90313
+ }
89058
90314
  }
89059
- };
89060
- });
89061
- var init_file_content_subscription = __esm(() => {
89062
- init_esm();
89063
- init_daemon_services();
89064
- init_file_content_fulfillment();
89065
- init_api3();
89066
- init_convex_error();
89067
- });
89068
-
89069
- // src/infrastructure/services/workspace/file-tree-data-hash.ts
89070
- import { createHash as createHash4 } from "node:crypto";
89071
- function computeFileTreeDataHash(tree) {
89072
- return createHash4("md5").update(JSON.stringify(tree)).digest("hex");
90315
+ }
90316
+ has(item) {
90317
+ const { items } = this;
90318
+ if (!items)
90319
+ return;
90320
+ return items.has(item);
90321
+ }
90322
+ getChildren() {
90323
+ const { items } = this;
90324
+ if (!items)
90325
+ return [];
90326
+ return [...items.values()];
90327
+ }
90328
+ dispose() {
90329
+ this.items.clear();
90330
+ this.path = "";
90331
+ this._removeWatcher = EMPTY_FN;
90332
+ this.items = EMPTY_SET;
90333
+ Object.freeze(this);
90334
+ }
89073
90335
  }
89074
- var init_file_tree_data_hash = () => {};
89075
90336
 
89076
- // src/infrastructure/services/workspace/file-tree-partition.ts
89077
- import { createHash as createHash5 } from "node:crypto";
89078
- import { gzipSync as gzipSync3 } from "node:zlib";
89079
- function computeShardDataHash(payload) {
89080
- return createHash5("md5").update(JSON.stringify(payload)).digest("hex");
89081
- }
89082
- function shardIdForPath(path4) {
89083
- const slash = path4.indexOf("/");
89084
- return slash === -1 ? "__root__" : path4.slice(0, slash);
89085
- }
89086
- function shouldUseV3Upload(tree) {
89087
- return Buffer.byteLength(JSON.stringify(tree), "utf8") > MAX_TREE_JSON_BYTES;
89088
- }
89089
- function childShardId(path4, parentShardId) {
89090
- if (parentShardId === "__root__") {
89091
- return shardIdForPath(path4);
90337
+ class WatchHelper {
90338
+ fsw;
90339
+ path;
90340
+ watchPath;
90341
+ fullWatchPath;
90342
+ dirParts;
90343
+ followSymlinks;
90344
+ statMethod;
90345
+ constructor(path5, follow, fsw) {
90346
+ this.fsw = fsw;
90347
+ const watchPath = path5;
90348
+ this.path = path5 = path5.replace(REPLACER_RE, "");
90349
+ this.watchPath = watchPath;
90350
+ this.fullWatchPath = sp2.resolve(watchPath);
90351
+ this.dirParts = [];
90352
+ this.dirParts.forEach((parts2) => {
90353
+ if (parts2.length > 1)
90354
+ parts2.pop();
90355
+ });
90356
+ this.followSymlinks = follow;
90357
+ this.statMethod = follow ? STAT_METHOD_F : STAT_METHOD_L;
89092
90358
  }
89093
- if (path4 === parentShardId) {
89094
- return parentShardId;
90359
+ entryPath(entry) {
90360
+ return sp2.join(this.watchPath, sp2.relative(this.watchPath, entry.fullPath));
89095
90361
  }
89096
- const prefix = `${parentShardId}/`;
89097
- if (!path4.startsWith(prefix)) {
89098
- return parentShardId;
90362
+ filterPath(entry) {
90363
+ const { stats } = entry;
90364
+ if (stats && stats.isSymbolicLink())
90365
+ return this.filterDir(entry);
90366
+ const resolvedPath = this.entryPath(entry);
90367
+ return this.fsw._isntIgnored(resolvedPath, stats) && this.fsw._hasReadPermissions(stats);
89099
90368
  }
89100
- const remainder = path4.slice(prefix.length);
89101
- const slash = remainder.indexOf("/");
89102
- return slash === -1 ? parentShardId : `${parentShardId}/${remainder.slice(0, slash)}`;
89103
- }
89104
- function groupEntriesByShardId(entries2, parentShardId) {
89105
- const groups = new Map;
89106
- for (const entry of entries2) {
89107
- const shardId = parentShardId === "__root__" ? shardIdForPath(entry.path) : childShardId(entry.path, parentShardId);
89108
- const group = groups.get(shardId) ?? [];
89109
- group.push(entry);
89110
- groups.set(shardId, group);
90369
+ filterDir(entry) {
90370
+ return this.fsw._isntIgnored(this.entryPath(entry), entry.stats);
89111
90371
  }
89112
- return groups;
89113
90372
  }
89114
- function buildPreparedShard(shardId, payload) {
89115
- const compressed = gzipSync3(Buffer.from(JSON.stringify(payload))).toString("base64");
89116
- return {
89117
- shardId,
89118
- payload,
89119
- dataHash: computeShardDataHash(payload),
89120
- entryCount: payload.entries.length,
89121
- data: { compression: "gzip", content: compressed }
89122
- };
90373
+ function watch(paths, options = {}) {
90374
+ const watcher = new FSWatcher(options);
90375
+ watcher.add(paths);
90376
+ return watcher;
89123
90377
  }
89124
- function prepareShardGroup(entries2, shardId, tree) {
89125
- const payload = {
89126
- entries: entries2,
89127
- scannedAt: tree.scannedAt,
89128
- rootDir: tree.rootDir
89129
- };
89130
- const compressed = gzipSync3(Buffer.from(JSON.stringify(payload))).toString("base64");
89131
- if (Buffer.byteLength(compressed, "utf8") <= MAX_SHARD_JSON_BYTES) {
89132
- return [buildPreparedShard(shardId, payload)];
89133
- }
89134
- const subgroups = groupEntriesByShardId(entries2, shardId);
89135
- if (subgroups.size <= 1) {
89136
- return [buildPreparedShard(shardId, payload)];
90378
+ var SLASH = "/", SLASH_SLASH = "//", ONE_DOT = ".", TWO_DOTS = "..", STRING_TYPE = "string", BACK_SLASH_RE, DOUBLE_SLASH_RE, DOT_RE, REPLACER_RE, isMatcherObject = (matcher) => typeof matcher === "object" && matcher !== null && !(matcher instanceof RegExp), unifyPaths = (paths_) => {
90379
+ const paths = arrify(paths_).flat();
90380
+ if (!paths.every((p) => typeof p === STRING_TYPE)) {
90381
+ throw new TypeError(`Non-string provided as watch path: ${paths}`);
89137
90382
  }
89138
- const shards = [];
89139
- for (const [subId, subEntries] of subgroups) {
89140
- shards.push(...prepareShardGroup(subEntries, subId, tree));
90383
+ return paths.map(normalizePathToUnix);
90384
+ }, toUnix = (string2) => {
90385
+ let str = string2.replace(BACK_SLASH_RE, SLASH);
90386
+ let prepend4 = false;
90387
+ if (str.startsWith(SLASH_SLASH)) {
90388
+ prepend4 = true;
89141
90389
  }
89142
- return shards;
89143
- }
89144
- function partitionFileTree(tree) {
89145
- const topGroups = groupEntriesByShardId(tree.entries, "__root__");
89146
- const shards = [];
89147
- for (const [shardId, entries2] of topGroups) {
89148
- shards.push(...prepareShardGroup(entries2, shardId, tree));
90390
+ str = str.replace(DOUBLE_SLASH_RE, SLASH);
90391
+ if (prepend4) {
90392
+ str = SLASH + str;
89149
90393
  }
89150
- return shards;
89151
- }
89152
- var MAX_TREE_JSON_BYTES, MAX_SHARD_JSON_BYTES, MAX_SHARD_BATCH_SIZE = 8;
89153
- var init_file_tree_partition = __esm(() => {
89154
- MAX_TREE_JSON_BYTES = 900 * 1024;
89155
- MAX_SHARD_JSON_BYTES = 800 * 1024;
89156
- });
89157
-
89158
- // src/infrastructure/services/workspace/workspace-file-walk.ts
89159
- import { promises as fsPromises } from "node:fs";
89160
- import path4 from "node:path";
89161
- async function walkWorkspaceFiles(rootDir, options) {
89162
- const maxFilePaths = options?.maxFilePaths ?? 1e4;
89163
- const filePaths = [];
89164
- let truncated = false;
89165
- const inRepo = await isGitRepo(rootDir);
89166
- const parsedIgnore = inRepo ? null : await loadWorkspaceIgnore(rootDir);
89167
- async function isIgnored(relativePath) {
89168
- if (inRepo) {
89169
- const ignored = await filterIgnoredPaths(rootDir, [relativePath]);
89170
- return ignored.has(relativePath);
90394
+ return str;
90395
+ }, normalizePathToUnix = (path5) => toUnix(sp2.normalize(toUnix(path5))), normalizeIgnored = (cwd = "") => (path5) => {
90396
+ if (typeof path5 === "string") {
90397
+ return normalizePathToUnix(sp2.isAbsolute(path5) ? path5 : sp2.join(cwd, path5));
90398
+ } else {
90399
+ return path5;
90400
+ }
90401
+ }, getAbsolutePath = (path5, cwd) => {
90402
+ if (sp2.isAbsolute(path5)) {
90403
+ return path5;
90404
+ }
90405
+ return sp2.join(cwd, path5);
90406
+ }, EMPTY_SET, STAT_METHOD_F = "stat", STAT_METHOD_L = "lstat", FSWatcher;
90407
+ var init_chokidar = __esm(() => {
90408
+ init_readdirp();
90409
+ init_handler();
90410
+ /*! chokidar - MIT License (c) 2012 Paul Miller (paulmillr.com) */
90411
+ BACK_SLASH_RE = /\\/g;
90412
+ DOUBLE_SLASH_RE = /\/\//g;
90413
+ DOT_RE = /\..*\.(sw[px])$|~$|\.subl.*\.tmp/;
90414
+ REPLACER_RE = /^\.[/\\]/;
90415
+ EMPTY_SET = Object.freeze(new Set);
90416
+ FSWatcher = class FSWatcher extends EventEmitter2 {
90417
+ closed;
90418
+ options;
90419
+ _closers;
90420
+ _ignoredPaths;
90421
+ _throttled;
90422
+ _streams;
90423
+ _symlinkPaths;
90424
+ _watched;
90425
+ _pendingWrites;
90426
+ _pendingUnlinks;
90427
+ _readyCount;
90428
+ _emitReady;
90429
+ _closePromise;
90430
+ _userIgnored;
90431
+ _readyEmitted;
90432
+ _emitRaw;
90433
+ _boundRemove;
90434
+ _nodeFsHandler;
90435
+ constructor(_opts = {}) {
90436
+ super();
90437
+ this.closed = false;
90438
+ this._closers = new Map;
90439
+ this._ignoredPaths = new Set;
90440
+ this._throttled = new Map;
90441
+ this._streams = new Set;
90442
+ this._symlinkPaths = new Map;
90443
+ this._watched = new Map;
90444
+ this._pendingWrites = new Map;
90445
+ this._pendingUnlinks = new Map;
90446
+ this._readyCount = 0;
90447
+ this._readyEmitted = false;
90448
+ const awf = _opts.awaitWriteFinish;
90449
+ const DEF_AWF = { stabilityThreshold: 2000, pollInterval: 100 };
90450
+ const opts = {
90451
+ persistent: true,
90452
+ ignoreInitial: false,
90453
+ ignorePermissionErrors: false,
90454
+ interval: 100,
90455
+ binaryInterval: 300,
90456
+ followSymlinks: true,
90457
+ usePolling: false,
90458
+ atomic: true,
90459
+ ..._opts,
90460
+ ignored: _opts.ignored ? arrify(_opts.ignored) : arrify([]),
90461
+ awaitWriteFinish: awf === true ? DEF_AWF : typeof awf === "object" ? { ...DEF_AWF, ...awf } : false
90462
+ };
90463
+ if (isIBMi)
90464
+ opts.usePolling = true;
90465
+ if (opts.atomic === undefined)
90466
+ opts.atomic = !opts.usePolling;
90467
+ const envPoll = process.env.CHOKIDAR_USEPOLLING;
90468
+ if (envPoll !== undefined) {
90469
+ const envLower = envPoll.toLowerCase();
90470
+ if (envLower === "false" || envLower === "0")
90471
+ opts.usePolling = false;
90472
+ else if (envLower === "true" || envLower === "1")
90473
+ opts.usePolling = true;
90474
+ else
90475
+ opts.usePolling = !!envLower;
90476
+ }
90477
+ const envInterval = process.env.CHOKIDAR_INTERVAL;
90478
+ if (envInterval)
90479
+ opts.interval = Number.parseInt(envInterval, 10);
90480
+ let readyCalls = 0;
90481
+ this._emitReady = () => {
90482
+ readyCalls++;
90483
+ if (readyCalls >= this._readyCount) {
90484
+ this._emitReady = EMPTY_FN;
90485
+ this._readyEmitted = true;
90486
+ process.nextTick(() => this.emit(EVENTS.READY));
90487
+ }
90488
+ };
90489
+ this._emitRaw = (...args2) => this.emit(EVENTS.RAW, ...args2);
90490
+ this._boundRemove = this._remove.bind(this);
90491
+ this.options = opts;
90492
+ this._nodeFsHandler = new NodeFsHandler(this);
90493
+ Object.freeze(opts);
90494
+ }
90495
+ _addIgnoredPath(matcher) {
90496
+ if (isMatcherObject(matcher)) {
90497
+ for (const ignored of this._ignoredPaths) {
90498
+ if (isMatcherObject(ignored) && ignored.path === matcher.path && ignored.recursive === matcher.recursive) {
90499
+ return;
90500
+ }
90501
+ }
90502
+ }
90503
+ this._ignoredPaths.add(matcher);
89171
90504
  }
89172
- return parsedIgnore !== null && isPathIgnoredByRules(parsedIgnore, relativePath);
89173
- }
89174
- async function visitDir(relDir) {
89175
- if (truncated || filePaths.length >= maxFilePaths) {
89176
- truncated = true;
89177
- return;
90505
+ _removeIgnoredPath(matcher) {
90506
+ this._ignoredPaths.delete(matcher);
90507
+ if (typeof matcher === "string") {
90508
+ for (const ignored of this._ignoredPaths) {
90509
+ if (isMatcherObject(ignored) && ignored.path === matcher) {
90510
+ this._ignoredPaths.delete(ignored);
90511
+ }
90512
+ }
90513
+ }
89178
90514
  }
89179
- const absDir = relDir ? path4.join(rootDir, relDir) : rootDir;
89180
- let dirents;
89181
- try {
89182
- dirents = await fsPromises.readdir(absDir, { withFileTypes: true });
89183
- } catch {
89184
- return;
90515
+ add(paths_, _origAdd, _internal) {
90516
+ const { cwd } = this.options;
90517
+ this.closed = false;
90518
+ this._closePromise = undefined;
90519
+ let paths = unifyPaths(paths_);
90520
+ if (cwd) {
90521
+ paths = paths.map((path5) => {
90522
+ const absPath = getAbsolutePath(path5, cwd);
90523
+ return absPath;
90524
+ });
90525
+ }
90526
+ paths.forEach((path5) => {
90527
+ this._removeIgnoredPath(path5);
90528
+ });
90529
+ this._userIgnored = undefined;
90530
+ if (!this._readyCount)
90531
+ this._readyCount = 0;
90532
+ this._readyCount += paths.length;
90533
+ Promise.all(paths.map(async (path5) => {
90534
+ const res = await this._nodeFsHandler._addToNodeFs(path5, !_internal, undefined, 0, _origAdd);
90535
+ if (res)
90536
+ this._emitReady();
90537
+ return res;
90538
+ })).then((results) => {
90539
+ if (this.closed)
90540
+ return;
90541
+ results.forEach((item) => {
90542
+ if (item)
90543
+ this.add(sp2.dirname(item), sp2.basename(_origAdd || item));
90544
+ });
90545
+ });
90546
+ return this;
89185
90547
  }
89186
- for (const ent of dirents) {
89187
- if (truncated || filePaths.length >= maxFilePaths) {
89188
- truncated = true;
90548
+ unwatch(paths_) {
90549
+ if (this.closed)
90550
+ return this;
90551
+ const paths = unifyPaths(paths_);
90552
+ const { cwd } = this.options;
90553
+ paths.forEach((path5) => {
90554
+ if (!sp2.isAbsolute(path5) && !this._closers.has(path5)) {
90555
+ if (cwd)
90556
+ path5 = sp2.join(cwd, path5);
90557
+ path5 = sp2.resolve(path5);
90558
+ }
90559
+ this._closePath(path5);
90560
+ this._addIgnoredPath(path5);
90561
+ if (this._watched.has(path5)) {
90562
+ this._addIgnoredPath({
90563
+ path: path5,
90564
+ recursive: true
90565
+ });
90566
+ }
90567
+ this._userIgnored = undefined;
90568
+ });
90569
+ return this;
90570
+ }
90571
+ close() {
90572
+ if (this._closePromise) {
90573
+ return this._closePromise;
90574
+ }
90575
+ this.closed = true;
90576
+ this.removeAllListeners();
90577
+ const closers = [];
90578
+ this._closers.forEach((closerList) => closerList.forEach((closer) => {
90579
+ const promise3 = closer();
90580
+ if (promise3 instanceof Promise)
90581
+ closers.push(promise3);
90582
+ }));
90583
+ this._streams.forEach((stream4) => stream4.destroy());
90584
+ this._userIgnored = undefined;
90585
+ this._readyCount = 0;
90586
+ this._readyEmitted = false;
90587
+ this._watched.forEach((dirent) => dirent.dispose());
90588
+ this._closers.clear();
90589
+ this._watched.clear();
90590
+ this._streams.clear();
90591
+ this._symlinkPaths.clear();
90592
+ this._throttled.clear();
90593
+ this._closePromise = closers.length ? Promise.all(closers).then(() => {
90594
+ return;
90595
+ }) : Promise.resolve();
90596
+ return this._closePromise;
90597
+ }
90598
+ getWatched() {
90599
+ const watchList = {};
90600
+ this._watched.forEach((entry, dir) => {
90601
+ const key = this.options.cwd ? sp2.relative(this.options.cwd, dir) : dir;
90602
+ const index = key || ONE_DOT;
90603
+ watchList[index] = entry.getChildren().sort();
90604
+ });
90605
+ return watchList;
90606
+ }
90607
+ emitWithAll(event, args2) {
90608
+ this.emit(event, ...args2);
90609
+ if (event !== EVENTS.ERROR)
90610
+ this.emit(EVENTS.ALL, event, ...args2);
90611
+ }
90612
+ async _emit(event, path5, stats) {
90613
+ if (this.closed)
89189
90614
  return;
90615
+ const opts = this.options;
90616
+ if (isWindows)
90617
+ path5 = sp2.normalize(path5);
90618
+ if (opts.cwd)
90619
+ path5 = sp2.relative(opts.cwd, path5);
90620
+ const args2 = [path5];
90621
+ if (stats != null)
90622
+ args2.push(stats);
90623
+ const awf = opts.awaitWriteFinish;
90624
+ let pw;
90625
+ if (awf && (pw = this._pendingWrites.get(path5))) {
90626
+ pw.lastChange = new Date;
90627
+ return this;
89190
90628
  }
89191
- if (isAlwaysExcludedDirName(ent.name))
89192
- continue;
89193
- const relativePath = relDir ? `${relDir}/${ent.name}` : ent.name;
89194
- if (!isPathVisible(relativePath))
89195
- continue;
89196
- if (ent.isDirectory()) {
89197
- if (await isIgnored(relativePath))
89198
- continue;
89199
- await visitDir(relativePath);
89200
- } else if (ent.isFile()) {
89201
- if (await isIgnored(relativePath))
89202
- continue;
89203
- filePaths.push(relativePath);
89204
- if (filePaths.length >= maxFilePaths)
89205
- truncated = true;
90629
+ if (opts.atomic) {
90630
+ if (event === EVENTS.UNLINK) {
90631
+ this._pendingUnlinks.set(path5, [event, ...args2]);
90632
+ setTimeout(() => {
90633
+ this._pendingUnlinks.forEach((entry, path6) => {
90634
+ this.emit(...entry);
90635
+ this.emit(EVENTS.ALL, ...entry);
90636
+ this._pendingUnlinks.delete(path6);
90637
+ });
90638
+ }, typeof opts.atomic === "number" ? opts.atomic : 100);
90639
+ return this;
90640
+ }
90641
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path5)) {
90642
+ event = EVENTS.CHANGE;
90643
+ this._pendingUnlinks.delete(path5);
90644
+ }
90645
+ }
90646
+ if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
90647
+ const awfEmit = (err, stats2) => {
90648
+ if (err) {
90649
+ event = EVENTS.ERROR;
90650
+ args2[0] = err;
90651
+ this.emitWithAll(event, args2);
90652
+ } else if (stats2) {
90653
+ if (args2.length > 1) {
90654
+ args2[1] = stats2;
90655
+ } else {
90656
+ args2.push(stats2);
90657
+ }
90658
+ this.emitWithAll(event, args2);
90659
+ }
90660
+ };
90661
+ this._awaitWriteFinish(path5, awf.stabilityThreshold, event, awfEmit);
90662
+ return this;
90663
+ }
90664
+ if (event === EVENTS.CHANGE) {
90665
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path5, 50);
90666
+ if (isThrottled)
90667
+ return this;
90668
+ }
90669
+ if (opts.alwaysStat && stats === undefined && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
90670
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path5) : path5;
90671
+ let stats2;
90672
+ try {
90673
+ stats2 = await stat4(fullPath);
90674
+ } catch (err) {}
90675
+ if (!stats2 || this.closed)
90676
+ return;
90677
+ args2.push(stats2);
90678
+ }
90679
+ this.emitWithAll(event, args2);
90680
+ return this;
90681
+ }
90682
+ _handleError(error) {
90683
+ const code2 = error && error.code;
90684
+ if (error && code2 !== "ENOENT" && code2 !== "ENOTDIR" && (!this.options.ignorePermissionErrors || code2 !== "EPERM" && code2 !== "EACCES")) {
90685
+ this.emit(EVENTS.ERROR, error);
90686
+ }
90687
+ return error || this.closed;
90688
+ }
90689
+ _throttle(actionType, path5, timeout3) {
90690
+ if (!this._throttled.has(actionType)) {
90691
+ this._throttled.set(actionType, new Map);
90692
+ }
90693
+ const action = this._throttled.get(actionType);
90694
+ if (!action)
90695
+ throw new Error("invalid throttle");
90696
+ const actionPath = action.get(path5);
90697
+ if (actionPath) {
90698
+ actionPath.count++;
90699
+ return false;
90700
+ }
90701
+ let timeoutObject;
90702
+ const clear = () => {
90703
+ const item = action.get(path5);
90704
+ const count3 = item ? item.count : 0;
90705
+ action.delete(path5);
90706
+ clearTimeout(timeoutObject);
90707
+ if (item)
90708
+ clearTimeout(item.timeoutObject);
90709
+ return count3;
90710
+ };
90711
+ timeoutObject = setTimeout(clear, timeout3);
90712
+ const thr = { timeoutObject, clear, count: 0 };
90713
+ action.set(path5, thr);
90714
+ return thr;
90715
+ }
90716
+ _incrReadyCount() {
90717
+ return this._readyCount++;
90718
+ }
90719
+ _awaitWriteFinish(path5, threshold, event, awfEmit) {
90720
+ const awf = this.options.awaitWriteFinish;
90721
+ if (typeof awf !== "object")
90722
+ return;
90723
+ const pollInterval = awf.pollInterval;
90724
+ let timeoutHandler;
90725
+ let fullPath = path5;
90726
+ if (this.options.cwd && !sp2.isAbsolute(path5)) {
90727
+ fullPath = sp2.join(this.options.cwd, path5);
90728
+ }
90729
+ const now = new Date;
90730
+ const writes = this._pendingWrites;
90731
+ function awaitWriteFinishFn(prevStat) {
90732
+ statcb(fullPath, (err, curStat) => {
90733
+ if (err || !writes.has(path5)) {
90734
+ if (err && err.code !== "ENOENT")
90735
+ awfEmit(err);
90736
+ return;
90737
+ }
90738
+ const now2 = Number(new Date);
90739
+ if (prevStat && curStat.size !== prevStat.size) {
90740
+ writes.get(path5).lastChange = now2;
90741
+ }
90742
+ const pw = writes.get(path5);
90743
+ const df = now2 - pw.lastChange;
90744
+ if (df >= threshold) {
90745
+ writes.delete(path5);
90746
+ awfEmit(undefined, curStat);
90747
+ } else {
90748
+ timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
90749
+ }
90750
+ });
90751
+ }
90752
+ if (!writes.has(path5)) {
90753
+ writes.set(path5, {
90754
+ lastChange: now,
90755
+ cancelWait: () => {
90756
+ writes.delete(path5);
90757
+ clearTimeout(timeoutHandler);
90758
+ return event;
90759
+ }
90760
+ });
90761
+ timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval);
90762
+ }
90763
+ }
90764
+ _isIgnored(path5, stats) {
90765
+ if (this.options.atomic && DOT_RE.test(path5))
90766
+ return true;
90767
+ if (!this._userIgnored) {
90768
+ const { cwd } = this.options;
90769
+ const ign = this.options.ignored;
90770
+ const ignored = (ign || []).map(normalizeIgnored(cwd));
90771
+ const ignoredPaths = [...this._ignoredPaths];
90772
+ const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
90773
+ this._userIgnored = anymatch(list, undefined);
90774
+ }
90775
+ return this._userIgnored(path5, stats);
90776
+ }
90777
+ _isntIgnored(path5, stat5) {
90778
+ return !this._isIgnored(path5, stat5);
90779
+ }
90780
+ _getWatchHelpers(path5) {
90781
+ return new WatchHelper(path5, this.options.followSymlinks, this);
90782
+ }
90783
+ _getWatchedDir(directory) {
90784
+ const dir = sp2.resolve(directory);
90785
+ if (!this._watched.has(dir))
90786
+ this._watched.set(dir, new DirEntry(dir, this._boundRemove));
90787
+ return this._watched.get(dir);
90788
+ }
90789
+ _hasReadPermissions(stats) {
90790
+ if (this.options.ignorePermissionErrors)
90791
+ return true;
90792
+ return Boolean(Number(stats.mode) & 256);
90793
+ }
90794
+ _remove(directory, item, isDirectory) {
90795
+ const path5 = sp2.join(directory, item);
90796
+ const fullPath = sp2.resolve(path5);
90797
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path5) || this._watched.has(fullPath);
90798
+ if (!this._throttle("remove", path5, 100))
90799
+ return;
90800
+ if (!isDirectory && this._watched.size === 1) {
90801
+ this.add(directory, item, true);
90802
+ }
90803
+ const wp = this._getWatchedDir(path5);
90804
+ const nestedDirectoryChildren = wp.getChildren();
90805
+ nestedDirectoryChildren.forEach((nested2) => this._remove(path5, nested2));
90806
+ const parent = this._getWatchedDir(directory);
90807
+ const wasTracked = parent.has(item);
90808
+ parent.remove(item);
90809
+ if (this._symlinkPaths.has(fullPath)) {
90810
+ this._symlinkPaths.delete(fullPath);
90811
+ }
90812
+ let relPath = path5;
90813
+ if (this.options.cwd)
90814
+ relPath = sp2.relative(this.options.cwd, path5);
90815
+ if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
90816
+ const event = this._pendingWrites.get(relPath).cancelWait();
90817
+ if (event === EVENTS.ADD)
90818
+ return;
89206
90819
  }
90820
+ this._watched.delete(path5);
90821
+ this._watched.delete(fullPath);
90822
+ const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
90823
+ if (wasTracked && !this._isIgnored(path5))
90824
+ this._emit(eventName, path5);
90825
+ this._closePath(path5);
90826
+ }
90827
+ _closePath(path5) {
90828
+ this._closeFile(path5);
90829
+ const dir = sp2.dirname(path5);
90830
+ this._getWatchedDir(dir).remove(sp2.basename(path5));
90831
+ }
90832
+ _closeFile(path5) {
90833
+ const closers = this._closers.get(path5);
90834
+ if (!closers)
90835
+ return;
90836
+ closers.forEach((closer) => closer());
90837
+ this._closers.delete(path5);
89207
90838
  }
89208
- }
89209
- await visitDir("");
89210
- return { filePaths, truncated };
89211
- }
89212
- var init_workspace_file_walk = __esm(() => {
89213
- init_workspace_ignore();
89214
- init_workspace_visibility_policy();
89215
- init_git_reader();
90839
+ _addPathCloser(path5, closer) {
90840
+ if (!closer)
90841
+ return;
90842
+ let list = this._closers.get(path5);
90843
+ if (!list) {
90844
+ list = [];
90845
+ this._closers.set(path5, list);
90846
+ }
90847
+ list.push(closer);
90848
+ }
90849
+ _readdirp(root, opts) {
90850
+ if (this.closed)
90851
+ return;
90852
+ const options = { type: EVENTS.ALL, alwaysStat: true, lstat: true, ...opts, depth: 0 };
90853
+ let stream4 = readdirp(root, options);
90854
+ this._streams.add(stream4);
90855
+ stream4.once(STR_CLOSE, () => {
90856
+ stream4 = undefined;
90857
+ });
90858
+ stream4.once(STR_END, () => {
90859
+ if (stream4) {
90860
+ this._streams.delete(stream4);
90861
+ stream4 = undefined;
90862
+ }
90863
+ });
90864
+ return stream4;
90865
+ }
90866
+ };
89216
90867
  });
89217
90868
 
89218
- // src/infrastructure/services/workspace/file-tree-scanner.ts
89219
- async function scanFileTree(rootDir, options) {
89220
- const maxEntries = options?.maxEntries ?? DEFAULT_MAX_ENTRIES;
89221
- const scannedAt = Date.now();
89222
- const walk = await walkWorkspaceFiles(rootDir, { maxFilePaths: maxEntries });
89223
- const filteredPaths = walk.filePaths.filter((p) => !isExcluded(p));
89224
- const entries2 = buildEntries(filteredPaths, maxEntries);
89225
- return {
89226
- entries: entries2,
89227
- scannedAt,
89228
- rootDir
89229
- };
89230
- }
89231
- function isExcluded(filePath) {
89232
- return hasExcludedDirSegment(filePath);
90869
+ // src/infrastructure/services/workspace/workspace-fs-watcher.ts
90870
+ import path5 from "node:path";
90871
+ function toRelativePath(rootDir, absolutePath) {
90872
+ return path5.relative(rootDir, absolutePath).replace(/\\/g, "/").replace(/^\.?\//, "");
89233
90873
  }
89234
- function buildEntries(filePaths, maxEntries) {
89235
- const directories = new Set;
89236
- for (const filePath of filePaths) {
89237
- const parts2 = filePath.split("/");
89238
- for (let i2 = 1;i2 < parts2.length; i2++) {
89239
- directories.add(parts2.slice(0, i2).join("/"));
90874
+ function createWorkspaceFsWatcher(options) {
90875
+ const absWorkingDir = path5.resolve(options.workingDir);
90876
+ const debounceMs = options.debounceMs ?? DEFAULT_DEBOUNCE_MS;
90877
+ let stopped = false;
90878
+ let debounceTimer = null;
90879
+ const pendingEvents = new Map;
90880
+ const scheduleFlush = () => {
90881
+ if (debounceTimer)
90882
+ clearTimeout(debounceTimer);
90883
+ debounceTimer = setTimeout(() => {
90884
+ debounceTimer = null;
90885
+ if (pendingEvents.size === 0 || stopped)
90886
+ return;
90887
+ const events = [...pendingEvents.values()].sort((a, b) => a.path.localeCompare(b.path));
90888
+ pendingEvents.clear();
90889
+ Promise.resolve(options.onEvents(events)).catch((error) => {
90890
+ options.onError?.(error);
90891
+ });
90892
+ }, debounceMs);
90893
+ debounceTimer.unref?.();
90894
+ };
90895
+ const enqueue = (kind, absolutePath) => {
90896
+ if (stopped)
90897
+ return;
90898
+ const relativePath = toRelativePath(absWorkingDir, absolutePath);
90899
+ if (!relativePath || !isPathVisible(relativePath) || hasExcludedDirSegment(relativePath))
90900
+ return;
90901
+ if (options.shouldIgnore?.(relativePath))
90902
+ return;
90903
+ pendingEvents.set(relativePath, { kind, path: relativePath });
90904
+ scheduleFlush();
90905
+ };
90906
+ const watcher = watch(absWorkingDir, {
90907
+ ignored: (watchPath) => {
90908
+ const relativePath = toRelativePath(absWorkingDir, watchPath);
90909
+ if (!relativePath)
90910
+ return false;
90911
+ return !isPathVisible(relativePath) || hasExcludedDirSegment(relativePath) || options.shouldIgnore?.(relativePath) === true;
90912
+ },
90913
+ ignoreInitial: true,
90914
+ persistent: true,
90915
+ awaitWriteFinish: { stabilityThreshold: 150, pollInterval: 25 }
90916
+ });
90917
+ watcher.on("add", (filePath) => enqueue("add", filePath)).on("addDir", (filePath) => enqueue("addDir", filePath)).on("change", (filePath) => enqueue("change", filePath)).on("unlink", (filePath) => enqueue("unlink", filePath)).on("unlinkDir", (filePath) => enqueue("unlinkDir", filePath)).on("error", (error) => options.onError?.(error));
90918
+ const ready = new Promise((resolve7) => {
90919
+ watcher.once("ready", resolve7);
90920
+ });
90921
+ return {
90922
+ ready,
90923
+ stop: async () => {
90924
+ stopped = true;
90925
+ if (debounceTimer) {
90926
+ clearTimeout(debounceTimer);
90927
+ debounceTimer = null;
90928
+ }
90929
+ pendingEvents.clear();
90930
+ await watcher.close();
89240
90931
  }
89241
- }
89242
- const entries2 = [];
89243
- const sortedDirs = Array.from(directories).sort();
89244
- for (const dir of sortedDirs) {
89245
- if (entries2.length >= maxEntries)
89246
- break;
89247
- entries2.push({ path: dir, type: "directory" });
89248
- }
89249
- const sortedFiles = filePaths.slice().sort();
89250
- for (const file of sortedFiles) {
89251
- if (entries2.length >= maxEntries)
89252
- break;
89253
- entries2.push({ path: file, type: "file" });
89254
- }
89255
- return entries2;
90932
+ };
89256
90933
  }
89257
- var DEFAULT_MAX_ENTRIES = 1e4;
89258
- var init_file_tree_scanner = __esm(() => {
89259
- init_workspace_file_walk();
90934
+ var DEFAULT_DEBOUNCE_MS = 250;
90935
+ var init_workspace_fs_watcher = __esm(() => {
90936
+ init_chokidar();
89260
90937
  init_workspace_visibility_policy();
89261
90938
  });
89262
90939
 
89263
- // src/infrastructure/services/workspace/file-tree-v3-upload.ts
89264
- async function uploadFileTreeV3(session2, workingDir, tree, syncGeneration) {
89265
- const shards = partitionFileTree(tree);
89266
- const shardIds = [];
89267
- for (let i2 = 0;i2 < shards.length; i2 += MAX_SHARD_BATCH_SIZE) {
89268
- const batch = shards.slice(i2, i2 + MAX_SHARD_BATCH_SIZE);
89269
- await session2.backend.mutation(api.workspaceFiles.syncFileTreeShardV3Batch, {
89270
- sessionId: session2.sessionId,
89271
- machineId: session2.machineId,
89272
- workingDir,
89273
- syncGeneration,
89274
- items: batch.map((s) => ({
89275
- shardId: s.shardId,
89276
- data: s.data,
89277
- dataHash: s.dataHash,
89278
- scannedAt: tree.scannedAt,
89279
- entryCount: s.entryCount
89280
- }))
89281
- });
89282
- for (const s of batch)
89283
- shardIds.push(s.shardId);
89284
- }
89285
- await session2.backend.mutation(api.workspaceFiles.syncFileTreeManifestV3, {
89286
- sessionId: session2.sessionId,
89287
- machineId: session2.machineId,
89288
- workingDir,
89289
- syncGeneration,
89290
- shardIds,
89291
- totalEntryCount: tree.entries.length,
89292
- complete: true,
89293
- scannedAt: tree.scannedAt
89294
- });
89295
- return { shardIds, totalEntryCount: tree.entries.length };
89296
- }
89297
- var init_file_tree_v3_upload = __esm(() => {
89298
- init_file_tree_partition();
89299
- init_api3();
89300
- });
89301
-
89302
90940
  // src/infrastructure/services/workspace/workspace-sync-diff.ts
89303
90941
  function diffPathIndexes(previous, next4) {
89304
90942
  const added = [];
89305
90943
  const removed = [];
89306
90944
  const typeChanged = [];
89307
90945
  const prev = previous ?? {};
89308
- for (const [path5, type] of Object.entries(next4)) {
89309
- if (!(path5 in prev)) {
89310
- added.push(path5);
89311
- } else if (prev[path5] !== type) {
89312
- typeChanged.push(path5);
90946
+ for (const [path6, type] of Object.entries(next4)) {
90947
+ if (!(path6 in prev)) {
90948
+ added.push(path6);
90949
+ } else if (prev[path6] !== type) {
90950
+ typeChanged.push(path6);
89313
90951
  }
89314
90952
  }
89315
- for (const path5 of Object.keys(prev)) {
89316
- if (!(path5 in next4)) {
89317
- removed.push(path5);
90953
+ for (const path6 of Object.keys(prev)) {
90954
+ if (!(path6 in next4)) {
90955
+ removed.push(path6);
89318
90956
  }
89319
90957
  }
89320
90958
  added.sort();
@@ -89322,57 +90960,12 @@ function diffPathIndexes(previous, next4) {
89322
90960
  typeChanged.sort();
89323
90961
  return { added, removed, typeChanged };
89324
90962
  }
89325
- function formatPathDiffSummary(diff8) {
89326
- return `+${diff8.added.length} added, -${diff8.removed.length} removed${diff8.typeChanged.length > 0 ? `, ~${diff8.typeChanged.length} type-changed` : ""}`;
89327
- }
89328
-
89329
- // src/infrastructure/services/workspace/workspace-sync-queue.ts
89330
- function queueKey(machineId, workingDir) {
89331
- return `${machineId}\x00${workingDir}`;
89332
- }
89333
- function getOrCreateState(key) {
89334
- let state = queues.get(key);
89335
- if (!state) {
89336
- state = { running: false, trailing: false, drainPromise: null };
89337
- queues.set(key, state);
89338
- }
89339
- return state;
89340
- }
89341
- async function enqueueFileTreeSync(machineId, workingDir, task) {
89342
- const key = queueKey(machineId, workingDir);
89343
- const state = getOrCreateState(key);
89344
- if (state.running) {
89345
- state.trailing = true;
89346
- return state.drainPromise ?? Promise.resolve();
89347
- }
89348
- const run3 = async () => {
89349
- state.running = true;
89350
- try {
89351
- do {
89352
- state.trailing = false;
89353
- await task();
89354
- } while (state.trailing);
89355
- } finally {
89356
- state.running = false;
89357
- state.trailing = false;
89358
- if (!state.trailing) {
89359
- queues.delete(key);
89360
- }
89361
- }
89362
- };
89363
- state.drainPromise = run3();
89364
- return state.drainPromise;
89365
- }
89366
- var queues;
89367
- var init_workspace_sync_queue = __esm(() => {
89368
- queues = new Map;
89369
- });
89370
90963
 
89371
90964
  // src/infrastructure/services/workspace/workspace-sync-state.ts
89372
90965
  import { createHash as createHash6, randomUUID as randomUUID6 } from "node:crypto";
89373
90966
  import * as fs11 from "node:fs/promises";
89374
90967
  import { homedir as homedir7 } from "node:os";
89375
- import { join as join18 } from "node:path";
90968
+ import { join as join20 } from "node:path";
89376
90969
  function workspaceKeyFor(workingDir) {
89377
90970
  const normalized = normalizeWorkingDirForLookup(workingDir);
89378
90971
  return createHash6("sha256").update(normalized).digest("hex").slice(0, 16);
@@ -89386,15 +90979,32 @@ function buildPathIndex(entries2) {
89386
90979
  }
89387
90980
  function manifestPath(machineId, workingDir) {
89388
90981
  const key = workspaceKeyFor(workingDir);
89389
- return join18(SYNC_STATE_DIR, machineId, key, "manifest.json");
90982
+ return join20(SYNC_STATE_DIR, machineId, key, "manifest.json");
89390
90983
  }
89391
90984
  async function ensureDir(filePath) {
89392
- await fs11.mkdir(join18(filePath, ".."), { recursive: true, mode: 448 });
90985
+ await fs11.mkdir(join20(filePath, ".."), { recursive: true, mode: 448 });
89393
90986
  }
89394
90987
  async function loadWorkspaceSyncManifest(machineId, workingDir) {
89395
90988
  try {
89396
90989
  const content = await fs11.readFile(manifestPath(machineId, workingDir), "utf-8");
89397
- return JSON.parse(content);
90990
+ const parsed = JSON.parse(content);
90991
+ if (!parsed || typeof parsed !== "object" || !parsed.paths)
90992
+ return null;
90993
+ if (parsed.version === "1") {
90994
+ return {
90995
+ ...parsed,
90996
+ version: SYNC_STATE_VERSION,
90997
+ scanner: "filesystem",
90998
+ localRevision: 0,
90999
+ backendRevision: 0,
91000
+ checkpointRevision: 0,
91001
+ lastReconciledAt: parsed.completedAt,
91002
+ pendingDeltas: []
91003
+ };
91004
+ }
91005
+ if (parsed.version !== SYNC_STATE_VERSION)
91006
+ return null;
91007
+ return parsed;
89398
91008
  } catch (error) {
89399
91009
  if (error.code === "ENOENT")
89400
91010
  return null;
@@ -89419,15 +91029,275 @@ function createManifestFromTree(args2) {
89419
91029
  scanner: args2.scanner,
89420
91030
  dataHash: args2.dataHash,
89421
91031
  totalEntryCount: args2.tree.entries.length,
89422
- paths: buildPathIndex(args2.tree.entries)
91032
+ paths: buildPathIndex(args2.tree.entries),
91033
+ localRevision: 0,
91034
+ backendRevision: 0,
91035
+ checkpointRevision: 0,
91036
+ lastReconciledAt: args2.tree.scannedAt,
91037
+ pendingDeltas: []
89423
91038
  };
89424
91039
  }
89425
- var SYNC_STATE_VERSION = "1", SYNC_STATE_DIR;
91040
+ function entriesFromPathIndex(paths) {
91041
+ return Object.entries(paths).map(([path6, type]) => ({ path: path6, type })).sort((a, b) => a.path.localeCompare(b.path));
91042
+ }
91043
+ var SYNC_STATE_VERSION = "2", SYNC_STATE_DIR;
89426
91044
  var init_workspace_sync_state = __esm(() => {
89427
- SYNC_STATE_DIR = join18(homedir7(), ".chatroom", "sync-state");
91045
+ SYNC_STATE_DIR = join20(homedir7(), ".chatroom", "sync-state");
91046
+ });
91047
+
91048
+ // src/infrastructure/services/workspace/workspace-file-tree-coordinator.ts
91049
+ import { randomUUID as randomUUID7 } from "node:crypto";
91050
+ import path6 from "node:path";
91051
+ function deltaEntry(paths, pathValue) {
91052
+ const type = paths[pathValue];
91053
+ if (type === undefined)
91054
+ throw new Error(`Missing path type for delta: ${pathValue}`);
91055
+ return { path: pathValue, type };
91056
+ }
91057
+ function buildPendingDeltas(previous, next4) {
91058
+ const diff8 = diffPathIndexes(previous, next4);
91059
+ if (diff8.added.length === 0 && diff8.removed.length === 0 && diff8.typeChanged.length === 0) {
91060
+ return [];
91061
+ }
91062
+ const operations = [
91063
+ ...diff8.added.map((entryPath) => ({
91064
+ kind: "added",
91065
+ entry: deltaEntry(next4, entryPath)
91066
+ })),
91067
+ ...diff8.removed.map((entryPath) => ({ kind: "removed", path: entryPath })),
91068
+ ...diff8.typeChanged.map((entryPath) => ({
91069
+ kind: "typeChanged",
91070
+ entry: deltaEntry(next4, entryPath)
91071
+ }))
91072
+ ];
91073
+ const result = [];
91074
+ for (let offset = 0;offset < operations.length; offset += MAX_DELTA_OPERATIONS) {
91075
+ const delta = {
91076
+ operationId: randomUUID7(),
91077
+ added: [],
91078
+ removed: [],
91079
+ typeChanged: [],
91080
+ createdAt: Date.now()
91081
+ };
91082
+ for (const operation of operations.slice(offset, offset + MAX_DELTA_OPERATIONS)) {
91083
+ if (operation.kind === "added")
91084
+ delta.added.push(operation.entry);
91085
+ else if (operation.kind === "removed")
91086
+ delta.removed.push(operation.path);
91087
+ else
91088
+ delta.typeChanged.push(operation.entry);
91089
+ }
91090
+ result.push(delta);
91091
+ }
91092
+ return result;
91093
+ }
91094
+ function isIgnoreFile(relativePath) {
91095
+ return relativePath === ".gitignore" || relativePath === ".cursorignore" || relativePath.endsWith("/.gitignore");
91096
+ }
91097
+ function removePathAndDescendants(paths, relativePath) {
91098
+ delete paths[relativePath];
91099
+ const prefix = `${relativePath}/`;
91100
+ for (const candidate of Object.keys(paths)) {
91101
+ if (candidate.startsWith(prefix))
91102
+ delete paths[candidate];
91103
+ }
91104
+ }
91105
+ function ensureParentDirectories(paths, relativePath) {
91106
+ const parts2 = relativePath.split("/");
91107
+ for (let index = 1;index < parts2.length; index++) {
91108
+ paths[parts2.slice(0, index).join("/")] = "directory";
91109
+ }
91110
+ }
91111
+ async function addDirectorySubtree(rootDir, relativeDir, paths) {
91112
+ if (await isWorkspacePathIgnored(rootDir, relativeDir))
91113
+ return;
91114
+ ensureParentDirectories(paths, relativeDir);
91115
+ paths[relativeDir] = "directory";
91116
+ const subtree = await scanFileTree(path6.join(rootDir, relativeDir));
91117
+ for (const entry of subtree.entries) {
91118
+ const prefixedPath = `${relativeDir}/${entry.path}`;
91119
+ if (await isWorkspacePathIgnored(rootDir, prefixedPath))
91120
+ continue;
91121
+ paths[prefixedPath] = entry.type;
91122
+ }
91123
+ }
91124
+ async function applyFsEvents(rootDir, previous, events) {
91125
+ if (events.some((event) => isIgnoreFile(event.path)))
91126
+ return null;
91127
+ const next4 = { ...previous };
91128
+ for (const event of events) {
91129
+ if (event.kind === "unlink" || event.kind === "unlinkDir") {
91130
+ removePathAndDescendants(next4, event.path);
91131
+ continue;
91132
+ }
91133
+ if (event.kind === "change" && next4[event.path] !== undefined)
91134
+ continue;
91135
+ if (await isWorkspacePathIgnored(rootDir, event.path)) {
91136
+ removePathAndDescendants(next4, event.path);
91137
+ continue;
91138
+ }
91139
+ if (event.kind === "addDir") {
91140
+ await addDirectorySubtree(rootDir, event.path, next4);
91141
+ } else {
91142
+ ensureParentDirectories(next4, event.path);
91143
+ next4[event.path] = "file";
91144
+ }
91145
+ }
91146
+ return next4;
91147
+ }
91148
+ function treeFromManifest(manifest) {
91149
+ return {
91150
+ entries: entriesFromPathIndex(manifest.paths),
91151
+ rootDir: manifest.workingDir,
91152
+ scannedAt: manifest.completedAt
91153
+ };
91154
+ }
91155
+ function nextReconcileDelay(baseMs) {
91156
+ const jitter = baseMs * RECONCILE_JITTER_RATIO;
91157
+ return Math.max(1000, Math.round(baseMs - jitter + Math.random() * jitter * 2));
91158
+ }
91159
+ async function startWorkspaceFileTreeCoordinator(options) {
91160
+ const workingDir = normalizeWorkingDirForLookup(options.workingDir);
91161
+ let stopped = false;
91162
+ let serial = Promise.resolve();
91163
+ let reconcileTimer = null;
91164
+ const loadedManifest = await loadWorkspaceSyncManifest(options.machineId, workingDir);
91165
+ const isColdStart = loadedManifest === null;
91166
+ let manifest;
91167
+ if (loadedManifest) {
91168
+ manifest = loadedManifest;
91169
+ } else {
91170
+ const tree = await scanFileTree(workingDir);
91171
+ manifest = createManifestFromTree({
91172
+ machineId: options.machineId,
91173
+ workingDir,
91174
+ scanner: "filesystem",
91175
+ dataHash: computeFileTreeDataHash(tree),
91176
+ tree
91177
+ });
91178
+ await saveWorkspaceSyncManifest(manifest);
91179
+ }
91180
+ const publishCheckpoint = async () => {
91181
+ const result = await options.onCheckpoint(treeFromManifest(manifest), manifest.backendRevision);
91182
+ manifest.backendRevision = result.revision;
91183
+ manifest.checkpointRevision = result.revision;
91184
+ manifest.pendingDeltas = [];
91185
+ await saveWorkspaceSyncManifest(manifest);
91186
+ };
91187
+ if (isColdStart)
91188
+ await publishCheckpoint();
91189
+ const flushPending = async () => {
91190
+ while (!stopped && manifest.pendingDeltas.length > 0) {
91191
+ const delta = manifest.pendingDeltas[0];
91192
+ if (!delta)
91193
+ break;
91194
+ const result = await options.onDelta(delta, manifest.backendRevision);
91195
+ if (result.status === "conflict") {
91196
+ manifest.backendRevision = result.revision;
91197
+ await saveWorkspaceSyncManifest(manifest);
91198
+ continue;
91199
+ }
91200
+ manifest.backendRevision = result.revision;
91201
+ manifest.pendingDeltas.shift();
91202
+ await saveWorkspaceSyncManifest(manifest);
91203
+ const checkpointEvery = options.checkpointEveryRevisions ?? DEFAULT_CHECKPOINT_EVERY_REVISIONS;
91204
+ if (manifest.pendingDeltas.length === 0 && manifest.backendRevision - manifest.checkpointRevision >= checkpointEvery) {
91205
+ await publishCheckpoint();
91206
+ }
91207
+ }
91208
+ };
91209
+ const commitPaths = async (nextPaths, reconciledAt) => {
91210
+ const deltas = buildPendingDeltas(manifest.paths, nextPaths);
91211
+ if (reconciledAt !== undefined)
91212
+ manifest.lastReconciledAt = reconciledAt;
91213
+ if (deltas.length === 0) {
91214
+ if (reconciledAt !== undefined)
91215
+ await saveWorkspaceSyncManifest(manifest);
91216
+ await flushPending();
91217
+ return 0;
91218
+ }
91219
+ manifest.paths = nextPaths;
91220
+ manifest.totalEntryCount = Object.keys(nextPaths).length;
91221
+ manifest.completedAt = Date.now();
91222
+ manifest.localRevision += 1;
91223
+ manifest.pendingDeltas.push(...deltas);
91224
+ manifest.dataHash = computeFileTreeDataHash(treeFromManifest(manifest));
91225
+ await saveWorkspaceSyncManifest(manifest);
91226
+ await flushPending();
91227
+ return deltas.reduce((total, delta) => total + delta.added.length + delta.removed.length + delta.typeChanged.length, 0);
91228
+ };
91229
+ const reconcileNow = async () => {
91230
+ const tree = await scanFileTree(workingDir);
91231
+ const corrected = await commitPaths(buildPathIndex(tree.entries), tree.scannedAt);
91232
+ options.onReconciled?.(corrected);
91233
+ };
91234
+ const enqueueSerial = (task) => {
91235
+ serial = serial.then(task, task).catch((error) => {
91236
+ options.onError?.(error);
91237
+ });
91238
+ return serial;
91239
+ };
91240
+ const scheduleReconcile = (delay3) => {
91241
+ if (stopped)
91242
+ return;
91243
+ if (reconcileTimer)
91244
+ clearTimeout(reconcileTimer);
91245
+ const interval = options.reconcileIntervalMs ?? DEFAULT_RECONCILE_INTERVAL_MS;
91246
+ reconcileTimer = setTimeout(() => {
91247
+ reconcileTimer = null;
91248
+ enqueueSerial(reconcileNow).finally(() => scheduleReconcile());
91249
+ }, delay3 ?? nextReconcileDelay(interval));
91250
+ reconcileTimer.unref?.();
91251
+ };
91252
+ await flushPending();
91253
+ const watcher = createWorkspaceFsWatcher({
91254
+ workingDir,
91255
+ onEvents: async (events) => {
91256
+ await enqueueSerial(async () => {
91257
+ const nextPaths = await applyFsEvents(workingDir, manifest.paths, events);
91258
+ if (nextPaths === null) {
91259
+ await reconcileNow();
91260
+ return;
91261
+ }
91262
+ await commitPaths(nextPaths);
91263
+ });
91264
+ },
91265
+ onError: (error) => {
91266
+ options.onError?.(error);
91267
+ scheduleReconcile(1000);
91268
+ }
91269
+ });
91270
+ await watcher.ready;
91271
+ scheduleReconcile();
91272
+ return {
91273
+ workingDir,
91274
+ getManifest: () => manifest,
91275
+ getTree: () => treeFromManifest(manifest),
91276
+ checkpoint: () => enqueueSerial(publishCheckpoint),
91277
+ reconcile: () => enqueueSerial(reconcileNow),
91278
+ stop: async () => {
91279
+ stopped = true;
91280
+ if (reconcileTimer)
91281
+ clearTimeout(reconcileTimer);
91282
+ reconcileTimer = null;
91283
+ await watcher.stop();
91284
+ await serial;
91285
+ await saveWorkspaceSyncManifest(manifest);
91286
+ }
91287
+ };
91288
+ }
91289
+ var DEFAULT_RECONCILE_INTERVAL_MS, DEFAULT_CHECKPOINT_EVERY_REVISIONS = 100, RECONCILE_JITTER_RATIO = 0.2, MAX_DELTA_OPERATIONS = 100;
91290
+ var init_workspace_file_tree_coordinator = __esm(() => {
91291
+ init_file_tree_data_hash();
91292
+ init_file_tree_scanner();
91293
+ init_workspace_fs_watcher();
91294
+ init_workspace_ignore();
91295
+ init_workspace_sync_state();
91296
+ DEFAULT_RECONCILE_INTERVAL_MS = 10 * 60 * 1000;
89428
91297
  });
89429
91298
 
89430
91299
  // src/commands/machine/daemon-start/file-tree-subscription.ts
91300
+ import { randomUUID as randomUUID8 } from "node:crypto";
89431
91301
  import { gzipSync as gzipSync4 } from "node:zlib";
89432
91302
  function logSubscriptionWarn(label, err) {
89433
91303
  console.warn(`[${formatTimestamp()}] ⚠️ ${label}: ${getErrorMessage2(err)}`);
@@ -89435,7 +91305,7 @@ function logSubscriptionWarn(label, err) {
89435
91305
  async function syncScannedFileTree(session2, normalizedWorkingDir, tree, dataHash, syncGeneration) {
89436
91306
  if (shouldUseV3Upload(tree)) {
89437
91307
  await uploadFileTreeV3(session2, normalizedWorkingDir, tree, syncGeneration);
89438
- return;
91308
+ return { snapshotKind: "v3", snapshotId: syncGeneration };
89439
91309
  }
89440
91310
  const treeJson = JSON.stringify(tree);
89441
91311
  const compressed = gzipSync4(Buffer.from(treeJson)).toString("base64");
@@ -89447,60 +91317,122 @@ async function syncScannedFileTree(session2, normalizedWorkingDir, tree, dataHas
89447
91317
  dataHash,
89448
91318
  scannedAt: tree.scannedAt
89449
91319
  });
91320
+ return { snapshotKind: "v2", snapshotId: dataHash };
89450
91321
  }
89451
- async function uploadFileTree(session2, workingDir) {
89452
- const normalizedWorkingDir = normalizeWorkingDirForLookup(workingDir);
89453
- const previousManifest = await loadWorkspaceSyncManifest(session2.machineId, normalizedWorkingDir);
89454
- const tree = await scanFileTree(normalizedWorkingDir);
91322
+ function toDeltaOperations(delta) {
91323
+ return [
91324
+ ...delta.added.map((entry) => ({
91325
+ operation: "add",
91326
+ path: entry.path,
91327
+ entryType: entry.type
91328
+ })),
91329
+ ...delta.removed.map((entryPath) => ({
91330
+ operation: "remove",
91331
+ path: entryPath
91332
+ })),
91333
+ ...delta.typeChanged.map((entry) => ({
91334
+ operation: "type-change",
91335
+ path: entry.path,
91336
+ entryType: entry.type
91337
+ }))
91338
+ ];
91339
+ }
91340
+ async function publishCheckpoint(session2, normalizedWorkingDir, tree, revision) {
89455
91341
  const dataHash = computeFileTreeDataHash(tree);
89456
- if (previousManifest?.dataHash === dataHash) {
89457
- await session2.backend.mutation(api.workspaceFiles.fulfillFileTreeRequest, {
91342
+ const syncGeneration = randomUUID8();
91343
+ const snapshot = await syncScannedFileTree(session2, normalizedWorkingDir, tree, dataHash, syncGeneration);
91344
+ let checkpointRevision = revision;
91345
+ let result = await session2.backend.mutation(api.workspaceFiles.publishFileTreeCheckpoint, {
91346
+ sessionId: session2.sessionId,
91347
+ machineId: session2.machineId,
91348
+ workingDir: normalizedWorkingDir,
91349
+ revision: checkpointRevision,
91350
+ ...snapshot
91351
+ });
91352
+ if (result.status === "resync-required") {
91353
+ checkpointRevision = result.expectedRevision + 1;
91354
+ result = await session2.backend.mutation(api.workspaceFiles.publishFileTreeCheckpoint, {
89458
91355
  sessionId: session2.sessionId,
89459
91356
  machineId: session2.machineId,
89460
- workingDir: normalizedWorkingDir
91357
+ workingDir: normalizedWorkingDir,
91358
+ revision: checkpointRevision,
91359
+ ...snapshot
89461
91360
  });
89462
- console.log(`[${formatTimestamp()}] \uD83C\uDF33 File tree unchanged, skipped upload: ${normalizedWorkingDir}`);
89463
- return;
89464
91361
  }
89465
- const pathDiff = diffPathIndexes(previousManifest?.paths, buildPathIndex(tree.entries));
89466
- if (previousManifest) {
89467
- console.log(`[${formatTimestamp()}] \uD83C\uDF33 File tree diff: ${formatPathDiffSummary(pathDiff)} (${normalizedWorkingDir})`);
91362
+ if (result.status === "snapshot-missing") {
91363
+ throw new Error(`File tree checkpoint rejected: ${result.status}`);
89468
91364
  }
89469
- const scanner = await isGitRepo(normalizedWorkingDir) ? "git" : "filesystem";
89470
- const manifest = createManifestFromTree({
89471
- machineId: session2.machineId,
89472
- workingDir: normalizedWorkingDir,
89473
- scanner,
89474
- dataHash,
89475
- tree
89476
- });
89477
- await syncScannedFileTree(session2, normalizedWorkingDir, tree, dataHash, manifest.syncGeneration);
89478
- await session2.backend.mutation(api.workspaceFiles.fulfillFileTreeRequest, {
89479
- sessionId: session2.sessionId,
89480
- machineId: session2.machineId,
89481
- workingDir: normalizedWorkingDir
89482
- });
89483
- await saveWorkspaceSyncManifest(manifest);
91365
+ console.log(`[${formatTimestamp()}] \uD83C\uDF33 File tree checkpoint: ${normalizedWorkingDir} (${tree.entries.length} entries, revision ${checkpointRevision})`);
91366
+ return { revision: checkpointRevision };
89484
91367
  }
89485
91368
  var startFileTreeSubscriptionEffect = (wsClient2) => exports_Effect.gen(function* () {
89486
91369
  const session2 = yield* DaemonSessionService;
91370
+ const coordinators = new Map;
91371
+ const ensureCoordinator = (workingDir, forceReconcile) => {
91372
+ const normalized = normalizeWorkingDirForLookup(workingDir);
91373
+ let coordinatorPromise = coordinators.get(normalized);
91374
+ if (!coordinatorPromise) {
91375
+ coordinatorPromise = startWorkspaceFileTreeCoordinator({
91376
+ machineId: session2.machineId,
91377
+ workingDir: normalized,
91378
+ onDelta: async (delta, baseRevision) => {
91379
+ const operations = toDeltaOperations(delta);
91380
+ const result = await session2.backend.mutation(api.workspaceFiles.applyFileTreeDeltaBatch, {
91381
+ sessionId: session2.sessionId,
91382
+ machineId: session2.machineId,
91383
+ workingDir: normalized,
91384
+ operationId: delta.operationId,
91385
+ baseRevision,
91386
+ operations
91387
+ });
91388
+ if (result.status === "resync-required") {
91389
+ return { status: "conflict", revision: result.expectedRevision };
91390
+ }
91391
+ console.log(`[${formatTimestamp()}] \uD83C\uDF33 File tree delta: ${normalized} (${operations.length} operations, ${Buffer.byteLength(JSON.stringify(operations))} bytes, revision ${result.revision})`);
91392
+ return result;
91393
+ },
91394
+ onCheckpoint: (tree, revision) => publishCheckpoint(session2, normalized, tree, revision),
91395
+ onError: (error) => logSubscriptionWarn(`File tree coordinator failed for ${normalized}`, error),
91396
+ onReconciled: (correctedPathCount) => {
91397
+ console.log(`[${formatTimestamp()}] \uD83C\uDF33 File tree reconciled: ${normalized} (${correctedPathCount} corrected paths)`);
91398
+ }
91399
+ }).catch((error) => {
91400
+ coordinators.delete(normalized);
91401
+ throw error;
91402
+ });
91403
+ coordinators.set(normalized, coordinatorPromise);
91404
+ }
91405
+ return coordinatorPromise.then(async (coordinator) => {
91406
+ const checkpoint = await session2.backend.query(api.workspaceFiles.getFileTreeCheckpoint, {
91407
+ sessionId: session2.sessionId,
91408
+ machineId: session2.machineId,
91409
+ workingDir: normalized
91410
+ });
91411
+ if (checkpoint === null)
91412
+ await coordinator.checkpoint();
91413
+ if (forceReconcile)
91414
+ await coordinator.reconcile();
91415
+ return coordinator;
91416
+ });
91417
+ };
89487
91418
  const unsubscribe = wsClient2.onUpdate(api.workspaceFiles.getPendingFileTreeRequests, { sessionId: session2.sessionId, machineId: session2.machineId }, (requests) => {
89488
91419
  if (!requests?.length)
89489
91420
  return;
89490
- const uniqueDirs = [
89491
- ...new Set(requests.map((r) => normalizeWorkingDirForLookup(r.workingDir)))
89492
- ];
89493
- for (const workingDir of uniqueDirs) {
89494
- const normalized = normalizeWorkingDirForLookup(workingDir);
89495
- enqueueFileTreeSync(session2.machineId, normalized, () => {
89496
- const start3 = Date.now();
89497
- return uploadFileTree(session2, normalized).then(() => {
89498
- console.log(`[${formatTimestamp()}] \uD83C\uDF33 File tree fulfilled: ${normalized} (${Date.now() - start3}ms)`);
89499
- }).catch((err) => {
89500
- logSubscriptionWarn(`File tree failed for ${normalized}`, err);
89501
- });
91421
+ const requestsByDir = new Map;
91422
+ for (const request2 of requests) {
91423
+ const normalized = normalizeWorkingDirForLookup(request2.workingDir);
91424
+ requestsByDir.set(normalized, requestsByDir.get(normalized) === true || request2.force === true);
91425
+ }
91426
+ for (const [normalized, force] of requestsByDir) {
91427
+ const start3 = Date.now();
91428
+ ensureCoordinator(normalized, force).then(() => session2.backend.mutation(api.workspaceFiles.fulfillFileTreeRequest, {
91429
+ sessionId: session2.sessionId,
91430
+ machineId: session2.machineId,
91431
+ workingDir: normalized
91432
+ })).then(() => {
91433
+ console.log(`[${formatTimestamp()}] \uD83C\uDF33 File tree ready: ${normalized} (${Date.now() - start3}ms${force ? ", reconciled" : ", cached"})`);
89502
91434
  }).catch((err) => {
89503
- logSubscriptionWarn("File tree queue drain failed", err);
91435
+ logSubscriptionWarn(`File tree failed for ${normalized}`, err);
89504
91436
  });
89505
91437
  }
89506
91438
  }, (err) => {
@@ -89510,6 +91442,10 @@ var startFileTreeSubscriptionEffect = (wsClient2) => exports_Effect.gen(function
89510
91442
  return {
89511
91443
  stop: () => {
89512
91444
  unsubscribe();
91445
+ Promise.all([...coordinators.values()].map((coordinator) => coordinator.then((handle) => handle.stop()).catch(() => {
91446
+ return;
91447
+ })));
91448
+ coordinators.clear();
89513
91449
  console.log(`[${formatTimestamp()}] \uD83C\uDF33 File tree subscription stopped`);
89514
91450
  }
89515
91451
  };
@@ -89518,13 +91454,10 @@ var init_file_tree_subscription = __esm(() => {
89518
91454
  init_esm();
89519
91455
  init_daemon_services();
89520
91456
  init_api3();
89521
- init_git_reader();
89522
91457
  init_file_tree_data_hash();
89523
91458
  init_file_tree_partition();
89524
- init_file_tree_scanner();
89525
91459
  init_file_tree_v3_upload();
89526
- init_workspace_sync_queue();
89527
- init_workspace_sync_state();
91460
+ init_workspace_file_tree_coordinator();
89528
91461
  init_convex_error();
89529
91462
  });
89530
91463
 
@@ -89535,7 +91468,7 @@ function unsupportedFileWriteOperationMessage(operation) {
89535
91468
 
89536
91469
  // src/commands/machine/daemon-start/file-write-fulfillment.ts
89537
91470
  import { access as access4, mkdir as mkdir9, rename as rename5, rm as rm3, writeFile as writeFile8 } from "node:fs/promises";
89538
- import { dirname as dirname9 } from "node:path";
91471
+ import { dirname as dirname11 } from "node:path";
89539
91472
  function isTerminalFileWriteError(errorMessage) {
89540
91473
  const terminalMessages = new Set([
89541
91474
  "Invalid file path",
@@ -89587,7 +91520,7 @@ async function validateWriteOperation(operation, absolutePath) {
89587
91520
  }
89588
91521
  async function writePayloadToDisk(absolutePath, operation, content) {
89589
91522
  if (operation === "create") {
89590
- await mkdir9(dirname9(absolutePath), { recursive: true });
91523
+ await mkdir9(dirname11(absolutePath), { recursive: true });
89591
91524
  }
89592
91525
  await writeFile8(absolutePath, content);
89593
91526
  }
@@ -89650,7 +91583,7 @@ async function fulfillOneFileWriteRequest(session2, request2) {
89650
91583
  });
89651
91584
  return;
89652
91585
  }
89653
- await mkdir9(dirname9(targetResolved.absolutePath), { recursive: true });
91586
+ await mkdir9(dirname11(targetResolved.absolutePath), { recursive: true });
89654
91587
  await rename5(resolved.absolutePath, targetResolved.absolutePath);
89655
91588
  await completeWriteRequest(session2, request2._id, { status: "done" });
89656
91589
  const elapsed4 = Date.now() - startTime;
@@ -92997,11 +94930,11 @@ class AgentProcessManager {
92997
94930
  const initPrompt = await this.fetchInitPromptResult(opts, slot);
92998
94931
  if (!initPrompt.ok)
92999
94932
  return initPrompt.result;
93000
- const spawn7 = await this.spawnAgentForEnsureRunning(key, slot, opts, initPrompt, wantResume);
93001
- if (!spawn7.ok)
93002
- return spawn7.result;
93003
- await this.finalizeRunningSlot(key, slot, opts, spawn7.spawnResult, wantResume);
93004
- return { success: true, pid: spawn7.spawnResult.pid };
94933
+ const spawn6 = await this.spawnAgentForEnsureRunning(key, slot, opts, initPrompt, wantResume);
94934
+ if (!spawn6.ok)
94935
+ return spawn6.result;
94936
+ await this.finalizeRunningSlot(key, slot, opts, spawn6.spawnResult, wantResume);
94937
+ return { success: true, pid: spawn6.spawnResult.pid };
93005
94938
  } catch (e) {
93006
94939
  this.resetSlotIdle(slot);
93007
94940
  return { success: false, error: `Unexpected error: ${e.message}` };
@@ -93248,7 +95181,7 @@ var init_daemon_startup_log = __esm(() => {
93248
95181
  });
93249
95182
 
93250
95183
  // src/commands/machine/daemon-start/init.ts
93251
- import { stat as stat2 } from "node:fs/promises";
95184
+ import { stat as stat5 } from "node:fs/promises";
93252
95185
  async function discoverModelsForHarness(harness, service3) {
93253
95186
  const installed2 = await service3.isInstalled();
93254
95187
  if (!installed2) {
@@ -93283,7 +95216,7 @@ function createDefaultDeps17() {
93283
95216
  kill: (pid, signal) => process.kill(pid, signal)
93284
95217
  },
93285
95218
  fs: {
93286
- stat: stat2
95219
+ stat: stat5
93287
95220
  },
93288
95221
  machine: {
93289
95222
  clearAgentPid,
@@ -93294,7 +95227,7 @@ function createDefaultDeps17() {
93294
95227
  },
93295
95228
  clock: {
93296
95229
  now: () => Date.now(),
93297
- delay: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms))
95230
+ delay: (ms) => new Promise((resolve7) => setTimeout(resolve7, ms))
93298
95231
  },
93299
95232
  spawning: new HarnessSpawningService({ rateLimiter: new SpawnRateLimiter }),
93300
95233
  agentProcessManager: null
@@ -94655,18 +96588,18 @@ var init_jsonc = __esm(() => {
94655
96588
  });
94656
96589
 
94657
96590
  // src/infrastructure/services/workspace/workspace-resolver.ts
94658
- import { readFile as readFile10, readdir, stat as stat3 } from "node:fs/promises";
94659
- import { join as join19, basename as basename2, resolve as resolve5 } from "node:path";
96591
+ import { readFile as readFile10, readdir as readdir3, stat as stat6 } from "node:fs/promises";
96592
+ import { join as join21, basename as basename4, resolve as resolve7 } from "node:path";
94660
96593
  async function resolveGlobPatternStar(rootDir, cleaned) {
94661
- const parentDir = join19(rootDir, cleaned.slice(0, -2));
96594
+ const parentDir = join21(rootDir, cleaned.slice(0, -2));
94662
96595
  try {
94663
- const entries2 = await readdir(parentDir, { withFileTypes: true });
96596
+ const entries2 = await readdir3(parentDir, { withFileTypes: true });
94664
96597
  const dirs = [];
94665
96598
  for (const entry of entries2) {
94666
96599
  if (!entry.isDirectory())
94667
96600
  continue;
94668
- const dirPath = join19(parentDir, entry.name);
94669
- if (resolve5(dirPath).startsWith(resolve5(rootDir))) {
96601
+ const dirPath = join21(parentDir, entry.name);
96602
+ if (resolve7(dirPath).startsWith(resolve7(rootDir))) {
94670
96603
  dirs.push(dirPath);
94671
96604
  }
94672
96605
  }
@@ -94676,11 +96609,11 @@ async function resolveGlobPatternStar(rootDir, cleaned) {
94676
96609
  }
94677
96610
  }
94678
96611
  async function resolveLiteralPath(rootDir, cleaned) {
94679
- const dir = join19(rootDir, cleaned);
96612
+ const dir = join21(rootDir, cleaned);
94680
96613
  try {
94681
- if (!resolve5(dir).startsWith(resolve5(rootDir)))
96614
+ if (!resolve7(dir).startsWith(resolve7(rootDir)))
94682
96615
  return [];
94683
- const s = await stat3(dir);
96616
+ const s = await stat6(dir);
94684
96617
  if (s.isDirectory())
94685
96618
  return [dir];
94686
96619
  } catch {}
@@ -94696,10 +96629,10 @@ async function resolveGlobPattern(rootDir, pattern2) {
94696
96629
  }
94697
96630
  async function readPackageJson(dir) {
94698
96631
  try {
94699
- const content = await readFile10(join19(dir, "package.json"), "utf-8");
96632
+ const content = await readFile10(join21(dir, "package.json"), "utf-8");
94700
96633
  const pkg = JSON.parse(content);
94701
96634
  return {
94702
- name: pkg.name || basename2(dir),
96635
+ name: pkg.name || basename4(dir),
94703
96636
  scripts: pkg.scripts && typeof pkg.scripts === "object" ? pkg.scripts : {}
94704
96637
  };
94705
96638
  } catch {
@@ -94708,7 +96641,7 @@ async function readPackageJson(dir) {
94708
96641
  }
94709
96642
  async function readPnpmWorkspacePatterns(rootDir) {
94710
96643
  try {
94711
- const content = await readFile10(join19(rootDir, "pnpm-workspace.yaml"), "utf-8");
96644
+ const content = await readFile10(join21(rootDir, "pnpm-workspace.yaml"), "utf-8");
94712
96645
  const patterns = [];
94713
96646
  let inPackages = false;
94714
96647
  for (const line of content.split(`
@@ -94735,7 +96668,7 @@ async function readPnpmWorkspacePatterns(rootDir) {
94735
96668
  }
94736
96669
  async function readPackageJsonWorkspacePatterns(rootDir) {
94737
96670
  try {
94738
- const content = await readFile10(join19(rootDir, "package.json"), "utf-8");
96671
+ const content = await readFile10(join21(rootDir, "package.json"), "utf-8");
94739
96672
  const pkg = JSON.parse(content);
94740
96673
  if (!pkg.workspaces)
94741
96674
  return [];
@@ -94784,11 +96717,11 @@ var init_workspace_resolver = () => {};
94784
96717
 
94785
96718
  // src/infrastructure/services/workspace/command-discovery.ts
94786
96719
  import { access as access5, readFile as readFile11 } from "node:fs/promises";
94787
- import { join as join20, relative as relative2, basename as basename3 } from "node:path";
96720
+ import { join as join22, relative as relative4, basename as basename5 } from "node:path";
94788
96721
  async function detectPackageManager(workingDir) {
94789
96722
  for (const { file, manager } of LOCKFILE_MAP) {
94790
96723
  try {
94791
- await access5(join20(workingDir, file));
96724
+ await access5(join22(workingDir, file));
94792
96725
  return manager;
94793
96726
  } catch {}
94794
96727
  }
@@ -94856,8 +96789,8 @@ function collectRootScriptCommands(scripts, pm, scriptPrefix, rootSw) {
94856
96789
  return commands;
94857
96790
  }
94858
96791
  async function readRootPackageJson(workingDir, pm, scriptPrefix) {
94859
- let rootPackageName = basename3(workingDir);
94860
- const pkg = await readJsonFile(join20(workingDir, "package.json"), "root package.json");
96792
+ let rootPackageName = basename5(workingDir);
96793
+ const pkg = await readJsonFile(join22(workingDir, "package.json"), "root package.json");
94861
96794
  if (!pkg)
94862
96795
  return { commands: [], rootPackageName };
94863
96796
  if (pkg.name)
@@ -94873,7 +96806,7 @@ async function readRootPackageJson(workingDir, pm, scriptPrefix) {
94873
96806
  }
94874
96807
  async function readTurboJson(workingDir, _turboPrefix, _rootSubWorkspace) {
94875
96808
  const turboTaskNames = [];
94876
- const turbo = await readJsonFile(join20(workingDir, "turbo.json"), "turbo.json");
96809
+ const turbo = await readJsonFile(join22(workingDir, "turbo.json"), "turbo.json");
94877
96810
  if (!turbo?.tasks || typeof turbo.tasks !== "object")
94878
96811
  return turboTaskNames;
94879
96812
  for (const taskName of Object.keys(turbo.tasks)) {
@@ -94902,7 +96835,7 @@ async function discoverCommands(workingDir) {
94902
96835
  }
94903
96836
  const subWorkspaces = await resolveSubWorkspaces(workingDir, pm);
94904
96837
  for (const pkg of subWorkspaces) {
94905
- const wsPath = relative2(workingDir, pkg.dir) || ".";
96838
+ const wsPath = relative4(workingDir, pkg.dir) || ".";
94906
96839
  const pkgSubWorkspace = { type: "npm", path: wsPath, name: pkg.name };
94907
96840
  await addSubWorkspaceCommands(commands, pm, pkg, wsPath, pkgSubWorkspace, turboTaskNames);
94908
96841
  }
@@ -95380,10 +97313,10 @@ function assignProp(target, prop, value) {
95380
97313
  configurable: true
95381
97314
  });
95382
97315
  }
95383
- function getElementAtPath(obj, path5) {
95384
- if (!path5)
97316
+ function getElementAtPath(obj, path7) {
97317
+ if (!path7)
95385
97318
  return obj;
95386
- return path5.reduce((acc, key) => acc?.[key], obj);
97319
+ return path7.reduce((acc, key) => acc?.[key], obj);
95387
97320
  }
95388
97321
  function promiseAllObject(promisesObj) {
95389
97322
  const keys5 = Object.keys(promisesObj);
@@ -95629,11 +97562,11 @@ function aborted(x, startIndex = 0) {
95629
97562
  }
95630
97563
  return false;
95631
97564
  }
95632
- function prefixIssues(path5, issues) {
97565
+ function prefixIssues(path7, issues) {
95633
97566
  return issues.map((iss) => {
95634
97567
  var _a2;
95635
97568
  (_a2 = iss).path ?? (_a2.path = []);
95636
- iss.path.unshift(path5);
97569
+ iss.path.unshift(path7);
95637
97570
  return iss;
95638
97571
  });
95639
97572
  }
@@ -95818,7 +97751,7 @@ function treeifyError(error, _mapper) {
95818
97751
  return issue2.message;
95819
97752
  };
95820
97753
  const result = { errors: [] };
95821
- const processError = (error2, path5 = []) => {
97754
+ const processError = (error2, path7 = []) => {
95822
97755
  var _a2, _b2;
95823
97756
  for (const issue2 of error2.issues) {
95824
97757
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -95828,7 +97761,7 @@ function treeifyError(error, _mapper) {
95828
97761
  } else if (issue2.code === "invalid_element") {
95829
97762
  processError({ issues: issue2.issues }, issue2.path);
95830
97763
  } else {
95831
- const fullpath = [...path5, ...issue2.path];
97764
+ const fullpath = [...path7, ...issue2.path];
95832
97765
  if (fullpath.length === 0) {
95833
97766
  result.errors.push(mapper(issue2));
95834
97767
  continue;
@@ -95858,9 +97791,9 @@ function treeifyError(error, _mapper) {
95858
97791
  processError(error);
95859
97792
  return result;
95860
97793
  }
95861
- function toDotPath(path5) {
97794
+ function toDotPath(path7) {
95862
97795
  const segs = [];
95863
- for (const seg of path5) {
97796
+ for (const seg of path7) {
95864
97797
  if (typeof seg === "number")
95865
97798
  segs.push(`[${seg}]`);
95866
97799
  else if (typeof seg === "symbol")
@@ -106873,7 +108806,7 @@ var init_layers = __esm(() => {
106873
108806
  IntervalClock = class IntervalClock extends exports_Context.Tag("IntervalClock")() {
106874
108807
  };
106875
108808
  IntervalClockLive = exports_Layer.succeed(IntervalClock, {
106876
- sleep: (ms) => exports_Effect.promise(() => new Promise((resolve6) => setTimeout(resolve6, ms)))
108809
+ sleep: (ms) => exports_Effect.promise(() => new Promise((resolve8) => setTimeout(resolve8, ms)))
106877
108810
  });
106878
108811
  });
106879
108812
 
@@ -107849,8 +109782,8 @@ var init_command_loop = __esm(() => {
107849
109782
  const withTimeout2 = async (p, ms) => {
107850
109783
  await Promise.race([
107851
109784
  Promise.resolve(p).catch(() => {}),
107852
- new Promise((resolve6) => {
107853
- const t = setTimeout(resolve6, ms);
109785
+ new Promise((resolve8) => {
109786
+ const t = setTimeout(resolve8, ms);
107854
109787
  t.unref?.();
107855
109788
  })
107856
109789
  ]);
@@ -108004,7 +109937,7 @@ async function daemonStop() {
108004
109937
  console.log(`Stopping daemon (PID: ${pid})...`);
108005
109938
  try {
108006
109939
  process.kill(pid, "SIGTERM");
108007
- await new Promise((resolve6) => setTimeout(resolve6, 8000));
109940
+ await new Promise((resolve8) => setTimeout(resolve8, 8000));
108008
109941
  try {
108009
109942
  process.kill(pid, 0);
108010
109943
  console.log(`Process did not exit gracefully, forcing...`);
@@ -108128,7 +110061,7 @@ __export(exports_opencode_install, {
108128
110061
  installTool: () => installTool
108129
110062
  });
108130
110063
  import * as os2 from "os";
108131
- import * as path5 from "path";
110064
+ import * as path7 from "path";
108132
110065
  async function isChatroomInstalledDefault() {
108133
110066
  try {
108134
110067
  const { execSync: execSync3 } = await import("child_process");
@@ -108498,9 +110431,9 @@ After logging in, try this command again.\`;
108498
110431
  const fsService = yield* OpenCodeInstallFsService;
108499
110432
  const { checkExisting = true } = options;
108500
110433
  const homeDir = os2.homedir();
108501
- const toolDir = path5.join(homeDir, ".config", "opencode", "tool");
108502
- const toolPath = path5.join(toolDir, "chatroom.ts");
108503
- const handoffToolPath = path5.join(toolDir, "chatroom-handoff.ts");
110434
+ const toolDir = path7.join(homeDir, ".config", "opencode", "tool");
110435
+ const toolPath = path7.join(toolDir, "chatroom.ts");
110436
+ const handoffToolPath = path7.join(toolDir, "chatroom-handoff.ts");
108504
110437
  if (checkExisting) {
108505
110438
  const existingFiles = [];
108506
110439
  const toolExists = yield* fsService.access(toolPath);
@@ -108573,7 +110506,7 @@ async function withRetry(fn2, opts) {
108573
110506
  return;
108574
110507
  }
108575
110508
  const delay3 = Math.min(baseDelayMs * 2 ** attempt, maxDelayMs);
108576
- await new Promise((resolve6) => setTimeout(resolve6, delay3));
110509
+ await new Promise((resolve8) => setTimeout(resolve8, delay3));
108577
110510
  }
108578
110511
  }
108579
110512
  return;
@@ -109055,4 +110988,4 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
109055
110988
  });
109056
110989
  program2.parse();
109057
110990
 
109058
- //# debugId=0F49F7F33D425EFB64756E2164756E21
110991
+ //# debugId=D9A06559C6AC810C64756E2164756E21