@miruzero/zero-agent 0.3.6 → 0.3.7

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 (68) hide show
  1. package/README.md +31 -22
  2. package/package.json +6 -3
  3. package/src/agent.js +193 -167
  4. package/src/args.js +1 -3
  5. package/src/autostart.js +44 -68
  6. package/src/capabilities.js +95 -138
  7. package/src/chatgpt/auth/index.js +114 -0
  8. package/src/chatgpt/config/paths.js +25 -0
  9. package/src/chatgpt/index.js +5 -0
  10. package/src/chatgpt/session/direct-transport.js +68 -0
  11. package/src/chatgpt/session/exchange.js +17 -0
  12. package/src/chatgpt/session/index.js +102 -0
  13. package/src/chatgpt/session/local-storage.js +106 -0
  14. package/src/chatgpt/transport/direct/http-client.js +191 -0
  15. package/src/chatgpt-auth.js +17 -111
  16. package/src/chatgpt-runtime.js +8 -0
  17. package/src/chatgpt-session-exchange.js +8 -0
  18. package/src/chatgpt-session.js +26 -0
  19. package/src/cli.js +108 -71
  20. package/src/command/exec.js +34 -0
  21. package/src/command/filesystem/append-file.js +7 -12
  22. package/src/command/filesystem/copy-directory.js +8 -12
  23. package/src/command/filesystem/copy-file.js +8 -13
  24. package/src/command/filesystem/create-directory.js +7 -11
  25. package/src/command/filesystem/create-file.js +8 -13
  26. package/src/command/filesystem/delete-directory.js +7 -11
  27. package/src/command/filesystem/delete-file.js +8 -12
  28. package/src/command/filesystem/exists.js +6 -10
  29. package/src/command/filesystem/get-file-info.js +7 -12
  30. package/src/command/filesystem/glob-files.js +8 -13
  31. package/src/command/filesystem/grep-files.js +9 -15
  32. package/src/command/filesystem/hash-file.js +6 -11
  33. package/src/command/filesystem/index.js +33 -34
  34. package/src/command/filesystem/list-directory.js +6 -10
  35. package/src/command/filesystem/move-directory.js +9 -14
  36. package/src/command/filesystem/move-file.js +8 -13
  37. package/src/command/filesystem/path-policy.js +33 -16
  38. package/src/command/filesystem/read-file.js +7 -11
  39. package/src/command/filesystem/read-files.js +5 -8
  40. package/src/command/filesystem/rename-file.js +5 -8
  41. package/src/command/filesystem/replace-file.js +7 -12
  42. package/src/command/filesystem/text.js +5 -7
  43. package/src/command/filesystem/touch-file.js +8 -13
  44. package/src/command/filesystem/truncate-file.js +6 -10
  45. package/src/command/filesystem/walk.js +36 -39
  46. package/src/command/filesystem/write-file.js +8 -13
  47. package/src/command/index.js +19 -24
  48. package/src/command/path/index.js +46 -53
  49. package/src/config.js +21 -33
  50. package/src/device-paths.js +53 -0
  51. package/src/enrollment-client.js +19 -0
  52. package/src/http.js +27 -2
  53. package/src/identity-state.js +27 -0
  54. package/src/index.js +2 -4
  55. package/src/instance.js +85 -0
  56. package/src/local-server.js +174 -0
  57. package/src/local-tools.js +205 -205
  58. package/src/shared-zero/catalog.js +33 -0
  59. package/src/shared-zero/contract.js +18 -0
  60. package/src/shared-zero/descriptor.js +25 -0
  61. package/src/shared-zero/errors.js +12 -0
  62. package/src/shared-zero/index.js +9 -0
  63. package/src/shared-zero/runtime.js +14 -0
  64. package/src/shared-zero/target.js +19 -0
  65. package/src/shared-zero/tool-id.js +26 -0
  66. package/src/storage-roots.js +21 -0
  67. package/src/zero-runtime.js +1 -11
  68. package/src/zero.js +78 -0
@@ -1,11 +1,9 @@
1
1
  'use strict';
2
2
 
3
- const fs = require('node:fs');
4
- const zero = require('../../zero-runtime');
5
- const pathPolicy = require('./path-policy');
6
- const text = require('./text');
3
+ const zero = require('../../zero');
7
4
 
8
- const descriptor = zero.descriptor.validate({
5
+
6
+ exports.descriptor = zero.descriptor.validate({
9
7
  name: 'zero.command.filesystem.getFileInfo',
10
8
  aliases: ['zero.command.getFileInfo'],
11
9
  description: 'Get metadata for a file or directory inside allowed roots.',
@@ -21,9 +19,9 @@ const descriptor = zero.descriptor.validate({
21
19
  }
22
20
  });
23
21
 
24
- const getFileInfo = (args = {}, context = {}) => {
25
- const target = pathPolicy.resolveAllowed(args.path, context);
26
- const stat = fs.statSync(target);
22
+ exports.getFileInfo = (args = {}, context = {}) => {
23
+ const target = zero.pathPolicy.resolveAllowed(args.path, context);
24
+ const stat = zero.fs.statSync(target);
27
25
  return {
28
26
  ok: true,
29
27
  path: target,
@@ -31,9 +29,6 @@ const getFileInfo = (args = {}, context = {}) => {
31
29
  size: stat.size,
32
30
  createdAt: stat.birthtime.toISOString(),
33
31
  modifiedAt: stat.mtime.toISOString(),
34
- lineCount: stat.isFile() ? text.readUtf8(target).split(/\r?\n/).length : null
32
+ lineCount: stat.isFile() ? zero.commandText.readUtf8(target).split(/\r?\n/).length : null
35
33
  };
36
34
  };
37
-
38
- const getFileInfoModule = { descriptor, getFileInfo };
39
- Object.assign(exports, getFileInfoModule);
@@ -1,11 +1,9 @@
1
1
  'use strict';
2
2
 
3
- const path = require('node:path');
4
- const zero = require('../../zero-runtime');
5
- const pathPolicy = require('./path-policy');
6
- const walk = require('./walk');
3
+ const zero = require('../../zero');
7
4
 
8
- const descriptor = zero.descriptor.validate({
5
+
6
+ exports.descriptor = zero.descriptor.validate({
9
7
  name: 'zero.command.filesystem.globFiles',
10
8
  aliases: ['zero.command.globFiles'],
11
9
  description: 'Find files by wildcard pattern inside allowed roots.',
@@ -25,15 +23,12 @@ const descriptor = zero.descriptor.validate({
25
23
  }
26
24
  });
27
25
 
28
- const globFiles = (args = {}, context = {}) => {
29
- const root = pathPolicy.resolveAllowed(args.path, context);
30
- const rx = walk.wildcard(args.pattern || '*');
26
+ exports.globFiles = (args = {}, context = {}) => {
27
+ const root = zero.pathPolicy.resolveAllowed(args.path, context);
28
+ const rx = zero.commandWalk.wildcard(args.pattern || '*');
31
29
  const max = Math.max(1, Math.min(args.maxResults || 200, 1000));
32
- const files = walk.walk(root, { filesOnly: true })
33
- .filter((item) => rx.test(path.basename(item.path)) || rx.test(item.relativePath))
30
+ const files = zero.commandWalk.walk(root, { filesOnly: true })
31
+ .filter((item) => rx.test(zero.path.basename(item.path)) || rx.test(item.relativePath))
34
32
  .slice(0, max);
35
33
  return { ok: true, path: root, pattern: args.pattern, files };
36
34
  };
37
-
38
- const globFilesModule = { descriptor, globFiles };
39
- Object.assign(exports, globFilesModule);
@@ -1,12 +1,9 @@
1
1
  'use strict';
2
2
 
3
- const path = require('node:path');
4
- const zero = require('../../zero-runtime');
5
- const pathPolicy = require('./path-policy');
6
- const textModule = require('./text');
7
- const walk = require('./walk');
3
+ const zero = require('../../zero');
8
4
 
9
- const descriptor = zero.descriptor.validate({
5
+
6
+ exports.descriptor = zero.descriptor.validate({
10
7
  name: 'zero.command.filesystem.grepFiles',
11
8
  aliases: ['zero.command.grepFiles'],
12
9
  description: 'Search text content in files inside allowed roots.',
@@ -28,16 +25,16 @@ const descriptor = zero.descriptor.validate({
28
25
  }
29
26
  });
30
27
 
31
- const grepFiles = (args = {}, context = {}) => {
32
- const root = pathPolicy.resolveAllowed(args.path, context);
28
+ exports.grepFiles = (args = {}, context = {}) => {
29
+ const root = zero.pathPolicy.resolveAllowed(args.path, context);
33
30
  const max = Math.max(1, Math.min(args.maxResults || 200, 1000));
34
31
  const rx = args.literal ? null : new RegExp(String(args.pattern), 'i');
35
- const fileRx = args.filePattern ? walk.wildcard(args.filePattern) : null;
32
+ const fileRx = args.filePattern ? zero.commandWalk.wildcard(args.filePattern) : null;
36
33
  const matches = [];
37
- for (const file of walk.walk(root, { filesOnly: true })) {
38
- if (fileRx && !fileRx.test(path.basename(file.path)) && !fileRx.test(file.relativePath)) continue;
34
+ for (const file of zero.commandWalk.walk(root, { filesOnly: true })) {
35
+ if (fileRx && !fileRx.test(zero.path.basename(file.path)) && !fileRx.test(file.relativePath)) continue;
39
36
  let text;
40
- try { text = textModule.readUtf8(file.path); } catch { continue; }
37
+ try { text = zero.commandText.readUtf8(file.path); } catch { continue; }
41
38
  const lines = text.split(/\r?\n/);
42
39
  for (let index = 0; index < lines.length; index += 1) {
43
40
  const line = lines[index];
@@ -49,6 +46,3 @@ const grepFiles = (args = {}, context = {}) => {
49
46
  }
50
47
  return { ok: true, path: root, pattern: args.pattern, matches };
51
48
  };
52
-
53
- const grepFilesModule = { descriptor, grepFiles };
54
- Object.assign(exports, grepFilesModule);
@@ -1,11 +1,9 @@
1
1
  'use strict';
2
2
 
3
- const crypto = require('node:crypto');
4
- const fs = require('node:fs');
5
- const zero = require('../../zero-runtime');
6
- const pathPolicy = require('./path-policy');
3
+ const zero = require('../../zero');
7
4
 
8
- const descriptor = zero.descriptor.validate({
5
+
6
+ exports.descriptor = zero.descriptor.validate({
9
7
  name: 'zero.command.filesystem.hashFile',
10
8
  aliases: ['zero.command.hashFile'],
11
9
  description: 'Calculate SHA-256 hash of a file inside allowed roots.',
@@ -21,15 +19,12 @@ const descriptor = zero.descriptor.validate({
21
19
  }
22
20
  });
23
21
 
24
- const hashFile = (args = {}, context = {}) => {
25
- const file = pathPolicy.resolveAllowed(args.path, context);
22
+ exports.hashFile = (args = {}, context = {}) => {
23
+ const file = zero.pathPolicy.resolveAllowed(args.path, context);
26
24
  return {
27
25
  ok: true,
28
26
  path: file,
29
27
  algorithm: 'sha256',
30
- hash: crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex')
28
+ hash: zero.crypto.createHash('sha256').update(zero.fs.readFileSync(file)).digest('hex')
31
29
  };
32
30
  };
33
-
34
- const hashFileModule = { descriptor, hashFile };
35
- Object.assign(exports, hashFileModule);
@@ -1,34 +1,33 @@
1
- 'use strict';
2
-
3
- const modules = {
4
- readFile: require('./read-file'),
5
- readFiles: require('./read-files'),
6
- listDirectory: require('./list-directory'),
7
- getFileInfo: require('./get-file-info'),
8
- globFiles: require('./glob-files'),
9
- grepFiles: require('./grep-files'),
10
- writeFile: require('./write-file'),
11
- appendFile: require('./append-file'),
12
- createFile: require('./create-file'),
13
- touchFile: require('./touch-file'),
14
- replaceFile: require('./replace-file'),
15
- createDirectory: require('./create-directory'),
16
- deleteFile: require('./delete-file'),
17
- copyDirectory: require('./copy-directory'),
18
- moveDirectory: require('./move-directory'),
19
- deleteDirectory: require('./delete-directory'),
20
- truncateFile: require('./truncate-file'),
21
- copyFile: require('./copy-file'),
22
- moveFile: require('./move-file'),
23
- renameFile: require('./rename-file'),
24
- exists: require('./exists'),
25
- hashFile: require('./hash-file')
26
- };
27
-
28
- const filesystem = {};
29
- for (const [name, item] of Object.entries(modules)) {
30
- filesystem[name] = item[name];
31
- filesystem[`${name}Descriptor`] = item.descriptor;
32
- }
33
-
34
- Object.assign(exports, filesystem);
1
+ 'use strict';
2
+
3
+ const zero = require('../../zero');
4
+
5
+ const modules = {
6
+ readFile: zero.readFileTool,
7
+ readFiles: zero.readFilesTool,
8
+ listDirectory: zero.listDirectoryTool,
9
+ getFileInfo: zero.getFileInfoTool,
10
+ globFiles: zero.globFilesTool,
11
+ grepFiles: zero.grepFilesTool,
12
+ writeFile: zero.writeFileTool,
13
+ appendFile: zero.appendFileTool,
14
+ createFile: zero.createFileTool,
15
+ touchFile: zero.touchFileTool,
16
+ replaceFile: zero.replaceFileTool,
17
+ createDirectory: zero.createDirectoryTool,
18
+ deleteFile: zero.deleteFileTool,
19
+ copyDirectory: zero.copyDirectoryTool,
20
+ moveDirectory: zero.moveDirectoryTool,
21
+ deleteDirectory: zero.deleteDirectoryTool,
22
+ truncateFile: zero.truncateFileTool,
23
+ copyFile: zero.copyFileTool,
24
+ moveFile: zero.moveFileTool,
25
+ renameFile: zero.renameFileTool,
26
+ exists: zero.existsTool,
27
+ hashFile: zero.hashFileTool
28
+ };
29
+
30
+ for (const [name, item] of Object.entries(modules)) {
31
+ exports[name] = item[name];
32
+ exports[`${name}Descriptor`] = item.descriptor;
33
+ }
@@ -1,10 +1,9 @@
1
1
  'use strict';
2
2
 
3
- const zero = require('../../zero-runtime');
4
- const pathPolicy = require('./path-policy');
5
- const walk = require('./walk');
3
+ const zero = require('../../zero');
6
4
 
7
- const descriptor = zero.descriptor.validate({
5
+
6
+ exports.descriptor = zero.descriptor.validate({
8
7
  name: 'zero.command.filesystem.listDirectory',
9
8
  aliases: ['zero.command.listDirectory'],
10
9
  description: 'List files and directories recursively inside allowed roots.',
@@ -23,11 +22,8 @@ const descriptor = zero.descriptor.validate({
23
22
  }
24
23
  });
25
24
 
26
- const listDirectory = (args = {}, context = {}) => {
27
- const root = pathPolicy.resolveAllowed(args.path, context);
25
+ exports.listDirectory = (args = {}, context = {}) => {
26
+ const root = zero.pathPolicy.resolveAllowed(args.path, context);
28
27
  const depth = Math.max(1, Math.min(args.depth || 2, 5));
29
- return { ok: true, path: root, depth, entries: walk.walk(root, { depth }) };
28
+ return { ok: true, path: root, depth, entries: zero.commandWalk.walk(root, { depth }) };
30
29
  };
31
-
32
- const listDirectoryModule = { descriptor, listDirectory };
33
- Object.assign(exports, listDirectoryModule);
@@ -1,11 +1,9 @@
1
1
  'use strict';
2
2
 
3
- const fs = require('node:fs');
4
- const path = require('node:path');
5
- const zero = require('../../zero-runtime');
6
- const pathPolicy = require('./path-policy');
3
+ const zero = require('../../zero');
7
4
 
8
- const descriptor = zero.descriptor.validate({
5
+
6
+ exports.descriptor = zero.descriptor.validate({
9
7
  name: 'zero.command.filesystem.moveDirectory',
10
8
  aliases: ['zero.command.moveDirectory'],
11
9
  description: 'Move a directory inside allowed roots.',
@@ -17,14 +15,11 @@ const descriptor = zero.descriptor.validate({
17
15
  });
18
16
 
19
17
  const fail = (code, message) => Object.assign(new Error(message), { code });
20
- const moveDirectory = (args = {}, context = {}) => {
21
- const source = pathPolicy.resolveAllowed(args.source, context);
22
- const destination = pathPolicy.resolveAllowed(args.destination, context);
23
- if (!fs.statSync(source).isDirectory()) throw fail('NOT_A_DIRECTORY', 'source is not a directory');
24
- fs.mkdirSync(path.dirname(destination), { recursive: true });
25
- fs.renameSync(source, destination);
18
+ exports.moveDirectory = (args = {}, context = {}) => {
19
+ const source = zero.pathPolicy.resolveAllowed(args.source, context);
20
+ const destination = zero.pathPolicy.resolveAllowed(args.destination, context);
21
+ if (!zero.fs.statSync(source).isDirectory()) throw fail('NOT_A_DIRECTORY', 'source is not a directory');
22
+ zero.fs.mkdirSync(zero.path.dirname(destination), { recursive: true });
23
+ zero.fs.renameSync(source, destination);
26
24
  return { ok: true, source, destination };
27
25
  };
28
-
29
- const moveDirectoryModule = { descriptor, moveDirectory };
30
- Object.assign(exports, moveDirectoryModule);
@@ -1,11 +1,9 @@
1
1
  'use strict';
2
2
 
3
- const fs = require('node:fs');
4
- const path = require('node:path');
5
- const zero = require('../../zero-runtime');
6
- const pathPolicy = require('./path-policy');
3
+ const zero = require('../../zero');
7
4
 
8
- const descriptor = zero.descriptor.validate({
5
+
6
+ exports.descriptor = zero.descriptor.validate({
9
7
  name: 'zero.command.filesystem.moveFile',
10
8
  aliases: ['zero.command.moveFile'],
11
9
  description: 'Move one file or directory inside allowed roots.',
@@ -21,13 +19,10 @@ const descriptor = zero.descriptor.validate({
21
19
  }
22
20
  });
23
21
 
24
- const moveFile = (args = {}, context = {}) => {
25
- const source = pathPolicy.resolveAllowed(args.source, context);
26
- const destination = pathPolicy.resolveAllowed(args.destination, context);
27
- fs.mkdirSync(path.dirname(destination), { recursive: true });
28
- fs.renameSync(source, destination);
22
+ exports.moveFile = (args = {}, context = {}) => {
23
+ const source = zero.pathPolicy.resolveAllowed(args.source, context);
24
+ const destination = zero.pathPolicy.resolveAllowed(args.destination, context);
25
+ zero.fs.mkdirSync(zero.path.dirname(destination), { recursive: true });
26
+ zero.fs.renameSync(source, destination);
29
27
  return { ok: true, source, destination };
30
28
  };
31
-
32
- const moveFileModule = { descriptor, moveFile };
33
- Object.assign(exports, moveFileModule);
@@ -1,31 +1,48 @@
1
1
  'use strict';
2
2
 
3
- const os = require('node:os');
4
- const path = require('node:path');
3
+ const zero = require('../../zero');
4
+
5
5
 
6
6
  const err = (code, message) => Object.assign(new Error(message), { code });
7
- const normalize = (value) => path.resolve(String(value)).toLowerCase();
8
- const rootsFor = (context = {}, env = context.env || process.env) => {
7
+ const normalize = (value) => zero.path.resolve(String(value)).toLowerCase();
8
+ exports.storeFile = (env = process.env) => zero.path.resolve(
9
+ env.ZERO_DEVICE_PATH_STORE || zero.path.join(zero.os.homedir(), '.zero', 'device-paths.json')
10
+ );
11
+
12
+ exports.loadDeviceRoots = (env = process.env) => {
13
+ try {
14
+ const data = JSON.parse(zero.fs.readFileSync(exports.storeFile(env), 'utf8'));
15
+ return Array.isArray(data.allowed_roots) ? data.allowed_roots : [];
16
+ } catch {
17
+ return [];
18
+ }
19
+ };
20
+
21
+ exports.rootsFor = (context = {}, env = context.env || process.env) => {
9
22
  const explicit = Array.isArray(context.commandRoots) && context.commandRoots.length
10
23
  ? context.commandRoots
11
- : (Array.isArray(context.roots) ? context.roots : []);
12
- const configured = explicit.length ? explicit : String(env.ZERO_AGENT_ROOTS || env.ZERO_COMMAND_ROOTS || '')
13
- .split(';').map((item) => item.trim()).filter(Boolean);
14
- const roots = configured.length ? configured : [os.homedir(), process.cwd()];
15
- return roots.map((item) => path.resolve(String(item)));
24
+ : (Array.isArray(context.roots) && context.roots.length ? context.roots : []);
25
+ if (explicit.length) {
26
+ return [...new Set(explicit.map((item) => zero.path.resolve(String(item))))];
27
+ }
28
+
29
+ const discovered = Array.isArray(context.storageRoots) && context.storageRoots.length
30
+ ? context.storageRoots
31
+ : zero.storageRoots.discover(context.system || {});
32
+ return [...new Set(discovered.map((item) => zero.path.resolve(String(item))))];
16
33
  };
17
- const resolveAllowed = (inputPath, context = {}, env = context.env || process.env) => {
34
+
35
+ exports.resolveAllowed = (inputPath, context = {}, env = context.env || process.env) => {
18
36
  if (!inputPath || typeof inputPath !== 'string') throw err('INVALID_PATH', 'path is required');
19
- const roots = rootsFor(context, env);
20
- const target = path.resolve(inputPath);
37
+ const roots = exports.rootsFor(context, env);
38
+ const target = zero.path.resolve(inputPath);
21
39
  const targetNorm = normalize(target);
22
40
  const ok = roots.some((root) => {
23
41
  const rootNorm = normalize(root);
24
- return targetNorm === rootNorm || targetNorm.startsWith(rootNorm + path.sep.toLowerCase());
42
+ const separator = zero.path.sep.toLowerCase();
43
+ const prefix = rootNorm.endsWith(separator) ? rootNorm : rootNorm + separator;
44
+ return targetNorm === rootNorm || targetNorm.startsWith(prefix);
25
45
  });
26
46
  if (!ok) throw err('PATH_NOT_ALLOWED', 'path is outside agent allowed roots');
27
47
  return target;
28
48
  };
29
-
30
- const pathPolicy = { rootsFor, resolveAllowed };
31
- Object.assign(exports, pathPolicy);
@@ -1,10 +1,9 @@
1
1
  'use strict';
2
2
 
3
- const zero = require('../../zero-runtime');
4
- const pathPolicy = require('./path-policy');
5
- const text = require('./text');
3
+ const zero = require('../../zero');
6
4
 
7
- const descriptor = zero.descriptor.validate({
5
+
6
+ exports.descriptor = zero.descriptor.validate({
8
7
  name: 'zero.command.filesystem.readFile',
9
8
  aliases: ['zero.command.readFile'],
10
9
  description: 'Read a UTF-8 text file inside allowed roots with line pagination.',
@@ -23,15 +22,12 @@ const descriptor = zero.descriptor.validate({
23
22
  additionalProperties: false
24
23
  }
25
24
  });
26
- const readFile = (args = {}, context = {}) => {
27
- const file = pathPolicy.resolveAllowed(args.path, context);
28
- const lines = text.readUtf8(file).split(/\r?\n/);
25
+ exports.readFile = (args = {}, context = {}) => {
26
+ const file = zero.pathPolicy.resolveAllowed(args.path, context);
27
+ const lines = zero.commandText.readUtf8(file).split(/\r?\n/);
29
28
  const offset = Number.isInteger(args.offset) ? args.offset : 0;
30
29
  const length = Number.isInteger(args.length) ? args.length : 200;
31
30
  const start = offset < 0 ? Math.max(lines.length + offset, 0) : Math.max(offset, 0);
32
31
  const slice = lines.slice(start, start + length);
33
- return { ok: true, path: file, totalLines: lines.length, offset: start, length: slice.length, content: text.numbered(slice, start) };
32
+ return { ok: true, path: file, totalLines: lines.length, offset: start, length: slice.length, content: zero.commandText.numbered(slice, start) };
34
33
  };
35
-
36
- const readFileModule = { descriptor, readFile };
37
- Object.assign(exports, readFileModule);
@@ -1,9 +1,9 @@
1
1
  'use strict';
2
2
 
3
- const zero = require('../../zero-runtime');
4
- const readFileModule = require('./read-file');
3
+ const zero = require('../../zero');
5
4
 
6
- const descriptor = zero.descriptor.validate({
5
+
6
+ exports.descriptor = zero.descriptor.validate({
7
7
  name: 'zero.command.filesystem.readFiles',
8
8
  aliases: ['zero.command.readFiles'],
9
9
  description: 'Read multiple UTF-8 text files inside allowed roots.',
@@ -36,14 +36,14 @@ const descriptor = zero.descriptor.validate({
36
36
  });
37
37
 
38
38
  const fail = (code, message) => Object.assign(new Error(message), { code });
39
- const readFiles = (args = {}, context = {}) => {
39
+ exports.readFiles = (args = {}, context = {}) => {
40
40
  const requests = Array.isArray(args.files) && args.files.length
41
41
  ? args.files.map((item) => ({ path: item.path, offset: item.offset, length: item.length }))
42
42
  : (Array.isArray(args.paths) ? args.paths : []).map((path) => ({ path, offset: args.offset, length: args.length }));
43
43
  if (!requests.length) throw fail('INVALID_PATHS', 'paths or files must contain at least one file');
44
44
  if (requests.length > 50) throw fail('TOO_MANY_PATHS', 'readFiles supports up to 50 files');
45
45
  const files = requests.map((request) => {
46
- try { return readFileModule.readFile(request, context); }
46
+ try { return zero.readFileTool.readFile(request, context); }
47
47
  catch (error) {
48
48
  return { ok: false, path: String(request.path), error: { code: error.code || 'READ_FAILED', message: error.message || String(error) } };
49
49
  }
@@ -51,6 +51,3 @@ const readFiles = (args = {}, context = {}) => {
51
51
  const succeeded = files.filter((item) => item.ok).length;
52
52
  return { ok: succeeded === files.length, count: files.length, succeeded, failed: files.length - succeeded, files };
53
53
  };
54
-
55
- const readFilesModule = { descriptor, readFiles };
56
- Object.assign(exports, readFilesModule);
@@ -1,9 +1,9 @@
1
1
  'use strict';
2
2
 
3
- const zero = require('../../zero-runtime');
4
- const moveFileModule = require('./move-file');
3
+ const zero = require('../../zero');
5
4
 
6
- const descriptor = zero.descriptor.validate({
5
+
6
+ exports.descriptor = zero.descriptor.validate({
7
7
  name: 'zero.command.filesystem.renameFile',
8
8
  aliases: ['zero.command.renameFile'],
9
9
  description: 'Rename one file or directory inside allowed roots.',
@@ -11,10 +11,7 @@ const descriptor = zero.descriptor.validate({
11
11
  risk: 'write',
12
12
  execution: 'target',
13
13
  adapters: ['native'],
14
- inputSchema: moveFileModule.descriptor.inputSchema
14
+ inputSchema: zero.moveFileTool.descriptor.inputSchema
15
15
  });
16
16
 
17
- const renameFile = (args = {}, context = {}) => moveFileModule.moveFile(args, context);
18
-
19
- const renameFileModule = { descriptor, renameFile };
20
- Object.assign(exports, renameFileModule);
17
+ exports.renameFile = (args = {}, context = {}) => zero.moveFileTool.moveFile(args, context);
@@ -1,11 +1,9 @@
1
1
  'use strict';
2
2
 
3
- const fs = require('node:fs');
4
- const zero = require('../../zero-runtime');
5
- const pathPolicy = require('./path-policy');
6
- const textModule = require('./text');
3
+ const zero = require('../../zero');
7
4
 
8
- const descriptor = zero.descriptor.validate({
5
+
6
+ exports.descriptor = zero.descriptor.validate({
9
7
  name: 'zero.command.filesystem.replaceFile',
10
8
  aliases: ['zero.command.replaceFile'],
11
9
  description: 'Replace exact text inside a UTF-8 file in allowed roots.',
@@ -27,19 +25,16 @@ const descriptor = zero.descriptor.validate({
27
25
  });
28
26
 
29
27
  const fail = (code, message) => Object.assign(new Error(message), { code });
30
- const replaceFile = (args = {}, context = {}) => {
31
- const file = pathPolicy.resolveAllowed(args.path, context);
28
+ exports.replaceFile = (args = {}, context = {}) => {
29
+ const file = zero.pathPolicy.resolveAllowed(args.path, context);
32
30
  const oldString = String(args.oldString ?? '');
33
31
  if (!oldString) throw fail('INVALID_REPLACE', 'oldString must not be empty');
34
32
  const newString = String(args.newString ?? '');
35
- const text = textModule.readUtf8(file);
33
+ const text = zero.commandText.readUtf8(file);
36
34
  const replacements = text.split(oldString).length - 1;
37
35
  if (Number.isInteger(args.expectedReplacements) && replacements !== args.expectedReplacements) {
38
36
  throw fail('REPLACE_COUNT_MISMATCH', `expected ${args.expectedReplacements} replacements, got ${replacements}`);
39
37
  }
40
- fs.writeFileSync(file, text.split(oldString).join(newString), 'utf8');
38
+ zero.fs.writeFileSync(file, text.split(oldString).join(newString), 'utf8');
41
39
  return { ok: true, path: file, replacements };
42
40
  };
43
-
44
- const replaceFileModule = { descriptor, replaceFile };
45
- Object.assign(exports, replaceFileModule);
@@ -1,17 +1,15 @@
1
1
  'use strict';
2
2
 
3
- const fs = require('node:fs');
3
+ const zero = require('../../zero');
4
+
4
5
 
5
6
  const textDecoder = new TextDecoder('utf-8', { fatal: false });
6
7
  const err = (code, message) => Object.assign(new Error(message), { code });
7
- const readUtf8 = (file) => {
8
- const data = fs.readFileSync(file);
8
+ exports.readUtf8 = (file) => {
9
+ const data = zero.fs.readFileSync(file);
9
10
  if (data.includes(0)) throw err('BINARY_FILE', 'binary file is not supported');
10
11
  return textDecoder.decode(data);
11
12
  };
12
- const numbered = (lines, startIndex) => lines
13
+ exports.numbered = (lines, startIndex) => lines
13
14
  .map((line, index) => String(startIndex + index + 1).padStart(4, ' ') + '\t' + line)
14
15
  .join('\n');
15
-
16
- const text = { readUtf8, numbered };
17
- Object.assign(exports, text);
@@ -1,11 +1,9 @@
1
1
  'use strict';
2
2
 
3
- const fs = require('node:fs');
4
- const path = require('node:path');
5
- const zero = require('../../zero-runtime');
6
- const pathPolicy = require('./path-policy');
3
+ const zero = require('../../zero');
7
4
 
8
- const descriptor = zero.descriptor.validate({
5
+
6
+ exports.descriptor = zero.descriptor.validate({
9
7
  name: 'zero.command.filesystem.touchFile',
10
8
  aliases: ['zero.command.touchFile'],
11
9
  description: 'Create or update a file timestamp inside allowed roots.',
@@ -16,14 +14,11 @@ const descriptor = zero.descriptor.validate({
16
14
  inputSchema: { type: 'object', properties: { path: { type: 'string' } }, required: ['path'], additionalProperties: false }
17
15
  });
18
16
 
19
- const touchFile = (args = {}, context = {}) => {
20
- const file = pathPolicy.resolveAllowed(args.path, context);
21
- fs.mkdirSync(path.dirname(file), { recursive: true });
22
- if (!fs.existsSync(file)) fs.closeSync(fs.openSync(file, 'w'));
17
+ exports.touchFile = (args = {}, context = {}) => {
18
+ const file = zero.pathPolicy.resolveAllowed(args.path, context);
19
+ zero.fs.mkdirSync(zero.path.dirname(file), { recursive: true });
20
+ if (!zero.fs.existsSync(file)) zero.fs.closeSync(zero.fs.openSync(file, 'w'));
23
21
  const now = new Date();
24
- fs.utimesSync(file, now, now);
22
+ zero.fs.utimesSync(file, now, now);
25
23
  return { ok: true, path: file, touched: true };
26
24
  };
27
-
28
- const touchFileModule = { descriptor, touchFile };
29
- Object.assign(exports, touchFileModule);
@@ -1,10 +1,9 @@
1
1
  'use strict';
2
2
 
3
- const fs = require('node:fs');
4
- const zero = require('../../zero-runtime');
5
- const pathPolicy = require('./path-policy');
3
+ const zero = require('../../zero');
6
4
 
7
- const descriptor = zero.descriptor.validate({
5
+
6
+ exports.descriptor = zero.descriptor.validate({
8
7
  name: 'zero.command.filesystem.truncateFile',
9
8
  aliases: ['zero.command.truncateFile'],
10
9
  description: 'Truncate a file to a specific byte size inside allowed roots.',
@@ -15,12 +14,9 @@ const descriptor = zero.descriptor.validate({
15
14
  inputSchema: { type: 'object', properties: { path: { type: 'string' }, size: { type: 'integer', minimum: 0, default: 0 } }, required: ['path'], additionalProperties: false }
16
15
  });
17
16
 
18
- const truncateFile = (args = {}, context = {}) => {
19
- const file = pathPolicy.resolveAllowed(args.path, context);
17
+ exports.truncateFile = (args = {}, context = {}) => {
18
+ const file = zero.pathPolicy.resolveAllowed(args.path, context);
20
19
  const size = Number.isInteger(args.size) && args.size >= 0 ? args.size : 0;
21
- fs.truncateSync(file, size);
20
+ zero.fs.truncateSync(file, size);
22
21
  return { ok: true, path: file, size };
23
22
  };
24
-
25
- const truncateFileModule = { descriptor, truncateFile };
26
- Object.assign(exports, truncateFileModule);