@framers/agentos-ext-cli-executor 1.1.0 → 1.1.1
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 +80 -28
- package/dist/index.d.ts +54 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +60 -18
- package/dist/index.js.map +1 -1
- package/dist/services/shellService.d.ts +6 -1
- package/dist/services/shellService.d.ts.map +1 -1
- package/dist/services/shellService.js +82 -10
- package/dist/services/shellService.js.map +1 -1
- package/dist/tools/execute.d.ts +15 -18
- package/dist/tools/execute.d.ts.map +1 -1
- package/dist/tools/execute.js +36 -36
- package/dist/tools/execute.js.map +1 -1
- package/dist/tools/fileRead.d.ts +15 -18
- package/dist/tools/fileRead.d.ts.map +1 -1
- package/dist/tools/fileRead.js +39 -39
- package/dist/tools/fileRead.js.map +1 -1
- package/dist/tools/fileWrite.d.ts +15 -18
- package/dist/tools/fileWrite.d.ts.map +1 -1
- package/dist/tools/fileWrite.js +40 -40
- package/dist/tools/fileWrite.js.map +1 -1
- package/dist/tools/listDir.d.ts +15 -18
- package/dist/tools/listDir.d.ts.map +1 -1
- package/dist/tools/listDir.js +45 -45
- package/dist/tools/listDir.js.map +1 -1
- package/dist/types.d.ts +38 -0
- package/dist/types.d.ts.map +1 -1
- package/manifest.json +6 -14
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -18,30 +18,36 @@ npm install @framers/agentos-ext-cli-executor
|
|
|
18
18
|
## Quick Start
|
|
19
19
|
|
|
20
20
|
```typescript
|
|
21
|
-
import { createExtensionPack } from '@framers/agentos-ext-cli-executor';
|
|
22
21
|
import { ExtensionManager } from '@framers/agentos';
|
|
22
|
+
import { createExtensionPack } from '@framers/agentos-ext-cli-executor';
|
|
23
23
|
|
|
24
24
|
const extensionManager = new ExtensionManager();
|
|
25
25
|
|
|
26
|
-
//
|
|
27
|
-
extensionManager.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
26
|
+
// Load the pack into the runtime
|
|
27
|
+
await extensionManager.loadPackFromFactory(
|
|
28
|
+
createExtensionPack({
|
|
29
|
+
options: {
|
|
30
|
+
defaultShell: 'bash',
|
|
31
|
+
timeout: 60000,
|
|
32
|
+
blockedCommands: ['rm -rf /', 'format'],
|
|
33
|
+
// Restrict file_* tools to a per-agent workspace
|
|
34
|
+
filesystem: { allowRead: true, allowWrite: true },
|
|
35
|
+
agentWorkspace: { agentId: 'my-agent' },
|
|
36
|
+
},
|
|
37
|
+
logger: console,
|
|
38
|
+
}),
|
|
39
|
+
'@framers/agentos-ext-cli-executor',
|
|
40
|
+
);
|
|
35
41
|
```
|
|
36
42
|
|
|
37
43
|
## Tools
|
|
38
44
|
|
|
39
|
-
###
|
|
45
|
+
### shell_execute
|
|
40
46
|
|
|
41
47
|
Execute a shell command.
|
|
42
48
|
|
|
43
49
|
```typescript
|
|
44
|
-
const result = await gmi.executeTool('
|
|
50
|
+
const result = await gmi.executeTool('shell_execute', {
|
|
45
51
|
command: 'npm install lodash',
|
|
46
52
|
cwd: '/path/to/project',
|
|
47
53
|
timeout: 30000
|
|
@@ -49,31 +55,31 @@ const result = await gmi.executeTool('shellExecute', {
|
|
|
49
55
|
// Returns: { command, exitCode, stdout, stderr, duration, success }
|
|
50
56
|
```
|
|
51
57
|
|
|
52
|
-
###
|
|
58
|
+
### file_read
|
|
53
59
|
|
|
54
60
|
Read file contents.
|
|
55
61
|
|
|
56
62
|
```typescript
|
|
57
|
-
const result = await gmi.executeTool('
|
|
63
|
+
const result = await gmi.executeTool('file_read', {
|
|
58
64
|
path: './package.json',
|
|
59
65
|
encoding: 'utf-8'
|
|
60
66
|
});
|
|
61
67
|
// Returns: { path, content, size, truncated, encoding }
|
|
62
68
|
|
|
63
69
|
// Read last 50 lines
|
|
64
|
-
const logs = await gmi.executeTool('
|
|
70
|
+
const logs = await gmi.executeTool('file_read', {
|
|
65
71
|
path: './app.log',
|
|
66
72
|
lines: 50,
|
|
67
73
|
fromEnd: true
|
|
68
74
|
});
|
|
69
75
|
```
|
|
70
76
|
|
|
71
|
-
###
|
|
77
|
+
### file_write
|
|
72
78
|
|
|
73
79
|
Write content to a file.
|
|
74
80
|
|
|
75
81
|
```typescript
|
|
76
|
-
const result = await gmi.executeTool('
|
|
82
|
+
const result = await gmi.executeTool('file_write', {
|
|
77
83
|
path: './config.json',
|
|
78
84
|
content: JSON.stringify({ key: 'value' }),
|
|
79
85
|
createDirs: true
|
|
@@ -81,12 +87,12 @@ const result = await gmi.executeTool('fileWrite', {
|
|
|
81
87
|
// Returns: { path, bytesWritten, created, appended }
|
|
82
88
|
```
|
|
83
89
|
|
|
84
|
-
###
|
|
90
|
+
### list_directory
|
|
85
91
|
|
|
86
92
|
List directory contents.
|
|
87
93
|
|
|
88
94
|
```typescript
|
|
89
|
-
const result = await gmi.executeTool('
|
|
95
|
+
const result = await gmi.executeTool('list_directory', {
|
|
90
96
|
path: './src',
|
|
91
97
|
recursive: true,
|
|
92
98
|
pattern: '*.ts',
|
|
@@ -119,6 +125,18 @@ createExtensionPack({
|
|
|
119
125
|
});
|
|
120
126
|
```
|
|
121
127
|
|
|
128
|
+
### Disabling Safety Checks (Dangerous)
|
|
129
|
+
|
|
130
|
+
If you need full control (for example, in a locked-down container or local dev), you can disable all command safety checks:
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
createExtensionPack({
|
|
134
|
+
options: {
|
|
135
|
+
dangerouslySkipSecurityChecks: true
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
122
140
|
### Risk Assessment
|
|
123
141
|
|
|
124
142
|
Each command is assessed for risk level:
|
|
@@ -138,24 +156,61 @@ Each command is assessed for risk level:
|
|
|
138
156
|
| `defaultShell` | string | `auto` | Shell to use (bash, powershell, cmd, zsh) |
|
|
139
157
|
| `timeout` | number | `60000` | Default timeout (ms) |
|
|
140
158
|
| `workingDirectory` | string | `process.cwd()` | Default working directory |
|
|
159
|
+
| `filesystem` | object | `undefined` | Optional policy for file_* tools (allowRead/allowWrite + roots) |
|
|
160
|
+
| `agentWorkspace` | object | `undefined` | Optional per-agent workspace directory helper |
|
|
141
161
|
| `allowedCommands` | string[] | `[]` | Command whitelist (empty = all) |
|
|
142
|
-
| `blockedCommands` | string[] | `[
|
|
162
|
+
| `blockedCommands` | string[] | `[]` | Command blacklist (additional to built-in dangerous patterns) |
|
|
163
|
+
| `dangerouslySkipSecurityChecks` | boolean | `false` | Disable all command safety checks (use only in trusted environments) |
|
|
143
164
|
| `env` | object | `{}` | Environment variables |
|
|
144
165
|
|
|
166
|
+
### Filesystem Policy (Recommended)
|
|
167
|
+
|
|
168
|
+
By default, the `file_*` tools can access any path (legacy behavior). To enforce a safe filesystem sandbox, configure `filesystem` + roots:
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
createExtensionPack({
|
|
172
|
+
options: {
|
|
173
|
+
filesystem: {
|
|
174
|
+
allowRead: true,
|
|
175
|
+
allowWrite: true,
|
|
176
|
+
readRoots: ['/Users/me/Documents/AgentOS/agents/my-agent'],
|
|
177
|
+
writeRoots: ['/Users/me/Documents/AgentOS/agents/my-agent'],
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Per-Agent Workspace Helper
|
|
184
|
+
|
|
185
|
+
To simplify safe defaults, you can configure `agentWorkspace`. When paired with `filesystem.allowRead/allowWrite`, the extension defaults roots to the workspace directory.
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
createExtensionPack({
|
|
189
|
+
options: {
|
|
190
|
+
filesystem: { allowRead: true, allowWrite: true },
|
|
191
|
+
agentWorkspace: {
|
|
192
|
+
agentId: 'my-agent',
|
|
193
|
+
// baseDir defaults to ~/Documents/AgentOS
|
|
194
|
+
subdirs: ['assets', 'exports', 'tmp'],
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
```
|
|
199
|
+
|
|
145
200
|
## Use Cases
|
|
146
201
|
|
|
147
202
|
### Code Generation and Execution
|
|
148
203
|
|
|
149
204
|
```typescript
|
|
150
205
|
// Write generated code
|
|
151
|
-
await gmi.executeTool('
|
|
206
|
+
await gmi.executeTool('file_write', {
|
|
152
207
|
path: './generated/app.py',
|
|
153
208
|
content: generatedPythonCode,
|
|
154
209
|
createDirs: true
|
|
155
210
|
});
|
|
156
211
|
|
|
157
212
|
// Execute it
|
|
158
|
-
await gmi.executeTool('
|
|
213
|
+
await gmi.executeTool('shell_execute', {
|
|
159
214
|
command: 'python ./generated/app.py',
|
|
160
215
|
timeout: 30000
|
|
161
216
|
});
|
|
@@ -165,12 +220,12 @@ await gmi.executeTool('shellExecute', {
|
|
|
165
220
|
|
|
166
221
|
```typescript
|
|
167
222
|
// Create project structure
|
|
168
|
-
await gmi.executeTool('
|
|
223
|
+
await gmi.executeTool('shell_execute', {
|
|
169
224
|
command: 'npx create-react-app my-app --template typescript'
|
|
170
225
|
});
|
|
171
226
|
|
|
172
227
|
// Install dependencies
|
|
173
|
-
await gmi.executeTool('
|
|
228
|
+
await gmi.executeTool('shell_execute', {
|
|
174
229
|
command: 'npm install axios lodash',
|
|
175
230
|
cwd: './my-app'
|
|
176
231
|
});
|
|
@@ -180,7 +235,7 @@ await gmi.executeTool('shellExecute', {
|
|
|
180
235
|
|
|
181
236
|
```typescript
|
|
182
237
|
// Read recent logs
|
|
183
|
-
const logs = await gmi.executeTool('
|
|
238
|
+
const logs = await gmi.executeTool('file_read', {
|
|
184
239
|
path: '/var/log/app.log',
|
|
185
240
|
lines: 100,
|
|
186
241
|
fromEnd: true
|
|
@@ -216,6 +271,3 @@ Together, an intelligent agent can:
|
|
|
216
271
|
## License
|
|
217
272
|
|
|
218
273
|
MIT © Frame.dev
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
package/dist/index.d.ts
CHANGED
|
@@ -5,11 +5,14 @@
|
|
|
5
5
|
* capabilities for AgentOS agents.
|
|
6
6
|
*
|
|
7
7
|
* @module @framers/agentos-ext-cli-executor
|
|
8
|
-
* @version 1.
|
|
8
|
+
* @version 1.1.0
|
|
9
9
|
* @license MIT
|
|
10
10
|
*/
|
|
11
|
-
import
|
|
12
|
-
import
|
|
11
|
+
import { ExecuteTool } from './tools/execute.js';
|
|
12
|
+
import { FileReadTool } from './tools/fileRead.js';
|
|
13
|
+
import { FileWriteTool } from './tools/fileWrite.js';
|
|
14
|
+
import { ListDirectoryTool } from './tools/listDir.js';
|
|
15
|
+
import type { ShellConfig } from './types.js';
|
|
13
16
|
/**
|
|
14
17
|
* Extension configuration options
|
|
15
18
|
*/
|
|
@@ -37,12 +40,53 @@ export interface CLIExecutorExtensionOptions extends ShellConfig {
|
|
|
37
40
|
* });
|
|
38
41
|
* ```
|
|
39
42
|
*/
|
|
40
|
-
export declare function createExtensionPack(context:
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
export declare function createExtensionPack(context: {
|
|
44
|
+
options?: Record<string, unknown>;
|
|
45
|
+
secrets?: Record<string, string>;
|
|
46
|
+
logger?: {
|
|
47
|
+
info: (...args: unknown[]) => void;
|
|
48
|
+
};
|
|
49
|
+
onActivate?: () => Promise<void>;
|
|
50
|
+
onDeactivate?: () => Promise<void>;
|
|
51
|
+
[key: string]: unknown;
|
|
52
|
+
}): {
|
|
53
|
+
name: string;
|
|
54
|
+
version: string;
|
|
55
|
+
descriptors: ({
|
|
56
|
+
id: string;
|
|
57
|
+
kind: string;
|
|
58
|
+
priority: number;
|
|
59
|
+
payload: ExecuteTool;
|
|
60
|
+
} | {
|
|
61
|
+
id: string;
|
|
62
|
+
kind: string;
|
|
63
|
+
priority: number;
|
|
64
|
+
payload: FileReadTool;
|
|
65
|
+
} | {
|
|
66
|
+
id: string;
|
|
67
|
+
kind: string;
|
|
68
|
+
priority: number;
|
|
69
|
+
payload: FileWriteTool;
|
|
70
|
+
} | {
|
|
71
|
+
id: string;
|
|
72
|
+
kind: string;
|
|
73
|
+
priority: number;
|
|
74
|
+
payload: ListDirectoryTool;
|
|
75
|
+
})[];
|
|
76
|
+
/**
|
|
77
|
+
* Called when extension is activated
|
|
78
|
+
*/
|
|
79
|
+
onActivate: () => Promise<void>;
|
|
80
|
+
/**
|
|
81
|
+
* Called when extension is deactivated
|
|
82
|
+
*/
|
|
83
|
+
onDeactivate: () => Promise<void>;
|
|
84
|
+
};
|
|
85
|
+
export { ShellService } from './services/shellService.js';
|
|
86
|
+
export { ExecuteTool } from './tools/execute.js';
|
|
87
|
+
export { FileReadTool } from './tools/fileRead.js';
|
|
88
|
+
export { FileWriteTool } from './tools/fileWrite.js';
|
|
89
|
+
export { ListDirectoryTool } from './tools/listDir.js';
|
|
90
|
+
export * from './types.js';
|
|
47
91
|
export default createExtensionPack;
|
|
48
92
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;GAEG;AACH,MAAM,WAAW,2BAA4B,SAAQ,WAAW;IAC9D,sCAAsC;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE;IAC3C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE;QAAE,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAA;KAAE,CAAC;IAChD,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;;;;;;;;;;;;;;;;;;;;;;;;IAuFG;;OAEG;;IAcH;;OAEG;;EAQN;AAGD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,cAAc,YAAY,CAAC;AAG3B,eAAe,mBAAmB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -5,14 +5,17 @@
|
|
|
5
5
|
* capabilities for AgentOS agents.
|
|
6
6
|
*
|
|
7
7
|
* @module @framers/agentos-ext-cli-executor
|
|
8
|
-
* @version 1.
|
|
8
|
+
* @version 1.1.0
|
|
9
9
|
* @license MIT
|
|
10
10
|
*/
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
11
|
+
import * as fs from 'node:fs/promises';
|
|
12
|
+
import * as os from 'node:os';
|
|
13
|
+
import * as path from 'node:path';
|
|
14
|
+
import { ShellService } from './services/shellService.js';
|
|
15
|
+
import { ExecuteTool } from './tools/execute.js';
|
|
16
|
+
import { FileReadTool } from './tools/fileRead.js';
|
|
17
|
+
import { FileWriteTool } from './tools/fileWrite.js';
|
|
18
|
+
import { ListDirectoryTool } from './tools/listDir.js';
|
|
16
19
|
/**
|
|
17
20
|
* Creates the CLI executor extension pack
|
|
18
21
|
*
|
|
@@ -35,13 +38,46 @@ import { ListDirectoryTool } from './tools/listDir';
|
|
|
35
38
|
*/
|
|
36
39
|
export function createExtensionPack(context) {
|
|
37
40
|
const options = context.options || {};
|
|
41
|
+
let workspaceDir;
|
|
42
|
+
let workspaceSubdirs = [];
|
|
43
|
+
const workspaceConfig = options.agentWorkspace;
|
|
44
|
+
if (workspaceConfig && workspaceConfig.enabled !== false) {
|
|
45
|
+
const baseDir = (workspaceConfig.baseDir && String(workspaceConfig.baseDir).trim()) ||
|
|
46
|
+
path.join(os.homedir(), 'Documents', 'AgentOS');
|
|
47
|
+
const agentId = String(workspaceConfig.agentId || '').trim();
|
|
48
|
+
if (agentId) {
|
|
49
|
+
workspaceDir = path.resolve(baseDir, agentId);
|
|
50
|
+
workspaceSubdirs = Array.isArray(workspaceConfig.subdirs) && workspaceConfig.subdirs.length > 0
|
|
51
|
+
? workspaceConfig.subdirs.map((d) => String(d)).filter(Boolean)
|
|
52
|
+
: ['assets', 'exports', 'tmp'];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const workingDirectory = options.workingDirectory || workspaceDir;
|
|
56
|
+
const filesystem = options.filesystem
|
|
57
|
+
? {
|
|
58
|
+
...options.filesystem,
|
|
59
|
+
readRoots: options.filesystem.allowRead === true &&
|
|
60
|
+
(!options.filesystem.readRoots || options.filesystem.readRoots.length === 0) &&
|
|
61
|
+
workspaceDir
|
|
62
|
+
? [workspaceDir]
|
|
63
|
+
: options.filesystem.readRoots,
|
|
64
|
+
writeRoots: options.filesystem.allowWrite === true &&
|
|
65
|
+
(!options.filesystem.writeRoots || options.filesystem.writeRoots.length === 0) &&
|
|
66
|
+
workspaceDir
|
|
67
|
+
? [workspaceDir]
|
|
68
|
+
: options.filesystem.writeRoots,
|
|
69
|
+
}
|
|
70
|
+
: undefined;
|
|
38
71
|
// Initialize shell service with configuration
|
|
39
72
|
const shellService = new ShellService({
|
|
40
73
|
defaultShell: options.defaultShell,
|
|
41
74
|
timeout: options.timeout,
|
|
42
|
-
workingDirectory
|
|
75
|
+
workingDirectory,
|
|
76
|
+
filesystem,
|
|
77
|
+
agentWorkspace: options.agentWorkspace,
|
|
43
78
|
allowedCommands: options.allowedCommands,
|
|
44
79
|
blockedCommands: options.blockedCommands,
|
|
80
|
+
dangerouslySkipSecurityChecks: options.dangerouslySkipSecurityChecks,
|
|
45
81
|
env: options.env,
|
|
46
82
|
});
|
|
47
83
|
// Create tool instances
|
|
@@ -51,28 +87,28 @@ export function createExtensionPack(context) {
|
|
|
51
87
|
const listDirectoryTool = new ListDirectoryTool(shellService);
|
|
52
88
|
return {
|
|
53
89
|
name: '@framers/agentos-ext-cli-executor',
|
|
54
|
-
version: '1.
|
|
90
|
+
version: '1.1.0',
|
|
55
91
|
descriptors: [
|
|
56
92
|
{
|
|
57
|
-
id:
|
|
93
|
+
id: executeTool.name,
|
|
58
94
|
kind: 'tool',
|
|
59
95
|
priority: options.priority || 50,
|
|
60
96
|
payload: executeTool,
|
|
61
97
|
},
|
|
62
98
|
{
|
|
63
|
-
id:
|
|
99
|
+
id: fileReadTool.name,
|
|
64
100
|
kind: 'tool',
|
|
65
101
|
priority: options.priority || 50,
|
|
66
102
|
payload: fileReadTool,
|
|
67
103
|
},
|
|
68
104
|
{
|
|
69
|
-
id:
|
|
105
|
+
id: fileWriteTool.name,
|
|
70
106
|
kind: 'tool',
|
|
71
107
|
priority: options.priority || 50,
|
|
72
108
|
payload: fileWriteTool,
|
|
73
109
|
},
|
|
74
110
|
{
|
|
75
|
-
id:
|
|
111
|
+
id: listDirectoryTool.name,
|
|
76
112
|
kind: 'tool',
|
|
77
113
|
priority: options.priority || 50,
|
|
78
114
|
payload: listDirectoryTool,
|
|
@@ -85,6 +121,12 @@ export function createExtensionPack(context) {
|
|
|
85
121
|
if (context.onActivate) {
|
|
86
122
|
await context.onActivate();
|
|
87
123
|
}
|
|
124
|
+
if (workspaceDir && workspaceConfig?.createIfMissing !== false) {
|
|
125
|
+
await fs.mkdir(workspaceDir, { recursive: true });
|
|
126
|
+
for (const sub of workspaceSubdirs) {
|
|
127
|
+
await fs.mkdir(path.join(workspaceDir, sub), { recursive: true });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
88
130
|
context.logger?.info('CLI Executor Extension activated');
|
|
89
131
|
},
|
|
90
132
|
/**
|
|
@@ -99,12 +141,12 @@ export function createExtensionPack(context) {
|
|
|
99
141
|
};
|
|
100
142
|
}
|
|
101
143
|
// Export types and classes for consumers
|
|
102
|
-
export { ShellService } from './services/shellService';
|
|
103
|
-
export { ExecuteTool } from './tools/execute';
|
|
104
|
-
export { FileReadTool } from './tools/fileRead';
|
|
105
|
-
export { FileWriteTool } from './tools/fileWrite';
|
|
106
|
-
export { ListDirectoryTool } from './tools/listDir';
|
|
107
|
-
export * from './types';
|
|
144
|
+
export { ShellService } from './services/shellService.js';
|
|
145
|
+
export { ExecuteTool } from './tools/execute.js';
|
|
146
|
+
export { FileReadTool } from './tools/fileRead.js';
|
|
147
|
+
export { FileWriteTool } from './tools/fileWrite.js';
|
|
148
|
+
export { ListDirectoryTool } from './tools/listDir.js';
|
|
149
|
+
export * from './types.js';
|
|
108
150
|
// Default export for convenience
|
|
109
151
|
export default createExtensionPack;
|
|
110
152
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAWvD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAOnC;IACC,MAAM,OAAO,GAAI,OAAO,CAAC,OAAuC,IAAI,EAAE,CAAC;IAEvE,IAAI,YAAgC,CAAC;IACrC,IAAI,gBAAgB,GAAa,EAAE,CAAC;IACpC,MAAM,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC;IAC/C,IAAI,eAAe,IAAI,eAAe,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;QACzD,MAAM,OAAO,GACX,CAAC,eAAe,CAAC,OAAO,IAAI,MAAM,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;YACnE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC;QAClD,MAAM,OAAO,GAAG,MAAM,CAAC,eAAe,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7D,IAAI,OAAO,EAAE,CAAC;YACZ,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC9C,gBAAgB,GAAG,KAAK,CAAC,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,IAAI,eAAe,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;gBAC7F,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;gBAC/D,CAAC,CAAC,CAAC,QAAQ,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;IAED,MAAM,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,IAAI,YAAY,CAAC;IAClE,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU;QACnC,CAAC,CAAC;YACE,GAAG,OAAO,CAAC,UAAU;YACrB,SAAS,EACP,OAAO,CAAC,UAAU,CAAC,SAAS,KAAK,IAAI;gBACrC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,SAAS,IAAI,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC;gBAC5E,YAAY;gBACV,CAAC,CAAC,CAAC,YAAY,CAAC;gBAChB,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,SAAS;YAClC,UAAU,EACR,OAAO,CAAC,UAAU,CAAC,UAAU,KAAK,IAAI;gBACtC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,UAAU,IAAI,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,CAAC;gBAC9E,YAAY;gBACV,CAAC,CAAC,CAAC,YAAY,CAAC;gBAChB,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,UAAU;SACpC;QACH,CAAC,CAAC,SAAS,CAAC;IAEd,8CAA8C;IAC9C,MAAM,YAAY,GAAG,IAAI,YAAY,CAAC;QACpC,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,gBAAgB;QAChB,UAAU;QACV,cAAc,EAAE,OAAO,CAAC,cAAc;QACtC,eAAe,EAAE,OAAO,CAAC,eAAe;QACxC,eAAe,EAAE,OAAO,CAAC,eAAe;QACxC,6BAA6B,EAAE,OAAO,CAAC,6BAA6B;QACpE,GAAG,EAAE,OAAO,CAAC,GAAG;KACjB,CAAC,CAAC;IAEH,wBAAwB;IACxB,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,YAAY,CAAC,CAAC;IAClD,MAAM,YAAY,GAAG,IAAI,YAAY,CAAC,YAAY,CAAC,CAAC;IACpD,MAAM,aAAa,GAAG,IAAI,aAAa,CAAC,YAAY,CAAC,CAAC;IACtD,MAAM,iBAAiB,GAAG,IAAI,iBAAiB,CAAC,YAAY,CAAC,CAAC;IAE9D,OAAO;QACL,IAAI,EAAE,mCAAmC;QACzC,OAAO,EAAE,OAAO;QAChB,WAAW,EAAE;YACX;gBACE,EAAE,EAAE,WAAW,CAAC,IAAI;gBACpB,IAAI,EAAE,MAAM;gBACZ,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,EAAE;gBAChC,OAAO,EAAE,WAAW;aACrB;YACD;gBACE,EAAE,EAAE,YAAY,CAAC,IAAI;gBACrB,IAAI,EAAE,MAAM;gBACZ,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,EAAE;gBAChC,OAAO,EAAE,YAAY;aACtB;YACD;gBACE,EAAE,EAAE,aAAa,CAAC,IAAI;gBACtB,IAAI,EAAE,MAAM;gBACZ,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,EAAE;gBAChC,OAAO,EAAE,aAAa;aACvB;YACD;gBACE,EAAE,EAAE,iBAAiB,CAAC,IAAI;gBAC1B,IAAI,EAAE,MAAM;gBACZ,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,EAAE;gBAChC,OAAO,EAAE,iBAAiB;aAC3B;SACF;QAED;;WAEG;QACH,UAAU,EAAE,KAAK,IAAI,EAAE;YACrB,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;gBACvB,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC;YAC7B,CAAC;YACD,IAAI,YAAY,IAAI,eAAe,EAAE,eAAe,KAAK,KAAK,EAAE,CAAC;gBAC/D,MAAM,EAAE,CAAC,KAAK,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;gBAClD,KAAK,MAAM,GAAG,IAAI,gBAAgB,EAAE,CAAC;oBACnC,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;gBACpE,CAAC;YACH,CAAC;YACD,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,kCAAkC,CAAC,CAAC;QAC3D,CAAC;QAED;;WAEG;QACH,YAAY,EAAE,KAAK,IAAI,EAAE;YACvB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;gBACzB,MAAM,OAAO,CAAC,YAAY,EAAE,CAAC;YAC/B,CAAC;YACD,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,oCAAoC,CAAC,CAAC;QAC7D,CAAC;KACF,CAAC;AACJ,CAAC;AAED,yCAAyC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,cAAc,YAAY,CAAC;AAE3B,iCAAiC;AACjC,eAAe,mBAAmB,CAAC"}
|
|
@@ -4,13 +4,18 @@
|
|
|
4
4
|
*
|
|
5
5
|
* @module @framers/agentos-ext-cli-executor
|
|
6
6
|
*/
|
|
7
|
-
import type { ShellConfig, ExecutionResult, ScriptOptions, ScriptResult, FileReadOptions, FileReadResult, FileWriteOptions, FileWriteResult, ListDirectoryOptions, ListDirectoryResult, SecurityCheckResult } from '../types';
|
|
7
|
+
import type { ShellConfig, ExecutionResult, ScriptOptions, ScriptResult, FileReadOptions, FileReadResult, FileWriteOptions, FileWriteResult, ListDirectoryOptions, ListDirectoryResult, SecurityCheckResult } from '../types.js';
|
|
8
8
|
/**
|
|
9
9
|
* Shell service for executing commands
|
|
10
10
|
*/
|
|
11
11
|
export declare class ShellService {
|
|
12
12
|
private config;
|
|
13
13
|
constructor(config?: ShellConfig);
|
|
14
|
+
private resolveAbsolutePath;
|
|
15
|
+
private isFilesystemPolicyEnabled;
|
|
16
|
+
private isWithinRoot;
|
|
17
|
+
private resolvePathForAuthorization;
|
|
18
|
+
private assertFilesystemAllowed;
|
|
14
19
|
/**
|
|
15
20
|
* Detect the appropriate shell for the current platform
|
|
16
21
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shellService.d.ts","sourceRoot":"","sources":["../../src/services/shellService.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EACV,WAAW,EACX,eAAe,EACf,aAAa,EACb,YAAY,EACZ,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EAEnB,mBAAmB,EACpB,MAAM,
|
|
1
|
+
{"version":3,"file":"shellService.d.ts","sourceRoot":"","sources":["../../src/services/shellService.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EACV,WAAW,EACX,eAAe,EACf,aAAa,EACb,YAAY,EACZ,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EAEnB,mBAAmB,EACpB,MAAM,aAAa,CAAC;AAuBrB;;GAEG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAc;gBAEhB,MAAM,GAAE,WAAgB;IAUpC,OAAO,CAAC,mBAAmB;IAM3B,OAAO,CAAC,yBAAyB;IAIjC,OAAO,CAAC,YAAY;YAKN,2BAA2B;YAwB3B,uBAAuB;IAoCrC;;OAEG;IACH,OAAO,CAAC,WAAW;IAenB;;OAEG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,mBAAmB;IAgFnD;;OAEG;IACG,OAAO,CACX,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GACzE,OAAO,CAAC,eAAe,CAAC;IAwD3B;;OAEG;IACG,SAAS,CACb,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,aAAa,GACtB,OAAO,CAAC,YAAY,CAAC;IAiCxB;;OAEG;IACG,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC;IA4CpF;;OAEG;IACG,SAAS,CACb,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,eAAe,CAAC;IAkC3B;;OAEG;IACG,aAAa,CACjB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,mBAAmB,CAAC;CA6EhC"}
|
|
@@ -37,9 +37,76 @@ export class ShellService {
|
|
|
37
37
|
defaultShell: 'auto',
|
|
38
38
|
timeout: 60000,
|
|
39
39
|
blockedCommands: [],
|
|
40
|
+
dangerouslySkipSecurityChecks: false,
|
|
40
41
|
...config,
|
|
41
42
|
};
|
|
42
43
|
}
|
|
44
|
+
resolveAbsolutePath(filePath) {
|
|
45
|
+
const baseDir = this.config.workingDirectory || process.cwd();
|
|
46
|
+
const abs = path.isAbsolute(filePath) ? filePath : path.resolve(baseDir, filePath);
|
|
47
|
+
return path.normalize(abs);
|
|
48
|
+
}
|
|
49
|
+
isFilesystemPolicyEnabled() {
|
|
50
|
+
return !!this.config.filesystem;
|
|
51
|
+
}
|
|
52
|
+
isWithinRoot(targetPath, rootPath) {
|
|
53
|
+
const rel = path.relative(rootPath, targetPath);
|
|
54
|
+
return rel === '' || (!rel.startsWith(`..${path.sep}`) && rel !== '..' && !path.isAbsolute(rel));
|
|
55
|
+
}
|
|
56
|
+
async resolvePathForAuthorization(absolutePath, op) {
|
|
57
|
+
try {
|
|
58
|
+
return await fs.realpath(absolutePath);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// For writes, resolve the closest existing ancestor so symlink escapes are still detected.
|
|
62
|
+
if (op !== 'write')
|
|
63
|
+
return absolutePath;
|
|
64
|
+
let cursor = path.dirname(absolutePath);
|
|
65
|
+
while (true) {
|
|
66
|
+
try {
|
|
67
|
+
const realCursor = await fs.realpath(cursor);
|
|
68
|
+
const remainder = path.relative(cursor, absolutePath);
|
|
69
|
+
return path.join(realCursor, remainder);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
const parent = path.dirname(cursor);
|
|
73
|
+
if (parent === cursor)
|
|
74
|
+
break;
|
|
75
|
+
cursor = parent;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return absolutePath;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async assertFilesystemAllowed(op, absolutePath) {
|
|
82
|
+
if (!this.isFilesystemPolicyEnabled())
|
|
83
|
+
return;
|
|
84
|
+
const policy = this.config.filesystem;
|
|
85
|
+
const allow = op === 'write' ? policy.allowWrite === true : policy.allowRead === true;
|
|
86
|
+
if (!allow) {
|
|
87
|
+
throw new Error(`Filesystem ${op} is disabled by policy`);
|
|
88
|
+
}
|
|
89
|
+
const rootsRaw = op === 'write' ? policy.writeRoots : policy.readRoots;
|
|
90
|
+
if (!Array.isArray(rootsRaw) || rootsRaw.length === 0) {
|
|
91
|
+
throw new Error(`Filesystem ${op} roots are not configured`);
|
|
92
|
+
}
|
|
93
|
+
const baseDir = this.config.workingDirectory || process.cwd();
|
|
94
|
+
const roots = await Promise.all(rootsRaw.map(async (root) => {
|
|
95
|
+
const absRoot = path.isAbsolute(root) ? root : path.resolve(baseDir, root);
|
|
96
|
+
const normalized = path.normalize(absRoot);
|
|
97
|
+
try {
|
|
98
|
+
return await fs.realpath(normalized);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return normalized;
|
|
102
|
+
}
|
|
103
|
+
}));
|
|
104
|
+
const authPath = await this.resolvePathForAuthorization(absolutePath, op);
|
|
105
|
+
const allowed = roots.some((root) => this.isWithinRoot(authPath, root));
|
|
106
|
+
if (!allowed) {
|
|
107
|
+
throw new Error(`Path is outside allowed filesystem ${op} roots: ${absolutePath}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
43
110
|
/**
|
|
44
111
|
* Detect the appropriate shell for the current platform
|
|
45
112
|
*/
|
|
@@ -62,6 +129,14 @@ export class ShellService {
|
|
|
62
129
|
* Check if a command is safe to execute
|
|
63
130
|
*/
|
|
64
131
|
checkSecurity(command) {
|
|
132
|
+
if (this.config.dangerouslySkipSecurityChecks) {
|
|
133
|
+
return {
|
|
134
|
+
allowed: true,
|
|
135
|
+
reason: 'Security checks disabled by configuration',
|
|
136
|
+
riskLevel: 'critical',
|
|
137
|
+
warnings: ['Security checks are disabled'],
|
|
138
|
+
};
|
|
139
|
+
}
|
|
65
140
|
const warnings = [];
|
|
66
141
|
let riskLevel = 'safe';
|
|
67
142
|
// Check against blocked commands list
|
|
@@ -219,9 +294,8 @@ export class ShellService {
|
|
|
219
294
|
*/
|
|
220
295
|
async readFile(filePath, options) {
|
|
221
296
|
const encoding = options?.encoding || 'utf-8';
|
|
222
|
-
const absolutePath =
|
|
223
|
-
|
|
224
|
-
: path.resolve(this.config.workingDirectory || process.cwd(), filePath);
|
|
297
|
+
const absolutePath = this.resolveAbsolutePath(filePath);
|
|
298
|
+
await this.assertFilesystemAllowed('read', absolutePath);
|
|
225
299
|
const stats = await fs.stat(absolutePath);
|
|
226
300
|
let content;
|
|
227
301
|
let truncated = false;
|
|
@@ -263,9 +337,8 @@ export class ShellService {
|
|
|
263
337
|
*/
|
|
264
338
|
async writeFile(filePath, content, options) {
|
|
265
339
|
const encoding = options?.encoding || 'utf-8';
|
|
266
|
-
const absolutePath =
|
|
267
|
-
|
|
268
|
-
: path.resolve(this.config.workingDirectory || process.cwd(), filePath);
|
|
340
|
+
const absolutePath = this.resolveAbsolutePath(filePath);
|
|
341
|
+
await this.assertFilesystemAllowed('write', absolutePath);
|
|
269
342
|
// Check if file exists
|
|
270
343
|
let fileExists = true;
|
|
271
344
|
try {
|
|
@@ -296,9 +369,8 @@ export class ShellService {
|
|
|
296
369
|
* List directory contents
|
|
297
370
|
*/
|
|
298
371
|
async listDirectory(dirPath, options) {
|
|
299
|
-
const absolutePath =
|
|
300
|
-
|
|
301
|
-
: path.resolve(this.config.workingDirectory || process.cwd(), dirPath);
|
|
372
|
+
const absolutePath = this.resolveAbsolutePath(dirPath);
|
|
373
|
+
await this.assertFilesystemAllowed('list', absolutePath);
|
|
302
374
|
const entries = [];
|
|
303
375
|
const readDir = async (dir, depth) => {
|
|
304
376
|
const items = await fs.readdir(dir, { withFileTypes: true });
|
|
@@ -332,7 +404,7 @@ export class ShellService {
|
|
|
332
404
|
// Include stats if requested
|
|
333
405
|
if (options?.includeStats) {
|
|
334
406
|
try {
|
|
335
|
-
const stats = await fs.
|
|
407
|
+
const stats = await fs.lstat(itemPath);
|
|
336
408
|
entry.size = stats.size;
|
|
337
409
|
entry.modifiedAt = stats.mtime.toISOString();
|
|
338
410
|
entry.createdAt = stats.birthtime.toISOString();
|