@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.
- package/README.md +31 -22
- package/package.json +6 -3
- package/src/agent.js +193 -167
- package/src/args.js +1 -3
- package/src/autostart.js +44 -68
- package/src/capabilities.js +95 -138
- package/src/chatgpt/auth/index.js +114 -0
- package/src/chatgpt/config/paths.js +25 -0
- package/src/chatgpt/index.js +5 -0
- package/src/chatgpt/session/direct-transport.js +68 -0
- package/src/chatgpt/session/exchange.js +17 -0
- package/src/chatgpt/session/index.js +102 -0
- package/src/chatgpt/session/local-storage.js +106 -0
- package/src/chatgpt/transport/direct/http-client.js +191 -0
- package/src/chatgpt-auth.js +17 -111
- package/src/chatgpt-runtime.js +8 -0
- package/src/chatgpt-session-exchange.js +8 -0
- package/src/chatgpt-session.js +26 -0
- package/src/cli.js +108 -71
- package/src/command/exec.js +34 -0
- package/src/command/filesystem/append-file.js +7 -12
- package/src/command/filesystem/copy-directory.js +8 -12
- package/src/command/filesystem/copy-file.js +8 -13
- package/src/command/filesystem/create-directory.js +7 -11
- package/src/command/filesystem/create-file.js +8 -13
- package/src/command/filesystem/delete-directory.js +7 -11
- package/src/command/filesystem/delete-file.js +8 -12
- package/src/command/filesystem/exists.js +6 -10
- package/src/command/filesystem/get-file-info.js +7 -12
- package/src/command/filesystem/glob-files.js +8 -13
- package/src/command/filesystem/grep-files.js +9 -15
- package/src/command/filesystem/hash-file.js +6 -11
- package/src/command/filesystem/index.js +33 -34
- package/src/command/filesystem/list-directory.js +6 -10
- package/src/command/filesystem/move-directory.js +9 -14
- package/src/command/filesystem/move-file.js +8 -13
- package/src/command/filesystem/path-policy.js +33 -16
- package/src/command/filesystem/read-file.js +7 -11
- package/src/command/filesystem/read-files.js +5 -8
- package/src/command/filesystem/rename-file.js +5 -8
- package/src/command/filesystem/replace-file.js +7 -12
- package/src/command/filesystem/text.js +5 -7
- package/src/command/filesystem/touch-file.js +8 -13
- package/src/command/filesystem/truncate-file.js +6 -10
- package/src/command/filesystem/walk.js +36 -39
- package/src/command/filesystem/write-file.js +8 -13
- package/src/command/index.js +19 -24
- package/src/command/path/index.js +46 -53
- package/src/config.js +21 -33
- package/src/device-paths.js +53 -0
- package/src/enrollment-client.js +19 -0
- package/src/http.js +27 -2
- package/src/identity-state.js +27 -0
- package/src/index.js +2 -4
- package/src/instance.js +85 -0
- package/src/local-server.js +174 -0
- package/src/local-tools.js +205 -205
- package/src/shared-zero/catalog.js +33 -0
- package/src/shared-zero/contract.js +18 -0
- package/src/shared-zero/descriptor.js +25 -0
- package/src/shared-zero/errors.js +12 -0
- package/src/shared-zero/index.js +9 -0
- package/src/shared-zero/runtime.js +14 -0
- package/src/shared-zero/target.js +19 -0
- package/src/shared-zero/tool-id.js +26 -0
- package/src/storage-roots.js +21 -0
- package/src/zero-runtime.js +1 -11
- package/src/zero.js +78 -0
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
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() ?
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
29
|
-
const root = pathPolicy.resolveAllowed(args.path, context);
|
|
30
|
-
const rx =
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 ?
|
|
32
|
+
const fileRx = args.filePattern ? zero.commandWalk.wildcard(args.filePattern) : null;
|
|
36
33
|
const matches = [];
|
|
37
|
-
for (const file of
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
|
4
|
-
const pathPolicy = require('./path-policy');
|
|
5
|
-
const walk = require('./walk');
|
|
3
|
+
const zero = require('../../zero');
|
|
6
4
|
|
|
7
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
4
|
-
|
|
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
|
-
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
4
|
-
const pathPolicy = require('./path-policy');
|
|
5
|
-
const text = require('./text');
|
|
3
|
+
const zero = require('../../zero');
|
|
6
4
|
|
|
7
|
-
|
|
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
|
-
|
|
27
|
-
const file = pathPolicy.resolveAllowed(args.path, context);
|
|
28
|
-
const lines =
|
|
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:
|
|
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
|
|
4
|
-
const readFileModule = require('./read-file');
|
|
3
|
+
const zero = require('../../zero');
|
|
5
4
|
|
|
6
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
4
|
-
const moveFileModule = require('./move-file');
|
|
3
|
+
const zero = require('../../zero');
|
|
5
4
|
|
|
6
|
-
|
|
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:
|
|
14
|
+
inputSchema: zero.moveFileTool.descriptor.inputSchema
|
|
15
15
|
});
|
|
16
16
|
|
|
17
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
4
|
-
const zero = require('../../zero-runtime');
|
|
5
|
-
const pathPolicy = require('./path-policy');
|
|
3
|
+
const zero = require('../../zero');
|
|
6
4
|
|
|
7
|
-
|
|
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
|
-
|
|
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);
|