@ricsam/r5d-worker 0.0.165 → 0.0.167

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.
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.165",
3
+ "version": "0.0.167",
4
4
  "type": "commonjs"
5
5
  }
@@ -20605,7 +20605,7 @@ function resolveEntrypointPath(entrypoint) {
20605
20605
  }
20606
20606
  }
20607
20607
  function getR5dctlVersion() {
20608
- if (true) return "0.0.165";
20608
+ if (true) return "0.0.167";
20609
20609
  const entrypoint = process.argv[1] ? resolveEntrypointPath(process.argv[1]) : null;
20610
20610
  let current = entrypoint ? import_node_path2.default.dirname(entrypoint) : process.cwd();
20611
20611
  for (let index = 0; index < 12; index += 1) {
package/dist/mjs/main.mjs CHANGED
@@ -7,7 +7,7 @@ import { startManagerRpc } from "./runtime/releases/rpc-main.mjs";
7
7
  import { ManagerRpcConfig } from "./runtime/releases/rpc-protocol.mjs";
8
8
  const args = process.argv.slice(2);
9
9
  if (args.includes("--version")) {
10
- console.log(`r5d-worker ${true ? "0.0.165" : "development"}`);
10
+ console.log(`r5d-worker ${true ? "0.0.167" : "development"}`);
11
11
  } else if (!args.length || args.includes("--help")) {
12
12
  console.log(
13
13
  "Usage: r5d-worker start --label <label> [--root <dir>] [--base-url <url>] [--token <worker-token>]\n r5d-worker executor /absolute/private/config.json\n r5d-worker manager /absolute/private/config.json\n\nRun independently supervised durable runtime services. Provision configuration with r5dinfra."
@@ -15,7 +15,7 @@ if (args.includes("--version")) {
15
15
  } else if (args[0] === "start") {
16
16
  const runtime = await startPersonalWorker(
17
17
  parsePersonalWorkerOptions(args.slice(1)),
18
- true ? "0.0.165" : "development"
18
+ true ? "0.0.167" : "development"
19
19
  );
20
20
  console.log(`Worker connected: ${runtime.resourceId}`);
21
21
  let closing = false;
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.165",
3
+ "version": "0.0.167",
4
4
  "type": "module"
5
5
  }
@@ -103,7 +103,7 @@ async function startPersonalWorker(options, version) {
103
103
  platform: process.platform,
104
104
  arch: process.arch,
105
105
  hostname: os.hostname(),
106
- capabilities: { updateClis: true, durableWorkspace: true, outerWorkspace: true, fileWalk: true }
106
+ capabilities: { updateClis: true, durableWorkspace: true, outerWorkspace: true, fileWalk: true, fileSearch: true }
107
107
  });
108
108
  const grant = PersonalWorkerGrant.parse(
109
109
  await request("/api/personal/resources/register", { kind: "worker", ...identity, label: options.label, metadata: metadata() })
@@ -13,6 +13,7 @@ import { privateDirectory, readPrivateJson } from "../runtime/storage.mjs";
13
13
  import { WorkspaceAuthority } from "../runtime/workspace/authority.mjs";
14
14
  import { ApprovedWorkbench, WorkspaceError } from "../runtime/workspace/contracts.mjs";
15
15
  import { OuterSnapshotRefusal } from "../runtime/workspace/outer.mjs";
16
+ import { WorkspaceSearch } from "../runtime/workspace/search.mjs";
16
17
  import { WorkspaceStorageClient } from "../runtime/workspace/storage-client.mjs";
17
18
  import { BranchName, GitOid, StorageId } from "../runtime/workspace/storage-wire.mjs";
18
19
  const PersonalWorkerGrant = z.object({
@@ -371,6 +372,7 @@ exec ${quote(executable)} ${quote(cli)} "$@"
371
372
  if (command.method === "fileHistoryDates") return authority.fileHistoryDates(identity, command.path);
372
373
  if (command.method === "directory" || command.method === "raw") return authority.inspectFiles(identity, command);
373
374
  if (command.method === "walk") return authority.walkFiles(identity, command);
375
+ if (command.method === "search") return authority.searchFiles(identity, WorkspaceSearch.parse(command));
374
376
  }
375
377
  if (request.kind === "terminal") {
376
378
  await authority.ensureHydrated(identity);
@@ -15,6 +15,7 @@ import { OuterRepository, OuterSnapshotRefusal, OUTER_BRANCH, OUTER_CONFLICT_COD
15
15
  import { SessionArtifactStore, SessionArtifactChunk, RESERVED_ARTIFACT_ENV } from "./artifacts.mjs";
16
16
  import { WorkspaceFileWrite, WorkspaceFileWriteLookup } from "./file-write.mjs";
17
17
  import { walkDirectoryTree } from "./walk.mjs";
18
+ import { searchDirectoryTree } from "./search.mjs";
18
19
  import { WorkspaceStorageClient } from "./storage-client.mjs";
19
20
  import {
20
21
  durableJson,
@@ -338,6 +339,25 @@ class WorkspaceAuthority {
338
339
  const walked = await walkDirectoryTree(target);
339
340
  return { directory: display, ...walked };
340
341
  }
342
+ /** Searches a tree (or one file) locally in one operation: the walk, every
343
+ * bounded file read and the thread-isolated match all happen here, so a
344
+ * search costs one round-trip however many files it touches. */
345
+ async searchFiles(identity, command) {
346
+ const b = await this.bench(identity);
347
+ const relative = command.hostPath ? "" : command.path.replace(/^\/+/, "").replace(/\/+$/, "");
348
+ if (!command.hostPath && relative) safeTreePath(relative);
349
+ const target = command.hostPath ? this.resolveHostPath(command.path) : path.join(b.config.cwd, relative);
350
+ const display = command.hostPath ? target : `/${relative}`;
351
+ const inspect = command.hostPath ? fs.lstat(target).then((stat) => {
352
+ if (stat.isSymbolicLink()) throw new WorkspaceError("unsafe_path", "Final symlink paths are not inspected");
353
+ }) : noSymlinkAncestors(target);
354
+ await inspect.catch((error) => {
355
+ if (error.code === "ENOENT" || error.code === "ENOTDIR") throw new WorkspaceError("not_found", "File or directory not found");
356
+ throw error;
357
+ });
358
+ const searched = await searchDirectoryTree(target, { pattern: command.pattern, caseSensitive: command.caseSensitive, glob: command.glob, limit: command.limit, hostPath: command.hostPath });
359
+ return { directory: display, ...searched };
360
+ }
341
361
  async inspectGit(identity, command) {
342
362
  const b = await this.bench(identity);
343
363
  return this.serial(b, async () => {
@@ -146,24 +146,22 @@ class OuterRepository {
146
146
  async snapshot(inners, options = {}) {
147
147
  const limit = options.limitBytes ?? OUTER_SNAPSHOT_LIMIT_BYTES;
148
148
  await this.seed(inners);
149
- const dependencies = nulSplit(
150
- await this.run(["ls-files", "--others", "--exclude-standard", "--directory", "-z", "--", ":(glob)**/node_modules/**"])
151
- );
152
- if (dependencies.length && !options.allowLargeDiff)
153
- throw new OuterSnapshotRefusal("too_large", "An unignored dependency tree exceeds the automatic publication limit", dependencies.slice(0, MAX_REPORTED_PATHS));
154
149
  const others = nulSplit(await this.run(["ls-files", "--others", "--exclude-standard", "-z"]));
155
150
  const foreign = others.filter((file) => file.endsWith("/")).map((file) => file.slice(0, -1));
151
+ const collapsed = nulSplit(await this.run(["ls-files", "--others", "--exclude-standard", "--directory", "-z"])).filter((entry) => entry.endsWith("/") && !foreign.includes(entry.slice(0, -1)));
152
+ const attribute = (paths) => [...new Set(paths.map((file) => collapsed.find((directory) => file.startsWith(directory)) ?? file))].slice(0, MAX_REPORTED_PATHS);
156
153
  const tracked = /* @__PURE__ */ new Map();
154
+ const trackedOf = async (inner) => {
155
+ let set = tracked.get(inner.root);
156
+ if (!set) tracked.set(inner.root, set = await inner.tracked());
157
+ return set;
158
+ };
157
159
  let unexplainedBytes = 0;
158
160
  const unexplainedPaths = [];
159
161
  for (const file of others) {
160
162
  if (file.endsWith("/")) continue;
161
163
  const inner = inners.find((candidate) => withinRoot(file, candidate.root));
162
- if (inner) {
163
- let set = tracked.get(inner.root);
164
- if (!set) tracked.set(inner.root, set = await inner.tracked());
165
- if (set.has(file.slice(inner.root.length + 1))) continue;
166
- }
164
+ if (inner && (await trackedOf(inner)).has(file.slice(inner.root.length + 1))) continue;
167
165
  const stat = await fs.lstat(path.join(this.workTree, file)).catch((error) => {
168
166
  if (error.code === "ENOENT" || error.code === "ENOTDIR") return null;
169
167
  throw error;
@@ -175,7 +173,7 @@ class OuterRepository {
175
173
  throw new OuterSnapshotRefusal(
176
174
  "too_large",
177
175
  `Unexplained workspace content exceeds the ${limit}-byte automatic publication limit`,
178
- unexplainedPaths.slice(0, MAX_REPORTED_PATHS)
176
+ attribute(unexplainedPaths)
179
177
  );
180
178
  }
181
179
  for (const file of unexplainedPaths) {
@@ -192,9 +190,40 @@ class OuterRepository {
192
190
  }
193
191
  await this.run(["add", "-A", "--", ".", ...foreign.map((directory) => `:(exclude,literal)${directory}`)]);
194
192
  await this.stripUnsupported();
193
+ await this.evictIgnored(inners, trackedOf);
195
194
  const tree = GitOid.parse((await this.run(["write-tree"])).toString("utf8").trim());
196
195
  return { tree, unexplainedBytes, unexplainedPaths };
197
196
  }
197
+ /** Under a checkout the outer tree holds exactly what the checkout tracks plus
198
+ * what it shows as untracked-and-not-ignored. Staging never drops a tracked
199
+ * file that has since become ignored — npm's cache, published by a cycle that
200
+ * raced `npm install` while it was still under the limit, then excluded by a
201
+ * restored `.gitignore` — so such files are removed from the index here. The
202
+ * working tree is untouched; a fixed ignore file simply cleans what was
203
+ * published. The seeding invariant holds: a checkout never loses its last
204
+ * outer entry. */
205
+ async evictIgnored(inners, trackedOf) {
206
+ const cached = nulSplit(await this.run(["ls-files", "--cached", "-z"]));
207
+ const tracked = /* @__PURE__ */ new Map(), untracked = /* @__PURE__ */ new Map();
208
+ for (const file of cached) {
209
+ const inner = inners.find((candidate) => withinRoot(file, candidate.root));
210
+ if (!inner) continue;
211
+ if ((await trackedOf(inner)).has(file.slice(inner.root.length + 1))) tracked.set(inner.root, (tracked.get(inner.root) ?? 0) + 1);
212
+ else untracked.set(inner.root, [...untracked.get(inner.root) ?? [], file]);
213
+ }
214
+ const paths = [...untracked.values()].flat();
215
+ if (!paths.length) return;
216
+ const { code, stdout } = await this.result(["check-ignore", "--no-index", "-z", "--stdin"], `${paths.join("\0")}\0`);
217
+ if (code !== 0 && code !== 1) throw new WorkspaceError("git_failed", "Outer repository ignore rules are unreadable");
218
+ const ignored = new Set(nulSplit(stdout));
219
+ const removals = [];
220
+ for (const [root, files] of untracked) {
221
+ const drop = files.filter((file) => ignored.has(file));
222
+ const survivors = (tracked.get(root) ?? 0) + files.length - drop.length;
223
+ removals.push(...survivors ? drop : drop.sort().slice(1));
224
+ }
225
+ if (removals.length) await this.run(["update-index", "--force-remove", "-z", "--stdin"], `${removals.join("\0")}\0`);
226
+ }
198
227
  /** Staging records a symlink as a link entry, and a nested repository that
199
228
  * appeared between the walk and the add as a gitlink. Neither can be
200
229
  * materialized on another host; both are dropped from the index without
@@ -0,0 +1,120 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { Worker } from "node:worker_threads";
4
+ import picomatch from "picomatch";
5
+ import { z } from "zod";
6
+ import { WorkspaceError } from "./contracts.mjs";
7
+ import { readHostRegular, readRegular } from "./files.mjs";
8
+ import { walkDirectoryTree, WORKSPACE_WALK_IGNORED, WORKSPACE_WALK_LIMITS } from "./walk.mjs";
9
+ const WORKSPACE_SEARCH_LIMITS = Object.freeze({
10
+ bytes: 8 * 1024 * 1024,
11
+ matches: 1e3,
12
+ outputBytes: 5e4,
13
+ lineChars: 2e3,
14
+ matchTimeoutMs: 1e3
15
+ });
16
+ const WorkspaceSearch = z.object({
17
+ method: z.literal("search"),
18
+ path: z.string().max(4096),
19
+ pattern: z.string().min(1).max(1e3),
20
+ caseSensitive: z.boolean().optional(),
21
+ glob: z.string().max(1e3).optional(),
22
+ limit: z.number().int().min(1).optional(),
23
+ hostPath: z.boolean().optional()
24
+ }).strict();
25
+ function globMatcher(glob) {
26
+ return glob.includes("/") ? picomatch(glob, { dot: true }) : picomatch(glob, { basename: true, dot: true });
27
+ }
28
+ function compileSearchPattern(pattern, caseSensitive) {
29
+ let source = pattern;
30
+ if (source.startsWith("(?i)")) {
31
+ source = source.slice(4);
32
+ caseSensitive = false;
33
+ }
34
+ if (!source) throw new WorkspaceError("invalid_pattern", "Invalid regular expression", true);
35
+ try {
36
+ new RegExp(source, caseSensitive ? "" : "i");
37
+ } catch {
38
+ throw new WorkspaceError("invalid_pattern", "Invalid regular expression", true);
39
+ }
40
+ return { source, caseSensitive };
41
+ }
42
+ function decodeSearchText(bytes) {
43
+ if (bytes.includes(0)) return null;
44
+ try {
45
+ return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
46
+ } catch {
47
+ return null;
48
+ }
49
+ }
50
+ function matchTextFiles(files, pattern, caseSensitive, maximum, timeoutMs = WORKSPACE_SEARCH_LIMITS.matchTimeoutMs) {
51
+ const compiled = compileSearchPattern(pattern, caseSensitive);
52
+ const limits = { maximum: Math.max(1, Math.min(maximum, WORKSPACE_SEARCH_LIMITS.matches)), outputBytes: WORKSPACE_SEARCH_LIMITS.outputBytes, lineChars: WORKSPACE_SEARCH_LIMITS.lineChars };
53
+ return new Promise((resolve, reject) => {
54
+ const worker = new Worker(
55
+ "const {parentPort,workerData}=require('node:worker_threads');const r=new RegExp(workerData.source,workerData.caseSensitive?'':'i');const matches=[];let truncated=false,bytes=0;outer:for(const file of workerData.files){let n=0;for(const line of file.text.split('\\n')){n++;if(!r.test(line))continue;const text=line.slice(0,workerData.lineChars),size=file.path.length+text.length+8;if(matches.length>=workerData.maximum||bytes+size>workerData.outputBytes){truncated=true;break outer}matches.push({path:file.path,line:n,text});bytes+=size}}parentPort.postMessage({matches,truncated})",
56
+ { eval: true, workerData: { files, source: compiled.source, caseSensitive: compiled.caseSensitive, ...limits } }
57
+ );
58
+ const timer = setTimeout(() => {
59
+ void worker.terminate();
60
+ reject(new WorkspaceError("pattern_budget", "Search pattern exceeded its execution budget; narrow the pattern", true));
61
+ }, timeoutMs);
62
+ worker.once("message", (value) => {
63
+ clearTimeout(timer);
64
+ void worker.terminate();
65
+ resolve(value);
66
+ });
67
+ worker.once("error", (error) => {
68
+ clearTimeout(timer);
69
+ void worker.terminate();
70
+ reject(error);
71
+ });
72
+ });
73
+ }
74
+ async function searchDirectoryTree(root, options) {
75
+ const compiled = compileSearchPattern(options.pattern, options.caseSensitive ?? true);
76
+ const limits = options.limits ?? WORKSPACE_SEARCH_LIMITS, read = options.hostPath ? readHostRegular : readRegular;
77
+ const stat = await fs.stat(root);
78
+ let candidates, truncated = false;
79
+ if (stat.isFile()) candidates = [{ path: "", size: stat.size }];
80
+ else {
81
+ const matches = options.glob ? globMatcher(options.glob) : () => true;
82
+ const walked = await walkDirectoryTree(root, { ignored: options.ignored ?? WORKSPACE_WALK_IGNORED, limits: WORKSPACE_WALK_LIMITS });
83
+ truncated = walked.truncated;
84
+ candidates = walked.entries.filter((entry) => entry.type === "file" && matches(entry.path));
85
+ }
86
+ const files = [];
87
+ let budget = limits.bytes;
88
+ for (const candidate of candidates) {
89
+ if ((candidate.size ?? 0) > budget) {
90
+ truncated = true;
91
+ continue;
92
+ }
93
+ let bytes;
94
+ try {
95
+ bytes = await read(candidate.path ? path.join(root, candidate.path) : root, budget);
96
+ } catch (error) {
97
+ const code = error.code;
98
+ if (code === "ENOENT" || code === "ENOTDIR") continue;
99
+ if (error instanceof WorkspaceError) {
100
+ truncated = true;
101
+ continue;
102
+ }
103
+ throw error;
104
+ }
105
+ budget -= bytes.length;
106
+ const text = decodeSearchText(bytes);
107
+ if (text !== null) files.push({ path: candidate.path, text });
108
+ }
109
+ const matched = await matchTextFiles(files, compiled.source, compiled.caseSensitive, options.limit ?? limits.matches, limits.matchTimeoutMs);
110
+ return { matches: matched.matches, truncated: truncated || matched.truncated };
111
+ }
112
+ export {
113
+ WORKSPACE_SEARCH_LIMITS,
114
+ WorkspaceSearch,
115
+ compileSearchPattern,
116
+ decodeSearchText,
117
+ globMatcher,
118
+ matchTextFiles,
119
+ searchDirectoryTree
120
+ };
@@ -4,6 +4,7 @@ import { WorkspaceConfig, type ExecutorRoute, type WorkspaceIdentity, type Outer
4
4
  import { SessionArtifactChunk } from "./artifacts";
5
5
  import { WorkspaceFileWrite } from "./file-write";
6
6
  import { type WorkspaceWalk, type WorkspaceWalkResult } from "./walk";
7
+ import { type WorkspaceSearch, type WorkspaceSearchResult } from "./search";
7
8
  import { WorkspaceStorageClient } from "./storage-client";
8
9
  export interface WorkspaceAuthorityOptions {
9
10
  config: WorkspaceConfig;
@@ -112,6 +113,10 @@ export declare class WorkspaceAuthority {
112
113
  * relative to the walked directory; the shared ignore list and bounds live
113
114
  * in `./walk` so the server's fallback agrees with them. */
114
115
  walkFiles(identity: WorkspaceIdentity, command: WorkspaceWalk): Promise<WorkspaceWalkResult>;
116
+ /** Searches a tree (or one file) locally in one operation: the walk, every
117
+ * bounded file read and the thread-isolated match all happen here, so a
118
+ * search costs one round-trip however many files it touches. */
119
+ searchFiles(identity: WorkspaceIdentity, command: WorkspaceSearch): Promise<WorkspaceSearchResult>;
115
120
  inspectGit(identity: WorkspaceIdentity, command: {
116
121
  method: "status" | "diff" | "history" | "fileDiff";
117
122
  path?: string;
@@ -75,6 +75,15 @@ export declare class OuterRepository {
75
75
  allowLargeDiff?: boolean;
76
76
  limitBytes?: number;
77
77
  }): Promise<OuterSnapshot>;
78
+ /** Under a checkout the outer tree holds exactly what the checkout tracks plus
79
+ * what it shows as untracked-and-not-ignored. Staging never drops a tracked
80
+ * file that has since become ignored — npm's cache, published by a cycle that
81
+ * raced `npm install` while it was still under the limit, then excluded by a
82
+ * restored `.gitignore` — so such files are removed from the index here. The
83
+ * working tree is untouched; a fixed ignore file simply cleans what was
84
+ * published. The seeding invariant holds: a checkout never loses its last
85
+ * outer entry. */
86
+ private evictIgnored;
78
87
  /** Staging records a symlink as a link entry, and a nested repository that
79
88
  * appeared between the walk and the add as a gitlink. Neither can be
80
89
  * materialized on another host; both are dropped from the index without
@@ -0,0 +1,81 @@
1
+ import { z } from "zod";
2
+ export type WorkspaceSearchLimits = {
3
+ /** Bytes of file content one search may read in total. */
4
+ readonly bytes: number;
5
+ /** Match lines reported before the result is marked truncated. */
6
+ readonly matches: number;
7
+ /** Bytes of reported match text. */
8
+ readonly outputBytes: number;
9
+ /** Characters kept from a matching line. */
10
+ readonly lineChars: number;
11
+ /** Wall-clock budget for regular-expression execution. */
12
+ readonly matchTimeoutMs: number;
13
+ };
14
+ /** Bounds one text search regardless of what a checkout contains. */
15
+ export declare const WORKSPACE_SEARCH_LIMITS: WorkspaceSearchLimits;
16
+ /** Inspect command: search text files under a path in one worker operation.
17
+ * `glob` matches paths relative to `path` (a glob without a slash matches
18
+ * base names). `limit` is clamped to the shared match bound. */
19
+ export declare const WorkspaceSearch: z.ZodObject<{
20
+ method: z.ZodLiteral<"search">;
21
+ path: z.ZodString;
22
+ pattern: z.ZodString;
23
+ caseSensitive: z.ZodOptional<z.ZodBoolean>;
24
+ glob: z.ZodOptional<z.ZodString>;
25
+ limit: z.ZodOptional<z.ZodNumber>;
26
+ hostPath: z.ZodOptional<z.ZodBoolean>;
27
+ }, z.core.$strict>;
28
+ export type WorkspaceSearch = z.infer<typeof WorkspaceSearch>;
29
+ /** A match's `path` is relative to the searched directory, or empty when the
30
+ * searched path was itself a file. */
31
+ export type WorkspaceSearchMatch = {
32
+ path: string;
33
+ line: number;
34
+ text: string;
35
+ };
36
+ export type WorkspaceSearchResult = {
37
+ directory: string;
38
+ matches: WorkspaceSearchMatch[];
39
+ truncated: boolean;
40
+ };
41
+ /** A glob without a slash matches base names anywhere under the searched
42
+ * directory; one with a slash matches the whole relative path. picomatch's
43
+ * `basename` option silently fails slash patterns, so it is applied only to
44
+ * slash-free globs. */
45
+ export declare function globMatcher(glob: string): (relative: string) => boolean;
46
+ /** Validates a search pattern before any file is touched. A leading `(?i)`,
47
+ * the inline flag models trained on ripgrep and PCRE emit, is folded into
48
+ * case-insensitivity because JavaScript has no inline flag syntax. */
49
+ export declare function compileSearchPattern(pattern: string, caseSensitive: boolean): {
50
+ source: string;
51
+ caseSensitive: boolean;
52
+ };
53
+ /** Decodes a file as UTF-8 text, or returns null for binary or non-UTF-8 content. */
54
+ export declare function decodeSearchText(bytes: Buffer): string | null;
55
+ /** Runs the regular expression in a disposable thread so a hostile pattern
56
+ * cannot stall the process's event loop; the thread is terminated at the
57
+ * budget. `maximum` bounds reported matches. */
58
+ export declare function matchTextFiles(files: Array<{
59
+ path: string;
60
+ text: string;
61
+ }>, pattern: string, caseSensitive: boolean, maximum: number, timeoutMs?: number): Promise<{
62
+ matches: WorkspaceSearchMatch[];
63
+ truncated: boolean;
64
+ }>;
65
+ /** Searches the tree under `root` (or `root` itself when it is a file) in
66
+ * process: one walk with the shared ignore list, bounded reads of the files
67
+ * the glob selects, and one thread-isolated match. Files that vanish, exceed
68
+ * the remaining byte budget, or are not UTF-8 text are skipped; the first two
69
+ * mark the result truncated. */
70
+ export declare function searchDirectoryTree(root: string, options: {
71
+ pattern: string;
72
+ caseSensitive?: boolean;
73
+ glob?: string;
74
+ limit?: number;
75
+ hostPath?: boolean;
76
+ ignored?: ReadonlySet<string>;
77
+ limits?: WorkspaceSearchLimits;
78
+ }): Promise<{
79
+ matches: WorkspaceSearchMatch[];
80
+ truncated: boolean;
81
+ }>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.165",
3
+ "version": "0.0.167",
4
4
  "type": "module",
5
5
  "main": "./dist/mjs/main.mjs",
6
6
  "module": "./dist/mjs/main.mjs",
@@ -21,7 +21,7 @@
21
21
  "r5d-worker": "dist/mjs/main.mjs"
22
22
  },
23
23
  "dependencies": {
24
- "@ricsam/r5d-api": "^0.0.165",
24
+ "@ricsam/r5d-api": "^0.0.167",
25
25
  "node-pty": "1.1.0",
26
26
  "zod": "^4.1.13",
27
27
  "picomatch": "^4.0.3"