@rcrsr/rill 0.16.0 → 0.18.0

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 (97) hide show
  1. package/README.md +37 -21
  2. package/dist/ast-nodes.d.ts +14 -4
  3. package/dist/ast-unions.d.ts +1 -1
  4. package/dist/constants.d.ts +1 -1
  5. package/dist/constants.js +1 -0
  6. package/dist/error-registry.js +228 -0
  7. package/dist/ext/crypto/index.d.ts +3 -3
  8. package/dist/ext/crypto/index.js +62 -59
  9. package/dist/ext/exec/index.d.ts +3 -3
  10. package/dist/ext/exec/index.js +15 -9
  11. package/dist/ext/fetch/index.d.ts +3 -3
  12. package/dist/ext/fetch/index.js +17 -12
  13. package/dist/ext/fetch/request.js +1 -1
  14. package/dist/ext/fs/index.d.ts +3 -3
  15. package/dist/ext/fs/index.js +256 -266
  16. package/dist/ext/fs/sandbox.d.ts +18 -0
  17. package/dist/ext/fs/sandbox.js +33 -0
  18. package/dist/ext/kv/index.d.ts +3 -3
  19. package/dist/ext/kv/index.js +198 -196
  20. package/dist/ext/kv/store.d.ts +1 -1
  21. package/dist/ext/kv/store.js +2 -1
  22. package/dist/ext-parse-bridge.d.ts +10 -0
  23. package/dist/ext-parse-bridge.js +10 -0
  24. package/dist/generated/introspection-data.d.ts +1 -1
  25. package/dist/generated/introspection-data.js +385 -296
  26. package/dist/generated/version-data.d.ts +1 -1
  27. package/dist/generated/version-data.js +2 -2
  28. package/dist/highlight-map.js +1 -0
  29. package/dist/index.d.ts +1 -4
  30. package/dist/index.js +1 -5
  31. package/dist/lexer/operators.js +1 -0
  32. package/dist/parser/helpers.js +1 -0
  33. package/dist/parser/parser-expr.js +44 -5
  34. package/dist/parser/parser-literals.js +111 -4
  35. package/dist/parser/parser-shape.js +2 -2
  36. package/dist/parser/parser-types.js +12 -0
  37. package/dist/parser/parser-use.js +26 -3
  38. package/dist/parser/parser.d.ts +2 -0
  39. package/dist/parser/parser.js +2 -0
  40. package/dist/runtime/core/callable.d.ts +24 -13
  41. package/dist/runtime/core/callable.js +71 -38
  42. package/dist/runtime/core/context.d.ts +2 -13
  43. package/dist/runtime/core/context.js +80 -79
  44. package/dist/runtime/core/eval/base.d.ts +2 -2
  45. package/dist/runtime/core/eval/base.js +2 -0
  46. package/dist/runtime/core/eval/evaluator.d.ts +1 -1
  47. package/dist/runtime/core/eval/index.d.ts +3 -3
  48. package/dist/runtime/core/eval/index.js +11 -0
  49. package/dist/runtime/core/eval/mixins/closures.js +381 -41
  50. package/dist/runtime/core/eval/mixins/collections.js +81 -6
  51. package/dist/runtime/core/eval/mixins/control-flow.js +1 -1
  52. package/dist/runtime/core/eval/mixins/conversion.js +61 -115
  53. package/dist/runtime/core/eval/mixins/core.js +17 -4
  54. package/dist/runtime/core/eval/mixins/expressions.js +36 -27
  55. package/dist/runtime/core/eval/mixins/extraction.js +2 -3
  56. package/dist/runtime/core/eval/mixins/list-dispatch.js +1 -1
  57. package/dist/runtime/core/eval/mixins/literals.js +17 -6
  58. package/dist/runtime/core/eval/mixins/types.js +73 -54
  59. package/dist/runtime/core/eval/mixins/variables.js +12 -8
  60. package/dist/runtime/core/execute.d.ts +1 -1
  61. package/dist/runtime/core/field-descriptor.d.ts +3 -3
  62. package/dist/runtime/core/field-descriptor.js +2 -1
  63. package/dist/runtime/core/introspection.d.ts +2 -2
  64. package/dist/runtime/core/introspection.js +7 -6
  65. package/dist/runtime/core/resolvers.d.ts +1 -1
  66. package/dist/runtime/core/signals.d.ts +6 -1
  67. package/dist/runtime/core/signals.js +9 -0
  68. package/dist/runtime/core/types/constructors.d.ts +54 -0
  69. package/dist/runtime/core/types/constructors.js +201 -0
  70. package/dist/runtime/core/types/guards.d.ts +42 -0
  71. package/dist/runtime/core/types/guards.js +88 -0
  72. package/dist/runtime/core/types/index.d.ts +18 -0
  73. package/dist/runtime/core/types/index.js +19 -0
  74. package/dist/runtime/core/types/markers.d.ts +12 -0
  75. package/dist/runtime/core/types/markers.js +7 -0
  76. package/dist/runtime/core/types/operations.d.ts +98 -0
  77. package/dist/runtime/core/types/operations.js +804 -0
  78. package/dist/runtime/core/types/registrations.d.ts +126 -0
  79. package/dist/runtime/core/types/registrations.js +751 -0
  80. package/dist/runtime/core/{types.d.ts → types/runtime.d.ts} +22 -10
  81. package/dist/runtime/core/types/structures.d.ts +146 -0
  82. package/dist/runtime/core/types/structures.js +12 -0
  83. package/dist/runtime/core/values.d.ts +29 -209
  84. package/dist/runtime/core/values.js +56 -968
  85. package/dist/runtime/ext/builtins.js +88 -68
  86. package/dist/runtime/ext/extensions.d.ts +31 -125
  87. package/dist/runtime/ext/extensions.js +2 -94
  88. package/dist/runtime/ext/test-context.d.ts +28 -0
  89. package/dist/runtime/ext/test-context.js +155 -0
  90. package/dist/runtime/index.d.ts +12 -12
  91. package/dist/runtime/index.js +13 -5
  92. package/dist/signature-parser.d.ts +2 -2
  93. package/dist/signature-parser.js +14 -14
  94. package/dist/token-types.d.ts +1 -0
  95. package/dist/token-types.js +1 -0
  96. package/package.json +1 -1
  97. /package/dist/runtime/core/{types.js → types/runtime.js} +0 -0
@@ -7,8 +7,9 @@
7
7
  import fs from 'node:fs/promises';
8
8
  import path from 'node:path';
9
9
  import { RuntimeError } from '../../error-classes.js';
10
- import { rillTypeToTypeValue, } from '../../runtime/core/values.js';
11
- import { resolvePath, matchesGlob, initializeMount, } from './sandbox.js';
10
+ import { toCallable } from '../../runtime/core/callable.js';
11
+ import { structureToTypeValue } from '../../runtime/core/values.js';
12
+ import { resolvePath, matchesGlob, initializeMount, parseMountPath, } from './sandbox.js';
12
13
  export const configSchema = {
13
14
  mounts: { type: 'string', required: true },
14
15
  };
@@ -22,7 +23,7 @@ export const configSchema = {
22
23
  * Returns 12 functions: read, write, append, list, find, exists, remove, stat, mkdir, copy, move, mounts.
23
24
  *
24
25
  * @param config - Mount configuration and defaults
25
- * @returns ExtensionResult with 12 filesystem functions
26
+ * @returns ExtensionFactoryResult with 12 filesystem functions
26
27
  * @throws RuntimeError if mount initialization fails
27
28
  *
28
29
  * @example
@@ -75,8 +76,7 @@ export function createFsExtension(config) {
75
76
  // ctx and location not used but required by CallableFn signature
76
77
  ) => {
77
78
  await ensureInitialized();
78
- const mountName = args['mount'];
79
- const filePath = args['path'];
79
+ const { mountName, relativePath: filePath } = parseMountPath(args['path'], mounts);
80
80
  // EC-5: Catch file not found from resolvePath
81
81
  let resolvedPath;
82
82
  try {
@@ -101,8 +101,7 @@ export function createFsExtension(config) {
101
101
  */
102
102
  const write = async (args) => {
103
103
  await ensureInitialized();
104
- const mountName = args['mount'];
105
- const filePath = args['path'];
104
+ const { mountName, relativePath: filePath } = parseMountPath(args['path'], mounts);
106
105
  const content = args['content'];
107
106
  const resolvedPath = await resolvePath(mountName, filePath, mounts, 'write', true // createMode: resolve parent directory
108
107
  );
@@ -120,8 +119,7 @@ export function createFsExtension(config) {
120
119
  */
121
120
  const append = async (args) => {
122
121
  await ensureInitialized();
123
- const mountName = args['mount'];
124
- const filePath = args['path'];
122
+ const { mountName, relativePath: filePath } = parseMountPath(args['path'], mounts);
125
123
  const content = args['content'];
126
124
  const resolvedPath = await resolvePath(mountName, filePath, mounts, 'write', true // createMode: allow new files
127
125
  );
@@ -157,8 +155,7 @@ export function createFsExtension(config) {
157
155
  */
158
156
  const list = async (args) => {
159
157
  await ensureInitialized();
160
- const mountName = args['mount'];
161
- const dirPath = args['path'] ?? '';
158
+ const { mountName, relativePath: dirPath } = parseMountPath(args['path'], mounts);
162
159
  const resolvedPath = await resolvePath(mountName, dirPath, mounts, 'read');
163
160
  const entries = await fs.readdir(resolvedPath, { withFileTypes: true });
164
161
  const result = [];
@@ -179,13 +176,20 @@ export function createFsExtension(config) {
179
176
  */
180
177
  const find = async (args) => {
181
178
  await ensureInitialized();
182
- const mountName = args['mount'];
179
+ const { mountName, relativePath: searchBase } = parseMountPath(args['path'], mounts);
183
180
  const pattern = args['pattern'] ?? '*';
184
181
  const mount = mounts[mountName];
185
182
  if (!mount || !mount.resolvedPath) {
186
183
  throw new RuntimeError('RILL-R004', `mount "${mountName}" not configured`, undefined, { mountName });
187
184
  }
188
- const basePath = mount.resolvedPath;
185
+ let basePath;
186
+ if (searchBase) {
187
+ // Validate searchBase through sandbox resolver to prevent path traversal
188
+ basePath = await resolvePath(mountName, searchBase, mounts, 'read');
189
+ }
190
+ else {
191
+ basePath = mount.resolvedPath;
192
+ }
189
193
  const results = [];
190
194
  // Recursive directory traversal
191
195
  const traverse = async (currentPath) => {
@@ -197,7 +201,7 @@ export function createFsExtension(config) {
197
201
  }
198
202
  else if (matchesGlob(entry.name, pattern)) {
199
203
  // Return path relative to mount base
200
- const relativePath = path.relative(basePath, fullPath);
204
+ const relativePath = path.relative(mount.resolvedPath, fullPath);
201
205
  results.push(relativePath);
202
206
  }
203
207
  }
@@ -211,8 +215,7 @@ export function createFsExtension(config) {
211
215
  */
212
216
  const exists = async (args) => {
213
217
  await ensureInitialized();
214
- const mountName = args['mount'];
215
- const filePath = args['path'];
218
+ const { mountName, relativePath: filePath } = parseMountPath(args['path'], mounts);
216
219
  try {
217
220
  await resolvePath(mountName, filePath, mounts, 'read');
218
221
  return true;
@@ -231,8 +234,7 @@ export function createFsExtension(config) {
231
234
  */
232
235
  const remove = async (args) => {
233
236
  await ensureInitialized();
234
- const mountName = args['mount'];
235
- const filePath = args['path'];
237
+ const { mountName, relativePath: filePath } = parseMountPath(args['path'], mounts);
236
238
  // Catch file not found from resolvePath
237
239
  let resolvedPath;
238
240
  try {
@@ -264,8 +266,7 @@ export function createFsExtension(config) {
264
266
  */
265
267
  const stat = async (args) => {
266
268
  await ensureInitialized();
267
- const mountName = args['mount'];
268
- const filePath = args['path'];
269
+ const { mountName, relativePath: filePath } = parseMountPath(args['path'], mounts);
269
270
  // Catch file not found from resolvePath
270
271
  let resolvedPath;
271
272
  try {
@@ -294,8 +295,7 @@ export function createFsExtension(config) {
294
295
  */
295
296
  const mkdir = async (args) => {
296
297
  await ensureInitialized();
297
- const mountName = args['mount'];
298
- const dirPath = args['path'];
298
+ const { mountName, relativePath: dirPath } = parseMountPath(args['path'], mounts);
299
299
  const mount = mounts[mountName];
300
300
  if (!mount || !mount.resolvedPath) {
301
301
  throw new RuntimeError('RILL-R004', `mount "${mountName}" not configured`, undefined, { mountName });
@@ -344,9 +344,13 @@ export function createFsExtension(config) {
344
344
  */
345
345
  const copy = async (args) => {
346
346
  await ensureInitialized();
347
- const mountName = args['mount'];
348
- const srcPath = args['src'];
349
- const destPath = args['dest'];
347
+ const { mountName: srcMountName, relativePath: srcPath } = parseMountPath(args['src'], mounts);
348
+ const { mountName: destMountName, relativePath: destPath } = parseMountPath(args['dest'], mounts);
349
+ const mountName = srcMountName;
350
+ // Verify same mount
351
+ if (srcMountName !== destMountName) {
352
+ throw new RuntimeError('RILL-R004', `copy requires same mount for src and dest`, undefined, { src: args['src'], dest: args['dest'] });
353
+ }
350
354
  const resolvedSrc = await resolvePath(mountName, srcPath, mounts, 'read');
351
355
  const resolvedDest = await resolvePath(mountName, destPath, mounts, 'write', true // createMode
352
356
  );
@@ -373,9 +377,13 @@ export function createFsExtension(config) {
373
377
  */
374
378
  const move = async (args) => {
375
379
  await ensureInitialized();
376
- const mountName = args['mount'];
377
- const srcPath = args['src'];
378
- const destPath = args['dest'];
380
+ const { mountName: srcMountName, relativePath: srcPath } = parseMountPath(args['src'], mounts);
381
+ const { mountName: destMountName, relativePath: destPath } = parseMountPath(args['dest'], mounts);
382
+ const mountName = srcMountName;
383
+ // Verify same mount
384
+ if (srcMountName !== destMountName) {
385
+ throw new RuntimeError('RILL-R004', `move requires same mount for src and dest`, undefined, { src: args['src'], dest: args['dest'] });
386
+ }
379
387
  const resolvedSrc = await resolvePath(mountName, srcPath, mounts, 'read');
380
388
  const resolvedDest = await resolvePath(mountName, destPath, mounts, 'write', true // createMode
381
389
  );
@@ -412,244 +420,226 @@ export function createFsExtension(config) {
412
420
  // EXTENSION RESULT
413
421
  // ============================================================
414
422
  return {
415
- read: {
416
- params: [
417
- {
418
- name: 'mount',
419
- type: { type: 'string' },
420
- defaultValue: undefined,
421
- annotations: { description: 'Mount name' },
422
- },
423
- {
424
- name: 'path',
425
- type: { type: 'string' },
426
- defaultValue: undefined,
427
- annotations: { description: 'File path relative to mount' },
428
- },
429
- ],
430
- fn: read,
431
- annotations: { description: 'Read file contents' },
432
- returnType: rillTypeToTypeValue({ type: 'string' }),
433
- },
434
- write: {
435
- params: [
436
- {
437
- name: 'mount',
438
- type: { type: 'string' },
439
- defaultValue: undefined,
440
- annotations: { description: 'Mount name' },
441
- },
442
- {
443
- name: 'path',
444
- type: { type: 'string' },
445
- defaultValue: undefined,
446
- annotations: { description: 'File path relative to mount' },
447
- },
448
- {
449
- name: 'content',
450
- type: { type: 'string' },
451
- defaultValue: undefined,
452
- annotations: { description: 'Content to write' },
453
- },
454
- ],
455
- fn: write,
456
- annotations: { description: 'Write file, replacing if exists' },
457
- returnType: rillTypeToTypeValue({ type: 'string' }),
458
- },
459
- append: {
460
- params: [
461
- {
462
- name: 'mount',
463
- type: { type: 'string' },
464
- defaultValue: undefined,
465
- annotations: { description: 'Mount name' },
466
- },
467
- {
468
- name: 'path',
469
- type: { type: 'string' },
470
- defaultValue: undefined,
471
- annotations: { description: 'File path relative to mount' },
472
- },
473
- {
474
- name: 'content',
475
- type: { type: 'string' },
476
- defaultValue: undefined,
477
- annotations: { description: 'Content to append' },
478
- },
479
- ],
480
- fn: append,
481
- annotations: { description: 'Append content to file' },
482
- returnType: rillTypeToTypeValue({ type: 'string' }),
483
- },
484
- list: {
485
- params: [
486
- {
487
- name: 'mount',
488
- type: { type: 'string' },
489
- defaultValue: undefined,
490
- annotations: { description: 'Mount name' },
491
- },
492
- {
493
- name: 'path',
494
- type: { type: 'string' },
495
- defaultValue: '',
496
- annotations: { description: 'Directory path relative to mount' },
497
- },
498
- ],
499
- fn: list,
500
- annotations: { description: 'List directory contents' },
501
- returnType: rillTypeToTypeValue({ type: 'list' }),
502
- },
503
- find: {
504
- params: [
505
- {
506
- name: 'mount',
507
- type: { type: 'string' },
508
- defaultValue: undefined,
509
- annotations: { description: 'Mount name' },
510
- },
511
- {
512
- name: 'pattern',
513
- type: { type: 'string' },
514
- defaultValue: '*',
515
- annotations: { description: 'Glob pattern for filtering' },
516
- },
517
- ],
518
- fn: find,
519
- annotations: { description: 'Recursive file search' },
520
- returnType: rillTypeToTypeValue({ type: 'list' }),
521
- },
522
- exists: {
523
- params: [
524
- {
525
- name: 'mount',
526
- type: { type: 'string' },
527
- defaultValue: undefined,
528
- annotations: { description: 'Mount name' },
529
- },
530
- {
531
- name: 'path',
532
- type: { type: 'string' },
533
- defaultValue: undefined,
534
- annotations: { description: 'File path relative to mount' },
535
- },
536
- ],
537
- fn: exists,
538
- annotations: { description: 'Check file existence' },
539
- returnType: rillTypeToTypeValue({ type: 'bool' }),
540
- },
541
- remove: {
542
- params: [
543
- {
544
- name: 'mount',
545
- type: { type: 'string' },
546
- defaultValue: undefined,
547
- annotations: { description: 'Mount name' },
548
- },
549
- {
550
- name: 'path',
551
- type: { type: 'string' },
552
- defaultValue: undefined,
553
- annotations: { description: 'File path relative to mount' },
554
- },
555
- ],
556
- fn: remove,
557
- annotations: { description: 'Delete file' },
558
- returnType: rillTypeToTypeValue({ type: 'bool' }),
559
- },
560
- stat: {
561
- params: [
562
- {
563
- name: 'mount',
564
- type: { type: 'string' },
565
- defaultValue: undefined,
566
- annotations: { description: 'Mount name' },
567
- },
568
- {
569
- name: 'path',
570
- type: { type: 'string' },
571
- defaultValue: undefined,
572
- annotations: { description: 'File path relative to mount' },
573
- },
574
- ],
575
- fn: stat,
576
- annotations: { description: 'Get file metadata' },
577
- returnType: rillTypeToTypeValue({ type: 'dict' }),
578
- },
579
- mkdir: {
580
- params: [
581
- {
582
- name: 'mount',
583
- type: { type: 'string' },
584
- defaultValue: undefined,
585
- annotations: { description: 'Mount name' },
586
- },
587
- {
588
- name: 'path',
589
- type: { type: 'string' },
590
- defaultValue: undefined,
591
- annotations: { description: 'Directory path relative to mount' },
592
- },
593
- ],
594
- fn: mkdir,
595
- annotations: { description: 'Create directory' },
596
- returnType: rillTypeToTypeValue({ type: 'bool' }),
597
- },
598
- copy: {
599
- params: [
600
- {
601
- name: 'mount',
602
- type: { type: 'string' },
603
- defaultValue: undefined,
604
- annotations: { description: 'Mount name' },
605
- },
606
- {
607
- name: 'src',
608
- type: { type: 'string' },
609
- defaultValue: undefined,
610
- annotations: { description: 'Source file path' },
611
- },
612
- {
613
- name: 'dest',
614
- type: { type: 'string' },
615
- defaultValue: undefined,
616
- annotations: { description: 'Destination file path' },
617
- },
618
- ],
619
- fn: copy,
620
- annotations: { description: 'Copy file within mount' },
621
- returnType: rillTypeToTypeValue({ type: 'bool' }),
622
- },
623
- move: {
624
- params: [
625
- {
626
- name: 'mount',
627
- type: { type: 'string' },
628
- defaultValue: undefined,
629
- annotations: { description: 'Mount name' },
630
- },
631
- {
632
- name: 'src',
633
- type: { type: 'string' },
634
- defaultValue: undefined,
635
- annotations: { description: 'Source file path' },
636
- },
637
- {
638
- name: 'dest',
639
- type: { type: 'string' },
640
- defaultValue: undefined,
641
- annotations: { description: 'Destination file path' },
642
- },
643
- ],
644
- fn: move,
645
- annotations: { description: 'Move file within mount' },
646
- returnType: rillTypeToTypeValue({ type: 'bool' }),
647
- },
648
- mounts: {
649
- params: [],
650
- fn: mountsList,
651
- annotations: { description: 'List configured mounts' },
652
- returnType: rillTypeToTypeValue({ type: 'list' }),
423
+ value: {
424
+ read: toCallable({
425
+ params: [
426
+ {
427
+ name: 'path',
428
+ type: { kind: 'string' },
429
+ defaultValue: undefined,
430
+ annotations: {
431
+ description: 'Mount-prefixed file path (e.g. "/mount/file.txt")',
432
+ },
433
+ },
434
+ ],
435
+ fn: read,
436
+ annotations: { description: 'Read file contents' },
437
+ returnType: structureToTypeValue({ kind: 'string' }),
438
+ }),
439
+ write: toCallable({
440
+ params: [
441
+ {
442
+ name: 'path',
443
+ type: { kind: 'string' },
444
+ defaultValue: undefined,
445
+ annotations: {
446
+ description: 'Mount-prefixed file path (e.g. "/mount/file.txt")',
447
+ },
448
+ },
449
+ {
450
+ name: 'content',
451
+ type: { kind: 'string' },
452
+ defaultValue: undefined,
453
+ annotations: { description: 'Content to write' },
454
+ },
455
+ ],
456
+ fn: write,
457
+ annotations: { description: 'Write file, replacing if exists' },
458
+ returnType: structureToTypeValue({ kind: 'string' }),
459
+ }),
460
+ append: toCallable({
461
+ params: [
462
+ {
463
+ name: 'path',
464
+ type: { kind: 'string' },
465
+ defaultValue: undefined,
466
+ annotations: {
467
+ description: 'Mount-prefixed file path (e.g. "/mount/file.txt")',
468
+ },
469
+ },
470
+ {
471
+ name: 'content',
472
+ type: { kind: 'string' },
473
+ defaultValue: undefined,
474
+ annotations: { description: 'Content to append' },
475
+ },
476
+ ],
477
+ fn: append,
478
+ annotations: { description: 'Append content to file' },
479
+ returnType: structureToTypeValue({ kind: 'string' }),
480
+ }),
481
+ list: toCallable({
482
+ params: [
483
+ {
484
+ name: 'path',
485
+ type: { kind: 'string' },
486
+ defaultValue: undefined,
487
+ annotations: {
488
+ description: 'Mount-prefixed directory path (e.g. "/mount/subdir")',
489
+ },
490
+ },
491
+ ],
492
+ fn: list,
493
+ annotations: { description: 'List directory contents' },
494
+ returnType: structureToTypeValue({
495
+ kind: 'list',
496
+ element: {
497
+ kind: 'dict',
498
+ fields: {
499
+ name: { type: { kind: 'string' } },
500
+ type: { type: { kind: 'string' } },
501
+ size: { type: { kind: 'number' } },
502
+ },
503
+ },
504
+ }),
505
+ }),
506
+ find: toCallable({
507
+ params: [
508
+ {
509
+ name: 'path',
510
+ type: { kind: 'string' },
511
+ defaultValue: undefined,
512
+ annotations: {
513
+ description: 'Mount-prefixed base path (e.g. "/mount" or "/mount/subdir")',
514
+ },
515
+ },
516
+ {
517
+ name: 'pattern',
518
+ type: { kind: 'string' },
519
+ defaultValue: '*',
520
+ annotations: { description: 'Glob pattern for filtering' },
521
+ },
522
+ ],
523
+ fn: find,
524
+ annotations: { description: 'Recursive file search' },
525
+ returnType: structureToTypeValue({
526
+ kind: 'list',
527
+ element: { kind: 'string' },
528
+ }),
529
+ }),
530
+ exists: toCallable({
531
+ params: [
532
+ {
533
+ name: 'path',
534
+ type: { kind: 'string' },
535
+ defaultValue: undefined,
536
+ annotations: {
537
+ description: 'Mount-prefixed file path (e.g. "/mount/file.txt")',
538
+ },
539
+ },
540
+ ],
541
+ fn: exists,
542
+ annotations: { description: 'Check file existence' },
543
+ returnType: structureToTypeValue({ kind: 'bool' }),
544
+ }),
545
+ remove: toCallable({
546
+ params: [
547
+ {
548
+ name: 'path',
549
+ type: { kind: 'string' },
550
+ defaultValue: undefined,
551
+ annotations: {
552
+ description: 'Mount-prefixed file path (e.g. "/mount/file.txt")',
553
+ },
554
+ },
555
+ ],
556
+ fn: remove,
557
+ annotations: { description: 'Delete file' },
558
+ returnType: structureToTypeValue({ kind: 'bool' }),
559
+ }),
560
+ stat: toCallable({
561
+ params: [
562
+ {
563
+ name: 'path',
564
+ type: { kind: 'string' },
565
+ defaultValue: undefined,
566
+ annotations: {
567
+ description: 'Mount-prefixed file path (e.g. "/mount/file.txt")',
568
+ },
569
+ },
570
+ ],
571
+ fn: stat,
572
+ annotations: { description: 'Get file metadata' },
573
+ returnType: structureToTypeValue({
574
+ kind: 'dict',
575
+ fields: {
576
+ name: { type: { kind: 'string' } },
577
+ type: { type: { kind: 'string' } },
578
+ size: { type: { kind: 'number' } },
579
+ created: { type: { kind: 'string' } },
580
+ modified: { type: { kind: 'string' } },
581
+ },
582
+ }),
583
+ }),
584
+ mkdir: toCallable({
585
+ params: [
586
+ {
587
+ name: 'path',
588
+ type: { kind: 'string' },
589
+ defaultValue: undefined,
590
+ annotations: {
591
+ description: 'Mount-prefixed directory path (e.g. "/mount/subdir")',
592
+ },
593
+ },
594
+ ],
595
+ fn: mkdir,
596
+ annotations: { description: 'Create directory' },
597
+ returnType: structureToTypeValue({ kind: 'bool' }),
598
+ }),
599
+ copy: toCallable({
600
+ params: [
601
+ {
602
+ name: 'src',
603
+ type: { kind: 'string' },
604
+ defaultValue: undefined,
605
+ annotations: { description: 'Mount-prefixed source path' },
606
+ },
607
+ {
608
+ name: 'dest',
609
+ type: { kind: 'string' },
610
+ defaultValue: undefined,
611
+ annotations: { description: 'Mount-prefixed destination path' },
612
+ },
613
+ ],
614
+ fn: copy,
615
+ annotations: { description: 'Copy file within mount' },
616
+ returnType: structureToTypeValue({ kind: 'bool' }),
617
+ }),
618
+ move: toCallable({
619
+ params: [
620
+ {
621
+ name: 'src',
622
+ type: { kind: 'string' },
623
+ defaultValue: undefined,
624
+ annotations: { description: 'Mount-prefixed source path' },
625
+ },
626
+ {
627
+ name: 'dest',
628
+ type: { kind: 'string' },
629
+ defaultValue: undefined,
630
+ annotations: { description: 'Mount-prefixed destination path' },
631
+ },
632
+ ],
633
+ fn: move,
634
+ annotations: { description: 'Move file within mount' },
635
+ returnType: structureToTypeValue({ kind: 'bool' }),
636
+ }),
637
+ mounts: toCallable({
638
+ params: [],
639
+ fn: mountsList,
640
+ annotations: { description: 'List configured mounts' },
641
+ returnType: structureToTypeValue({ kind: 'list' }),
642
+ }),
653
643
  },
654
644
  };
655
645
  }