@soederpop/luca 0.0.6 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/CLAUDE.md +10 -1
  2. package/RUNME.md +56 -0
  3. package/bun.lock +1 -1
  4. package/commands/build-bootstrap.ts +78 -0
  5. package/commands/build-scaffolds.ts +24 -2
  6. package/commands/try-all-challenges.ts +543 -0
  7. package/commands/try-challenge.ts +100 -0
  8. package/docs/README.md +52 -80
  9. package/docs/TABLE-OF-CONTENTS.md +82 -51
  10. package/docs/apis/clients/elevenlabs.md +232 -8
  11. package/docs/apis/clients/graph.md +59 -8
  12. package/docs/apis/clients/openai.md +362 -2
  13. package/docs/apis/clients/rest.md +122 -2
  14. package/docs/apis/clients/websocket.md +71 -17
  15. package/docs/apis/features/agi/assistant.md +9 -3
  16. package/docs/apis/features/agi/assistants-manager.md +2 -2
  17. package/docs/apis/features/agi/claude-code.md +153 -14
  18. package/docs/apis/features/agi/conversation-history.md +15 -3
  19. package/docs/apis/features/agi/conversation.md +133 -20
  20. package/docs/apis/features/agi/openai-codex.md +90 -12
  21. package/docs/apis/features/agi/skills-library.md +23 -5
  22. package/docs/apis/features/node/container-link.md +59 -0
  23. package/docs/apis/features/node/content-db.md +1 -1
  24. package/docs/apis/features/node/disk-cache.md +1 -1
  25. package/docs/apis/features/node/dns.md +1 -0
  26. package/docs/apis/features/node/docker.md +2 -1
  27. package/docs/apis/features/node/esbuild.md +4 -3
  28. package/docs/apis/features/node/file-manager.md +13 -4
  29. package/docs/apis/features/node/fs.md +726 -171
  30. package/docs/apis/features/node/git.md +1 -0
  31. package/docs/apis/features/node/google-auth.md +23 -4
  32. package/docs/apis/features/node/google-calendar.md +14 -2
  33. package/docs/apis/features/node/google-docs.md +15 -2
  34. package/docs/apis/features/node/google-drive.md +21 -3
  35. package/docs/apis/features/node/google-sheets.md +14 -2
  36. package/docs/apis/features/node/grep.md +2 -0
  37. package/docs/apis/features/node/helpers.md +29 -0
  38. package/docs/apis/features/node/ink.md +2 -2
  39. package/docs/apis/features/node/networking.md +39 -4
  40. package/docs/apis/features/node/os.md +28 -0
  41. package/docs/apis/features/node/postgres.md +26 -4
  42. package/docs/apis/features/node/proc.md +37 -28
  43. package/docs/apis/features/node/process-manager.md +33 -5
  44. package/docs/apis/features/node/repl.md +1 -1
  45. package/docs/apis/features/node/runpod.md +1 -0
  46. package/docs/apis/features/node/secure-shell.md +7 -0
  47. package/docs/apis/features/node/semantic-search.md +12 -5
  48. package/docs/apis/features/node/sqlite.md +26 -4
  49. package/docs/apis/features/node/telegram.md +30 -5
  50. package/docs/apis/features/node/tts.md +17 -2
  51. package/docs/apis/features/node/ui.md +1 -1
  52. package/docs/apis/features/node/vault.md +4 -9
  53. package/docs/apis/features/node/vm.md +3 -12
  54. package/docs/apis/features/node/window-manager.md +128 -20
  55. package/docs/apis/features/web/asset-loader.md +13 -1
  56. package/docs/apis/features/web/container-link.md +59 -0
  57. package/docs/apis/features/web/esbuild.md +4 -3
  58. package/docs/apis/features/web/helpers.md +29 -0
  59. package/docs/apis/features/web/network.md +16 -2
  60. package/docs/apis/features/web/speech.md +16 -2
  61. package/docs/apis/features/web/vault.md +4 -9
  62. package/docs/apis/features/web/vm.md +3 -12
  63. package/docs/apis/features/web/voice.md +18 -1
  64. package/docs/apis/servers/express.md +18 -2
  65. package/docs/apis/servers/mcp.md +29 -4
  66. package/docs/apis/servers/websocket.md +34 -6
  67. package/docs/bootstrap/CLAUDE.md +100 -0
  68. package/docs/bootstrap/SKILL.md +222 -0
  69. package/docs/bootstrap/templates/about-command.ts +41 -0
  70. package/docs/bootstrap/templates/docs-models.ts +22 -0
  71. package/docs/bootstrap/templates/docs-readme.md +43 -0
  72. package/docs/bootstrap/templates/example-feature.ts +53 -0
  73. package/docs/bootstrap/templates/health-endpoint.ts +15 -0
  74. package/docs/bootstrap/templates/luca-cli.ts +25 -0
  75. package/docs/bootstrap/templates/runme.md +54 -0
  76. package/docs/challenges/caching-proxy.md +16 -0
  77. package/docs/challenges/content-db-round-trip.md +14 -0
  78. package/docs/challenges/custom-command.md +9 -0
  79. package/docs/challenges/file-watcher-pipeline.md +11 -0
  80. package/docs/challenges/grep-audit-report.md +15 -0
  81. package/docs/challenges/multi-feature-dashboard.md +14 -0
  82. package/docs/challenges/process-orchestrator.md +17 -0
  83. package/docs/challenges/rest-api-server-with-client.md +12 -0
  84. package/docs/challenges/script-runner-with-vm.md +11 -0
  85. package/docs/challenges/simple-rest-api.md +15 -0
  86. package/docs/challenges/websocket-serve-and-client.md +11 -0
  87. package/docs/challenges/yaml-config-system.md +14 -0
  88. package/docs/command-system-overhaul.md +94 -0
  89. package/docs/examples/assistant/CORE.md +18 -0
  90. package/docs/examples/assistant/hooks.ts +3 -0
  91. package/docs/examples/assistant/tools.ts +10 -0
  92. package/docs/examples/window-manager-layouts.md +180 -0
  93. package/docs/in-memory-fs.md +4 -0
  94. package/docs/models.ts +13 -10
  95. package/docs/philosophy.md +4 -3
  96. package/docs/reports/console-hmr-design.md +170 -0
  97. package/docs/reports/helper-semantic-search.md +72 -0
  98. package/docs/scaffolds/client.md +29 -20
  99. package/docs/scaffolds/command.md +64 -50
  100. package/docs/scaffolds/endpoint.md +31 -36
  101. package/docs/scaffolds/feature.md +28 -18
  102. package/docs/scaffolds/selector.md +91 -0
  103. package/docs/scaffolds/server.md +18 -9
  104. package/docs/selectors.md +115 -0
  105. package/docs/sessions/custom-command/attempt-log-2.md +195 -0
  106. package/docs/sessions/file-watcher-pipeline/attempt-log-1.md +728 -0
  107. package/docs/sessions/file-watcher-pipeline/attempt-log-2.md +555 -0
  108. package/docs/sessions/grep-audit-report/attempt-log-1.md +289 -0
  109. package/docs/sessions/multi-feature-dashboard/attempt-log-2.md +679 -0
  110. package/docs/sessions/rest-api-server-with-client/attempt-log-1.md +1 -0
  111. package/docs/sessions/rest-api-server-with-client/attempt-log-3.md +920 -0
  112. package/docs/sessions/simple-rest-api/attempt-log-1.md +593 -0
  113. package/docs/sessions/websocket-serve-and-client/attempt-log-2.md +995 -0
  114. package/docs/tutorials/00-bootstrap.md +148 -0
  115. package/docs/tutorials/07-endpoints.md +7 -7
  116. package/docs/tutorials/08-commands.md +153 -72
  117. package/luca.cli.ts +3 -0
  118. package/package.json +6 -5
  119. package/public/index.html +1430 -0
  120. package/scripts/examples/using-ollama.ts +2 -1
  121. package/scripts/update-introspection-data.ts +2 -2
  122. package/src/agi/endpoints/experts.ts +1 -1
  123. package/src/agi/features/assistant.ts +7 -0
  124. package/src/agi/features/assistants-manager.ts +5 -5
  125. package/src/agi/features/claude-code.ts +263 -3
  126. package/src/agi/features/conversation-history.ts +7 -1
  127. package/src/agi/features/conversation.ts +26 -3
  128. package/src/agi/features/openai-codex.ts +26 -2
  129. package/src/agi/features/openapi.ts +6 -1
  130. package/src/agi/features/skills-library.ts +9 -1
  131. package/src/bootstrap/generated.ts +595 -0
  132. package/src/cli/cli.ts +64 -21
  133. package/src/client.ts +23 -357
  134. package/src/clients/civitai/index.ts +1 -1
  135. package/src/clients/client-template.ts +1 -1
  136. package/src/clients/comfyui/index.ts +13 -2
  137. package/src/clients/elevenlabs/index.ts +2 -1
  138. package/src/clients/graph.ts +87 -0
  139. package/src/clients/openai/index.ts +10 -1
  140. package/src/clients/rest.ts +207 -0
  141. package/src/clients/websocket.ts +176 -0
  142. package/src/command.ts +281 -34
  143. package/src/commands/bootstrap.ts +185 -0
  144. package/src/commands/chat.ts +5 -4
  145. package/src/commands/describe.ts +341 -4
  146. package/src/commands/help.ts +35 -9
  147. package/src/commands/index.ts +3 -0
  148. package/src/commands/introspect.ts +92 -2
  149. package/src/commands/prompt.ts +5 -6
  150. package/src/commands/run.ts +75 -10
  151. package/src/commands/save-api-docs.ts +49 -0
  152. package/src/commands/scaffold.ts +169 -23
  153. package/src/commands/select.ts +94 -0
  154. package/src/commands/serve.ts +10 -1
  155. package/src/container.ts +15 -0
  156. package/src/endpoint.ts +19 -0
  157. package/src/graft.ts +181 -0
  158. package/src/introspection/generated.agi.ts +12458 -8968
  159. package/src/introspection/generated.node.ts +10573 -7145
  160. package/src/introspection/generated.web.ts +1 -1
  161. package/src/introspection/index.ts +26 -0
  162. package/src/node/container.ts +6 -7
  163. package/src/node/features/content-db.ts +49 -2
  164. package/src/node/features/disk-cache.ts +16 -9
  165. package/src/node/features/dns.ts +16 -3
  166. package/src/node/features/docker.ts +16 -4
  167. package/src/node/features/esbuild.ts +22 -2
  168. package/src/node/features/file-manager.ts +184 -29
  169. package/src/node/features/fs.ts +704 -248
  170. package/src/node/features/git.ts +21 -8
  171. package/src/node/features/grep.ts +23 -3
  172. package/src/node/features/helpers.ts +372 -43
  173. package/src/node/features/networking.ts +39 -4
  174. package/src/node/features/opener.ts +28 -15
  175. package/src/node/features/os.ts +76 -0
  176. package/src/node/features/port-exposer.ts +11 -1
  177. package/src/node/features/postgres.ts +17 -1
  178. package/src/node/features/proc.ts +4 -1
  179. package/src/node/features/python.ts +63 -14
  180. package/src/node/features/repl.ts +11 -7
  181. package/src/node/features/runpod.ts +16 -3
  182. package/src/node/features/secure-shell.ts +27 -2
  183. package/src/node/features/semantic-search.ts +12 -1
  184. package/src/node/features/ui.ts +5 -69
  185. package/src/node/features/vm.ts +17 -0
  186. package/src/node/features/window-manager.ts +68 -20
  187. package/src/node.ts +5 -0
  188. package/src/scaffolds/generated.ts +492 -290
  189. package/src/scaffolds/template.ts +9 -0
  190. package/src/schemas/base.ts +46 -5
  191. package/src/selector.ts +282 -0
  192. package/src/server.ts +11 -0
  193. package/src/servers/express.ts +27 -12
  194. package/src/servers/socket.ts +45 -11
  195. package/src/web/clients/socket.ts +4 -1
  196. package/src/web/container.ts +2 -1
  197. package/src/web/features/network.ts +7 -1
  198. package/src/web/features/voice-recognition.ts +16 -1
  199. package/test/clients-servers.test.ts +2 -1
  200. package/test/command.test.ts +267 -0
  201. package/test/vm-context.test.ts +146 -0
  202. package/test-integration/assistants-manager.test.ts +10 -20
  203. package/docs/apis/features/node/launcher-app-command-listener.md +0 -145
  204. package/docs/examples/launcher-app-command-listener.md +0 -120
  205. package/docs/tasks/web-container-helper-discovery.md +0 -71
  206. package/docs/todos.md +0 -1
  207. package/scripts/test-command-listener.ts +0 -123
  208. package/src/node/features/launcher-app-command-listener.ts +0 -389
@@ -1,9 +1,9 @@
1
1
  import { z } from 'zod'
2
- import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
2
+ import { FeatureEventsSchema, FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
3
3
  import { State } from "../../state.js";
4
4
  import { Feature } from "../feature.js";
5
- import { parse, relative } from "path";
6
- import { statSync } from "fs";
5
+ import { parse, relative, join as pathJoin } from "path";
6
+ import { statSync, readFileSync, existsSync } from "fs";
7
7
  import micromatch from "micromatch";
8
8
  import { castArray } from "lodash-es";
9
9
  import chokidar from "chokidar";
@@ -37,6 +37,15 @@ export const FileManagerOptionsSchema = FeatureOptionsSchema.extend({
37
37
  exclude: z.union([z.string(), z.array(z.string())]).optional().describe('Glob patterns to exclude from file scanning'),
38
38
  })
39
39
 
40
+ export const FileManagerEventsSchema = FeatureEventsSchema.extend({
41
+ 'file:change': z.tuple([
42
+ z.object({
43
+ type: z.enum(['add', 'change', 'delete']).describe('The type of file change'),
44
+ path: z.string().describe('Absolute path to the changed file'),
45
+ }).describe('File change event payload'),
46
+ ]).describe('Emitted when a watched file is added, changed, or deleted'),
47
+ }).describe('FileManager events')
48
+
40
49
  export type FileManagerState = z.infer<typeof FileManagerStateSchema>
41
50
  export type FileManagerOptions = z.infer<typeof FileManagerOptionsSchema>
42
51
 
@@ -61,6 +70,7 @@ export class FileManager<
61
70
  static override shortcut = 'features.fileManager' as const
62
71
  static override stateSchema = FileManagerStateSchema
63
72
  static override optionsSchema = FileManagerOptionsSchema
73
+ static override eventsSchema = FileManagerEventsSchema
64
74
  static { Feature.register(this, 'fileManager') }
65
75
 
66
76
  files: State<Record<string, File>> = new State<Record<string, File>>({
@@ -135,6 +145,11 @@ export class FileManager<
135
145
  return !!this.state.get("watching");
136
146
  }
137
147
 
148
+ /** Returns the list of directories currently being watched. */
149
+ get watchedPaths(): string[] {
150
+ return this.state.get("watchedPaths") || [];
151
+ }
152
+
138
153
  /**
139
154
  * Starts the file manager and scans the files in the project.
140
155
  * @param {object} [options={}] - Options for the file manager
@@ -154,7 +169,11 @@ export class FileManager<
154
169
  }
155
170
 
156
171
  try {
157
- await this.scanFiles(options);
172
+ const loaded = await this.loadFromCache();
173
+ if (!loaded) {
174
+ await this.scanFiles(options);
175
+ await this.writeToCache();
176
+ }
158
177
  } catch (error) {
159
178
  console.error(error);
160
179
  this.state.set("failed", true);
@@ -166,6 +185,117 @@ export class FileManager<
166
185
  return this;
167
186
  }
168
187
 
188
+ /**
189
+ * Attempts to load the file index from disk cache.
190
+ * Only uses cache when in a clean git repo (sha hasn't changed, no dirty files).
191
+ * @returns true if cache was loaded successfully, false otherwise
192
+ */
193
+ /**
194
+ * Reads the current git SHA by reading .git/HEAD directly,
195
+ * avoiding the ~19ms cost of shelling out to `git rev-parse HEAD`.
196
+ */
197
+ private readGitShaFast(): string | null {
198
+ try {
199
+ const { git } = this.container;
200
+ if (!git.isRepo) return null;
201
+
202
+ const gitDir = pathJoin(git.repoRoot, '.git');
203
+ const head = readFileSync(pathJoin(gitDir, 'HEAD'), 'utf8').trim();
204
+
205
+ // Detached HEAD — already a sha
206
+ if (!head.startsWith('ref: ')) return head;
207
+
208
+ // Resolve the ref
209
+ const refPath = pathJoin(gitDir, head.slice(5));
210
+ if (existsSync(refPath)) {
211
+ return readFileSync(refPath, 'utf8').trim();
212
+ }
213
+
214
+ // Packed refs fallback
215
+ const packedRefsPath = pathJoin(gitDir, 'packed-refs');
216
+ if (existsSync(packedRefsPath)) {
217
+ const ref = head.slice(5);
218
+ const packed = readFileSync(packedRefsPath, 'utf8');
219
+ const match = packed.match(new RegExp(`^([0-9a-f]{40}) ${ref}`, 'm'));
220
+ if (match) return match[1];
221
+ }
222
+
223
+ return null;
224
+ } catch {
225
+ return null;
226
+ }
227
+ }
228
+
229
+ private async loadFromCache(): Promise<boolean> {
230
+ try {
231
+ const sha = this.readGitShaFast();
232
+ if (!sha) return false;
233
+
234
+ const cache = this.container.feature('diskCache') as any;
235
+ const cacheKey = `file-index:${sha}`;
236
+
237
+ if (!(await cache.has(cacheKey))) return false;
238
+
239
+ const cached = await cache.get(cacheKey, true) as { dirs: Record<string, number>, files: Record<string, any> };
240
+ if (!cached?.files || !cached?.dirs) return false;
241
+
242
+ // Check if any directory mtime has changed — catches new/deleted/renamed files
243
+ for (const [dir, cachedMtimeMs] of Object.entries(cached.dirs)) {
244
+ try {
245
+ const current = statSync(dir).mtimeMs;
246
+ if (current !== cachedMtimeMs) return false;
247
+ } catch {
248
+ // Directory no longer exists
249
+ return false;
250
+ }
251
+ }
252
+
253
+ for (const [relativePath, file] of Object.entries(cached.files)) {
254
+ this.files.set(relativePath, {
255
+ ...file as File,
256
+ modifiedAt: new Date((file as any).modifiedAt),
257
+ createdAt: new Date((file as any).createdAt),
258
+ });
259
+ }
260
+
261
+ return true;
262
+ } catch {
263
+ return false;
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Writes the current file index to disk cache, keyed by git sha.
269
+ * Stores directory mtimes alongside file data so the cache can be
270
+ * invalidated when files are added/removed without a new commit.
271
+ */
272
+ private async writeToCache(): Promise<void> {
273
+ try {
274
+ const sha = this.readGitShaFast();
275
+ if (!sha) return;
276
+
277
+ const cache = this.container.feature('diskCache') as any;
278
+ const cacheKey = `file-index:${sha}`;
279
+
280
+ // Collect unique directories and their mtimes
281
+ const dirs: Record<string, number> = {};
282
+ const files: Record<string, any> = {};
283
+
284
+ for (const [key, file] of this.files.entries()) {
285
+ files[key] = file;
286
+ if (!dirs[file.dirname]) {
287
+ try {
288
+ dirs[file.dirname] = statSync(file.dirname).mtimeMs;
289
+ } catch {}
290
+ }
291
+ }
292
+
293
+ await cache.set(cacheKey, { dirs, files });
294
+ } catch {
295
+ // Cache write failure is non-fatal
296
+ }
297
+ }
298
+
169
299
  /**
170
300
  * Scans the files in the project and updates the file manager state.
171
301
  * @param {object} [options={}] - Options for the file manager
@@ -252,14 +382,33 @@ export class FileManager<
252
382
  return this.watcher?.getWatched() || {};
253
383
  }
254
384
 
255
- /**
256
- * Watches the files in the project and updates the file manager state.
385
+ /**
386
+ * Watches directories for file changes. Can be called multiple times to add
387
+ * more directories to an existing watcher. Tracks all watched paths in state.
388
+ *
389
+ * When called without `paths`, watches the project's `directoryIds` (default behavior).
390
+ * When called with `paths`, watches only those specific directories/globs.
391
+ * Subsequent calls add to the existing watcher — they never replace what's already watched.
392
+ *
257
393
  * @param {object} [options={}] - Options for the file manager
394
+ * @param {string | string[]} [options.paths] - Specific directories or globs to watch. Defaults to project directoryIds.
258
395
  * @param {string | string[]} [options.exclude] - The patterns to exclude from the watch
259
- * @returns {Promise<void>} The file manager instance
396
+ * @returns {Promise<void>}
260
397
  */
261
- async watch(options: { exclude?: string | string[] } = {}) {
262
- if (this.isWatching) {
398
+ async watch(options: { paths?: string | string[]; exclude?: string | string[] } = {}) {
399
+ const pathsToWatch = castArray(options.paths || this.directoryIds.map(id => this.container.paths.resolve(id)))
400
+ .map(p => this.container.paths.resolve(p));
401
+
402
+ // If already watching, just add the new paths
403
+ if (this.isWatching && this.watcher) {
404
+ const currentPaths: string[] = this.state.get("watchedPaths") || [];
405
+ const newPaths = pathsToWatch.filter(p => !currentPaths.includes(p));
406
+
407
+ if (newPaths.length) {
408
+ this.watcher.add(newPaths);
409
+ this.state.set("watchedPaths", [...currentPaths, ...newPaths]);
410
+ }
411
+
263
412
  return;
264
413
  }
265
414
 
@@ -273,11 +422,7 @@ export class FileManager<
273
422
 
274
423
  exclude.push(...castArray(this.options.exclude!).filter((v) => v?.length));
275
424
 
276
- const { cwd } = this.container;
277
-
278
- const watcher = chokidar.watch(
279
- this.directoryIds.map(id => this.container.paths.resolve(id))
280
- , {
425
+ const watcher = chokidar.watch(pathsToWatch, {
281
426
  ignoreInitial: true,
282
427
  persistent: true,
283
428
  ignored: [
@@ -292,17 +437,17 @@ export class FileManager<
292
437
 
293
438
  watcher
294
439
  .on("add", (path) => {
440
+ this.updateFile(path);
295
441
  this.emit("file:change", {
296
442
  type: "add",
297
443
  path,
298
444
  });
299
- this.updateFile(path);
300
445
  })
301
446
  .on("change", (path) => {
302
447
  this.updateFile(path);
303
448
  this.emit("file:change", {
304
- path,
305
449
  type: "change",
450
+ path,
306
451
  });
307
452
  })
308
453
  .on("unlink", (path) => {
@@ -315,6 +460,7 @@ export class FileManager<
315
460
 
316
461
  watcher.on("ready", () => {
317
462
  this.state.set("watching", true);
463
+ this.state.set("watchedPaths", pathsToWatch);
318
464
  });
319
465
 
320
466
  this.watcher = watcher;
@@ -328,26 +474,35 @@ export class FileManager<
328
474
  if (this.watcher) {
329
475
  this.watcher.close();
330
476
  this.state.set("watching", false);
477
+ this.state.set("watchedPaths", []);
331
478
  this.watcher = null;
332
479
  }
333
480
  }
334
481
 
335
482
  async updateFile(path: string) {
336
- // Reuse the logic from the scanFiles method to update a single file
337
483
  const absolutePath = this.container.paths.resolve(path);
338
484
  const { name, ext, dir } = parse(absolutePath);
339
- const stats = statSync(absolutePath);
340
-
341
- this.files.set(path, {
342
- dirname: dir,
343
- absolutePath,
344
- relativePath: path,
345
- name,
346
- extension: ext,
347
- size: stats.size,
348
- modifiedAt: stats.mtime,
349
- createdAt: stats.birthtime,
350
- });
485
+
486
+ try {
487
+ const stats = statSync(absolutePath);
488
+ this.files.set(path, {
489
+ dirname: dir,
490
+ absolutePath,
491
+ relativePath: path,
492
+ name,
493
+ extension: ext,
494
+ size: stats.size,
495
+ modifiedAt: stats.mtime,
496
+ createdAt: stats.birthtime,
497
+ });
498
+ } catch (err: any) {
499
+ // File may have been moved or deleted by an event handler — remove from index gracefully
500
+ if (err.code === 'ENOENT') {
501
+ this.files.delete(path);
502
+ } else {
503
+ throw err;
504
+ }
505
+ }
351
506
  }
352
507
 
353
508
  async removeFile(path: string) {