@elizaos/plugin-code 2.0.0-alpha.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/dist/actions/changeDirectory.d.ts +2 -0
- package/dist/actions/editFile.d.ts +2 -0
- package/dist/actions/executeShell.d.ts +2 -0
- package/dist/actions/git.d.ts +2 -0
- package/dist/actions/index.d.ts +8 -0
- package/dist/actions/listFiles.d.ts +2 -0
- package/dist/actions/readFile.d.ts +2 -0
- package/dist/actions/searchFiles.d.ts +2 -0
- package/dist/actions/writeFile.d.ts +2 -0
- package/dist/build.d.ts +1 -0
- package/dist/generated/specs/spec-helpers.d.ts +48 -0
- package/dist/generated/specs/specs.d.ts +142 -0
- package/dist/index.browser.d.ts +9 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +1064 -0
- package/dist/index.js.map +24 -0
- package/dist/providers/coderStatusProvider.d.ts +2 -0
- package/dist/providers/index.d.ts +1 -0
- package/dist/services/coderService.d.ts +61 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types/index.d.ts +28 -0
- package/dist/utils/config.d.ts +2 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/pathUtils.d.ts +13 -0
- package/package.json +60 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1064 @@
|
|
|
1
|
+
// generated/specs/specs.ts
|
|
2
|
+
var coreActionsSpec = {
|
|
3
|
+
version: "1.0.0",
|
|
4
|
+
actions: [
|
|
5
|
+
{
|
|
6
|
+
name: "CHANGE_DIRECTORY",
|
|
7
|
+
description: "Change the working directory (restricted to allowed directory).",
|
|
8
|
+
similes: ["CD", "CWD"],
|
|
9
|
+
parameters: []
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
name: "EDIT_FILE",
|
|
13
|
+
description: "Replace a substring in a file (single replacement).",
|
|
14
|
+
similes: ["REPLACE_IN_FILE", "PATCH_FILE", "MODIFY_FILE"],
|
|
15
|
+
parameters: []
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: "EXECUTE_SHELL",
|
|
19
|
+
description: "Execute a shell command in the current working directory (restricted).",
|
|
20
|
+
similes: ["SHELL", "RUN_COMMAND", "EXEC", "TERMINAL"],
|
|
21
|
+
parameters: []
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: "GIT",
|
|
25
|
+
description: "Run a git command (restricted).",
|
|
26
|
+
similes: ["GIT_COMMAND", "GIT_RUN"],
|
|
27
|
+
parameters: []
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: "LIST_FILES",
|
|
31
|
+
description: "List files in a directory.",
|
|
32
|
+
similes: ["LS", "LIST_DIR", "LIST_DIRECTORY", "DIR"],
|
|
33
|
+
parameters: []
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: "READ_FILE",
|
|
37
|
+
description: "Read and return a file",
|
|
38
|
+
similes: ["VIEW_FILE", "OPEN_FILE", "CAT_FILE", "SHOW_FILE", "GET_FILE"],
|
|
39
|
+
parameters: []
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: "SEARCH_FILES",
|
|
43
|
+
description: "Search for text across files under a directory.",
|
|
44
|
+
similes: ["GREP", "RG", "FIND_IN_FILES", "SEARCH"],
|
|
45
|
+
parameters: []
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: "WRITE_FILE",
|
|
49
|
+
description: "Create or overwrite a file with given content.",
|
|
50
|
+
similes: ["CREATE_FILE", "SAVE_FILE", "OUTPUT_FILE"],
|
|
51
|
+
parameters: []
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
};
|
|
55
|
+
var allActionsSpec = {
|
|
56
|
+
version: "1.0.0",
|
|
57
|
+
actions: [
|
|
58
|
+
{
|
|
59
|
+
name: "CHANGE_DIRECTORY",
|
|
60
|
+
description: "Change the working directory (restricted to allowed directory).",
|
|
61
|
+
similes: ["CD", "CWD"],
|
|
62
|
+
parameters: []
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: "EDIT_FILE",
|
|
66
|
+
description: "Replace a substring in a file (single replacement).",
|
|
67
|
+
similes: ["REPLACE_IN_FILE", "PATCH_FILE", "MODIFY_FILE"],
|
|
68
|
+
parameters: []
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: "EXECUTE_SHELL",
|
|
72
|
+
description: "Execute a shell command in the current working directory (restricted).",
|
|
73
|
+
similes: ["SHELL", "RUN_COMMAND", "EXEC", "TERMINAL"],
|
|
74
|
+
parameters: []
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: "GIT",
|
|
78
|
+
description: "Run a git command (restricted).",
|
|
79
|
+
similes: ["GIT_COMMAND", "GIT_RUN"],
|
|
80
|
+
parameters: []
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: "LIST_FILES",
|
|
84
|
+
description: "List files in a directory.",
|
|
85
|
+
similes: ["LS", "LIST_DIR", "LIST_DIRECTORY", "DIR"],
|
|
86
|
+
parameters: []
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: "READ_FILE",
|
|
90
|
+
description: "Read and return a file",
|
|
91
|
+
similes: ["VIEW_FILE", "OPEN_FILE", "CAT_FILE", "SHOW_FILE", "GET_FILE"],
|
|
92
|
+
parameters: []
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: "SEARCH_FILES",
|
|
96
|
+
description: "Search for text across files under a directory.",
|
|
97
|
+
similes: ["GREP", "RG", "FIND_IN_FILES", "SEARCH"],
|
|
98
|
+
parameters: []
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: "WRITE_FILE",
|
|
102
|
+
description: "Create or overwrite a file with given content.",
|
|
103
|
+
similes: ["CREATE_FILE", "SAVE_FILE", "OUTPUT_FILE"],
|
|
104
|
+
parameters: []
|
|
105
|
+
}
|
|
106
|
+
]
|
|
107
|
+
};
|
|
108
|
+
var coreProvidersSpec = {
|
|
109
|
+
version: "1.0.0",
|
|
110
|
+
providers: [
|
|
111
|
+
{
|
|
112
|
+
name: "CODER_STATUS",
|
|
113
|
+
description: "Provides current working directory, allowed directory, and recent shell/file operations",
|
|
114
|
+
dynamic: true
|
|
115
|
+
}
|
|
116
|
+
]
|
|
117
|
+
};
|
|
118
|
+
var allProvidersSpec = {
|
|
119
|
+
version: "1.0.0",
|
|
120
|
+
providers: [
|
|
121
|
+
{
|
|
122
|
+
name: "CODER_STATUS",
|
|
123
|
+
description: "Provides current working directory, allowed directory, and recent shell/file operations",
|
|
124
|
+
dynamic: true
|
|
125
|
+
}
|
|
126
|
+
]
|
|
127
|
+
};
|
|
128
|
+
var coreEvaluatorsSpec = {
|
|
129
|
+
version: "1.0.0",
|
|
130
|
+
evaluators: []
|
|
131
|
+
};
|
|
132
|
+
var allEvaluatorsSpec = {
|
|
133
|
+
version: "1.0.0",
|
|
134
|
+
evaluators: []
|
|
135
|
+
};
|
|
136
|
+
var coreActionDocs = coreActionsSpec.actions;
|
|
137
|
+
var allActionDocs = allActionsSpec.actions;
|
|
138
|
+
var coreProviderDocs = coreProvidersSpec.providers;
|
|
139
|
+
var allProviderDocs = allProvidersSpec.providers;
|
|
140
|
+
var coreEvaluatorDocs = coreEvaluatorsSpec.evaluators;
|
|
141
|
+
var allEvaluatorDocs = allEvaluatorsSpec.evaluators;
|
|
142
|
+
|
|
143
|
+
// generated/specs/spec-helpers.ts
|
|
144
|
+
var coreActionMap = new Map(coreActionDocs.map((doc) => [doc.name, doc]));
|
|
145
|
+
var allActionMap = new Map(allActionDocs.map((doc) => [doc.name, doc]));
|
|
146
|
+
var coreProviderMap = new Map(coreProviderDocs.map((doc) => [doc.name, doc]));
|
|
147
|
+
var allProviderMap = new Map(allProviderDocs.map((doc) => [doc.name, doc]));
|
|
148
|
+
var coreEvaluatorMap = new Map(coreEvaluatorDocs.map((doc) => [doc.name, doc]));
|
|
149
|
+
var allEvaluatorMap = new Map(allEvaluatorDocs.map((doc) => [doc.name, doc]));
|
|
150
|
+
function getActionSpec(name) {
|
|
151
|
+
return coreActionMap.get(name) ?? allActionMap.get(name);
|
|
152
|
+
}
|
|
153
|
+
function requireActionSpec(name) {
|
|
154
|
+
const spec = getActionSpec(name);
|
|
155
|
+
if (!spec) {
|
|
156
|
+
throw new Error(`Action spec not found: ${name}`);
|
|
157
|
+
}
|
|
158
|
+
return spec;
|
|
159
|
+
}
|
|
160
|
+
function getProviderSpec(name) {
|
|
161
|
+
return coreProviderMap.get(name) ?? allProviderMap.get(name);
|
|
162
|
+
}
|
|
163
|
+
function requireProviderSpec(name) {
|
|
164
|
+
const spec = getProviderSpec(name);
|
|
165
|
+
if (!spec) {
|
|
166
|
+
throw new Error(`Provider spec not found: ${name}`);
|
|
167
|
+
}
|
|
168
|
+
return spec;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// actions/changeDirectory.ts
|
|
172
|
+
function getTarget(options) {
|
|
173
|
+
const opt = options;
|
|
174
|
+
return opt?.path?.trim() ?? "";
|
|
175
|
+
}
|
|
176
|
+
var spec = requireActionSpec("CHANGE_DIRECTORY");
|
|
177
|
+
var changeDirectory = {
|
|
178
|
+
name: spec.name,
|
|
179
|
+
similes: spec.similes ? [...spec.similes] : [],
|
|
180
|
+
description: spec.description,
|
|
181
|
+
validate: async (runtime) => runtime.getService("coder") !== null,
|
|
182
|
+
handler: async (runtime, message, _state, options, callback) => {
|
|
183
|
+
const svc = runtime.getService("coder");
|
|
184
|
+
if (!svc) {
|
|
185
|
+
const msg = "Coder service is not available.";
|
|
186
|
+
if (callback)
|
|
187
|
+
await callback({ content: { text: msg } });
|
|
188
|
+
return { success: false, text: msg };
|
|
189
|
+
}
|
|
190
|
+
const target = getTarget(options);
|
|
191
|
+
if (!target) {
|
|
192
|
+
const msg = "Missing path.";
|
|
193
|
+
if (callback)
|
|
194
|
+
await callback({ content: { text: msg } });
|
|
195
|
+
return { success: false, text: msg };
|
|
196
|
+
}
|
|
197
|
+
const conv = message.roomId ?? message.agentId ?? runtime.agentId;
|
|
198
|
+
const result = await svc.changeDirectory(conv, target);
|
|
199
|
+
const out = result.success ? result.stdout : result.stderr;
|
|
200
|
+
if (callback)
|
|
201
|
+
await callback({ content: { text: out } });
|
|
202
|
+
return { success: result.success, text: out };
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
// actions/editFile.ts
|
|
206
|
+
function getInputs(options, message) {
|
|
207
|
+
const opt = options;
|
|
208
|
+
const filepath = opt?.filepath?.trim() ?? "";
|
|
209
|
+
const oldStr = opt?.old_str ?? "";
|
|
210
|
+
const newStr = opt?.new_str ?? "";
|
|
211
|
+
if (filepath)
|
|
212
|
+
return { filepath, oldStr, newStr };
|
|
213
|
+
const text = message.content.text ?? "";
|
|
214
|
+
const m = text.match(/["'`]([^"'`]+)["'`]/);
|
|
215
|
+
const inferred = m?.[1]?.trim() ?? "";
|
|
216
|
+
return { filepath: inferred, oldStr, newStr };
|
|
217
|
+
}
|
|
218
|
+
var spec2 = requireActionSpec("EDIT_FILE");
|
|
219
|
+
var editFile = {
|
|
220
|
+
name: spec2.name,
|
|
221
|
+
similes: spec2.similes ? [...spec2.similes] : [],
|
|
222
|
+
description: spec2.description,
|
|
223
|
+
validate: async (runtime) => runtime.getService("coder") !== null,
|
|
224
|
+
handler: async (runtime, message, _state, options, callback) => {
|
|
225
|
+
const svc = runtime.getService("coder");
|
|
226
|
+
if (!svc) {
|
|
227
|
+
const msg2 = "Coder service is not available.";
|
|
228
|
+
if (callback)
|
|
229
|
+
await callback({ content: { text: msg2 } });
|
|
230
|
+
return { success: false, text: msg2 };
|
|
231
|
+
}
|
|
232
|
+
const { filepath, oldStr, newStr } = getInputs(options, message);
|
|
233
|
+
if (!filepath || oldStr.length === 0) {
|
|
234
|
+
const msg2 = "Missing filepath or old_str.";
|
|
235
|
+
if (callback)
|
|
236
|
+
await callback({ content: { text: msg2 } });
|
|
237
|
+
return { success: false, text: msg2 };
|
|
238
|
+
}
|
|
239
|
+
const conv = message.roomId ?? message.agentId ?? runtime.agentId;
|
|
240
|
+
const result = await svc.editFile(conv, filepath, oldStr, newStr);
|
|
241
|
+
if (!result.ok) {
|
|
242
|
+
if (callback)
|
|
243
|
+
await callback({ content: { text: result.error } });
|
|
244
|
+
return { success: false, text: result.error };
|
|
245
|
+
}
|
|
246
|
+
const msg = `Edited ${filepath}`;
|
|
247
|
+
if (callback)
|
|
248
|
+
await callback({ content: { text: msg } });
|
|
249
|
+
return { success: true, text: msg };
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
// actions/executeShell.ts
|
|
253
|
+
function getCommand(options) {
|
|
254
|
+
const opt = options;
|
|
255
|
+
return opt?.command?.trim() ?? "";
|
|
256
|
+
}
|
|
257
|
+
var spec3 = requireActionSpec("EXECUTE_SHELL");
|
|
258
|
+
var executeShell = {
|
|
259
|
+
name: spec3.name,
|
|
260
|
+
similes: spec3.similes ? [...spec3.similes] : [],
|
|
261
|
+
description: spec3.description,
|
|
262
|
+
validate: async (runtime) => runtime.getService("coder") !== null,
|
|
263
|
+
handler: async (runtime, message, _state, options, callback) => {
|
|
264
|
+
const svc = runtime.getService("coder");
|
|
265
|
+
if (!svc) {
|
|
266
|
+
const msg = "Coder service is not available.";
|
|
267
|
+
if (callback)
|
|
268
|
+
await callback({ content: { text: msg } });
|
|
269
|
+
return { success: false, text: msg };
|
|
270
|
+
}
|
|
271
|
+
const cmd = getCommand(options);
|
|
272
|
+
if (!cmd) {
|
|
273
|
+
const msg = "Missing command.";
|
|
274
|
+
if (callback)
|
|
275
|
+
await callback({ content: { text: msg } });
|
|
276
|
+
return { success: false, text: msg };
|
|
277
|
+
}
|
|
278
|
+
const conv = message.roomId ?? message.agentId ?? runtime.agentId;
|
|
279
|
+
const result = await svc.executeShell(cmd, conv);
|
|
280
|
+
const out = result.success ? result.stdout : result.stderr;
|
|
281
|
+
if (callback)
|
|
282
|
+
await callback({ content: { text: out } });
|
|
283
|
+
return { success: result.success, text: out };
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
// actions/git.ts
|
|
287
|
+
function getArgs(options) {
|
|
288
|
+
const opt = options;
|
|
289
|
+
return opt?.args?.trim() ?? "";
|
|
290
|
+
}
|
|
291
|
+
var spec4 = requireActionSpec("GIT");
|
|
292
|
+
var git = {
|
|
293
|
+
name: spec4.name,
|
|
294
|
+
similes: spec4.similes ? [...spec4.similes] : [],
|
|
295
|
+
description: spec4.description,
|
|
296
|
+
validate: async (runtime) => runtime.getService("coder") !== null,
|
|
297
|
+
handler: async (runtime, message, _state, options, callback) => {
|
|
298
|
+
const svc = runtime.getService("coder");
|
|
299
|
+
if (!svc) {
|
|
300
|
+
const msg = "Coder service is not available.";
|
|
301
|
+
if (callback)
|
|
302
|
+
await callback({ content: { text: msg } });
|
|
303
|
+
return { success: false, text: msg };
|
|
304
|
+
}
|
|
305
|
+
const args = getArgs(options);
|
|
306
|
+
if (!args) {
|
|
307
|
+
const msg = 'Missing args (provide options.args, e.g. "status").';
|
|
308
|
+
if (callback)
|
|
309
|
+
await callback({ content: { text: msg } });
|
|
310
|
+
return { success: false, text: msg };
|
|
311
|
+
}
|
|
312
|
+
const conv = message.roomId ?? message.agentId ?? runtime.agentId;
|
|
313
|
+
const result = await svc.git(args, conv);
|
|
314
|
+
const out = result.success ? result.stdout : result.stderr;
|
|
315
|
+
if (callback)
|
|
316
|
+
await callback({ content: { text: out } });
|
|
317
|
+
return { success: result.success, text: out };
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
// actions/listFiles.ts
|
|
321
|
+
function getDir(options) {
|
|
322
|
+
const opt = options;
|
|
323
|
+
return opt?.path?.trim() ?? ".";
|
|
324
|
+
}
|
|
325
|
+
var spec5 = requireActionSpec("LIST_FILES");
|
|
326
|
+
var listFiles = {
|
|
327
|
+
name: spec5.name,
|
|
328
|
+
similes: spec5.similes ? [...spec5.similes] : [],
|
|
329
|
+
description: spec5.description,
|
|
330
|
+
validate: async (runtime) => runtime.getService("coder") !== null,
|
|
331
|
+
handler: async (runtime, message, _state, options, callback) => {
|
|
332
|
+
const svc = runtime.getService("coder");
|
|
333
|
+
if (!svc) {
|
|
334
|
+
const msg = "Coder service is not available.";
|
|
335
|
+
if (callback)
|
|
336
|
+
await callback({ content: { text: msg } });
|
|
337
|
+
return { success: false, text: msg };
|
|
338
|
+
}
|
|
339
|
+
const conv = message.roomId ?? message.agentId ?? runtime.agentId;
|
|
340
|
+
const dir = getDir(options);
|
|
341
|
+
const result = await svc.listFiles(conv, dir);
|
|
342
|
+
if (!result.ok) {
|
|
343
|
+
if (callback)
|
|
344
|
+
await callback({ content: { text: result.error } });
|
|
345
|
+
return { success: false, text: result.error };
|
|
346
|
+
}
|
|
347
|
+
const out = result.items.length > 0 ? result.items.join(`
|
|
348
|
+
`) : "(empty)";
|
|
349
|
+
if (callback)
|
|
350
|
+
await callback({ content: { text: out } });
|
|
351
|
+
return { success: true, text: out };
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
// actions/readFile.ts
|
|
355
|
+
import * as path from "node:path";
|
|
356
|
+
function extToLang(ext) {
|
|
357
|
+
const e = ext.toLowerCase();
|
|
358
|
+
if (e === ".ts" || e === ".tsx")
|
|
359
|
+
return "ts";
|
|
360
|
+
if (e === ".js" || e === ".jsx")
|
|
361
|
+
return "js";
|
|
362
|
+
if (e === ".json")
|
|
363
|
+
return "json";
|
|
364
|
+
if (e === ".md")
|
|
365
|
+
return "md";
|
|
366
|
+
if (e === ".py")
|
|
367
|
+
return "py";
|
|
368
|
+
if (e === ".rs")
|
|
369
|
+
return "rs";
|
|
370
|
+
if (e === ".go")
|
|
371
|
+
return "go";
|
|
372
|
+
if (e === ".toml")
|
|
373
|
+
return "toml";
|
|
374
|
+
if (e === ".yml" || e === ".yaml")
|
|
375
|
+
return "yaml";
|
|
376
|
+
if (e === ".sh" || e === ".bash" || e === ".zsh")
|
|
377
|
+
return "bash";
|
|
378
|
+
return "";
|
|
379
|
+
}
|
|
380
|
+
function getFilepath(options, message) {
|
|
381
|
+
const opt = options;
|
|
382
|
+
if (opt?.filepath && opt.filepath.trim().length > 0)
|
|
383
|
+
return opt.filepath.trim();
|
|
384
|
+
const text = message.content.text ?? "";
|
|
385
|
+
const m = text.match(/["'`]([^"'`]+)["'`]/);
|
|
386
|
+
if (m?.[1])
|
|
387
|
+
return m[1].trim();
|
|
388
|
+
const loose = text.match(/(?:\.\/|\/)?[\w\-./]+\.[a-zA-Z0-9]+/);
|
|
389
|
+
return loose?.[0]?.trim() ?? "";
|
|
390
|
+
}
|
|
391
|
+
var spec6 = requireActionSpec("READ_FILE");
|
|
392
|
+
var readFile = {
|
|
393
|
+
name: spec6.name,
|
|
394
|
+
similes: spec6.similes ? [...spec6.similes] : [],
|
|
395
|
+
description: `${spec6.description} Reads a file's contents.`,
|
|
396
|
+
validate: async (runtime, _message) => runtime.getService("coder") !== null,
|
|
397
|
+
handler: async (runtime, message, _state, options, callback) => {
|
|
398
|
+
const svc = runtime.getService("coder");
|
|
399
|
+
if (!svc) {
|
|
400
|
+
const msg = "Coder service is not available.";
|
|
401
|
+
if (callback)
|
|
402
|
+
await callback({ content: { text: msg } });
|
|
403
|
+
return { success: false, text: msg };
|
|
404
|
+
}
|
|
405
|
+
const filepath = getFilepath(options, message);
|
|
406
|
+
if (!filepath) {
|
|
407
|
+
const msg = "Missing filepath.";
|
|
408
|
+
if (callback)
|
|
409
|
+
await callback({ content: { text: msg } });
|
|
410
|
+
return { success: false, text: msg };
|
|
411
|
+
}
|
|
412
|
+
const conv = message.roomId ?? message.agentId ?? runtime.agentId;
|
|
413
|
+
const result = await svc.readFile(conv, filepath);
|
|
414
|
+
if (!result.ok) {
|
|
415
|
+
if (callback)
|
|
416
|
+
await callback({ content: { text: result.error } });
|
|
417
|
+
return { success: false, text: result.error };
|
|
418
|
+
}
|
|
419
|
+
const lang = extToLang(path.extname(filepath));
|
|
420
|
+
const fenced = `\`\`\`${lang}
|
|
421
|
+
${result.content}
|
|
422
|
+
\`\`\``;
|
|
423
|
+
if (callback)
|
|
424
|
+
await callback({ content: { text: fenced } });
|
|
425
|
+
return { success: true, text: fenced };
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
// actions/searchFiles.ts
|
|
429
|
+
function getInputs2(options) {
|
|
430
|
+
const opt = options;
|
|
431
|
+
const pattern = (opt?.pattern ?? "").trim();
|
|
432
|
+
const p = (opt?.path ?? ".").trim() || ".";
|
|
433
|
+
const mm = typeof opt?.maxMatches === "number" ? opt.maxMatches : Number.parseInt((opt?.max_matches ?? "").trim(), 10);
|
|
434
|
+
const maxMatches = Number.isFinite(mm) && mm > 0 ? mm : 50;
|
|
435
|
+
return { pattern, path: p, maxMatches };
|
|
436
|
+
}
|
|
437
|
+
var spec7 = requireActionSpec("SEARCH_FILES");
|
|
438
|
+
var searchFiles = {
|
|
439
|
+
name: spec7.name,
|
|
440
|
+
similes: spec7.similes ? [...spec7.similes] : [],
|
|
441
|
+
description: spec7.description,
|
|
442
|
+
validate: async (runtime) => runtime.getService("coder") !== null,
|
|
443
|
+
handler: async (runtime, message, _state, options, callback) => {
|
|
444
|
+
const svc = runtime.getService("coder");
|
|
445
|
+
if (!svc) {
|
|
446
|
+
const msg = "Coder service is not available.";
|
|
447
|
+
if (callback)
|
|
448
|
+
await callback({ content: { text: msg } });
|
|
449
|
+
return { success: false, text: msg };
|
|
450
|
+
}
|
|
451
|
+
const conv = message.roomId ?? message.agentId ?? runtime.agentId;
|
|
452
|
+
const { pattern, path: path2, maxMatches } = getInputs2(options);
|
|
453
|
+
if (!pattern) {
|
|
454
|
+
const msg = "Missing pattern.";
|
|
455
|
+
if (callback)
|
|
456
|
+
await callback({ content: { text: msg } });
|
|
457
|
+
return { success: false, text: msg };
|
|
458
|
+
}
|
|
459
|
+
const result = await svc.searchFiles(conv, pattern, path2, maxMatches);
|
|
460
|
+
if (!result.ok) {
|
|
461
|
+
if (callback)
|
|
462
|
+
await callback({ content: { text: result.error } });
|
|
463
|
+
return { success: false, text: result.error };
|
|
464
|
+
}
|
|
465
|
+
const out = result.matches.length === 0 ? `No matches for "${pattern}".` : result.matches.map((m) => `${m.file}:L${m.line}: ${m.content}`).join(`
|
|
466
|
+
`);
|
|
467
|
+
if (callback)
|
|
468
|
+
await callback({ content: { text: out } });
|
|
469
|
+
return { success: true, text: out };
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
// actions/writeFile.ts
|
|
473
|
+
function getInputs3(options, message) {
|
|
474
|
+
const opt = options;
|
|
475
|
+
const filepath = opt?.filepath?.trim() ?? "";
|
|
476
|
+
const content = opt?.content ?? "";
|
|
477
|
+
if (filepath)
|
|
478
|
+
return { filepath, content };
|
|
479
|
+
const text = message.content.text ?? "";
|
|
480
|
+
const m = text.match(/["'`]([^"'`]+)["'`]/);
|
|
481
|
+
const inferred = m?.[1]?.trim() ?? "";
|
|
482
|
+
return { filepath: inferred, content };
|
|
483
|
+
}
|
|
484
|
+
var spec8 = requireActionSpec("WRITE_FILE");
|
|
485
|
+
var writeFile = {
|
|
486
|
+
name: spec8.name,
|
|
487
|
+
similes: spec8.similes ? [...spec8.similes] : [],
|
|
488
|
+
description: spec8.description,
|
|
489
|
+
validate: async (runtime) => runtime.getService("coder") !== null,
|
|
490
|
+
handler: async (runtime, message, _state, options, callback) => {
|
|
491
|
+
const svc = runtime.getService("coder");
|
|
492
|
+
if (!svc) {
|
|
493
|
+
const msg2 = "Coder service is not available.";
|
|
494
|
+
if (callback)
|
|
495
|
+
await callback({ content: { text: msg2 } });
|
|
496
|
+
return { success: false, text: msg2 };
|
|
497
|
+
}
|
|
498
|
+
const { filepath, content } = getInputs3(options, message);
|
|
499
|
+
if (!filepath) {
|
|
500
|
+
const msg2 = "Missing filepath.";
|
|
501
|
+
if (callback)
|
|
502
|
+
await callback({ content: { text: msg2 } });
|
|
503
|
+
return { success: false, text: msg2 };
|
|
504
|
+
}
|
|
505
|
+
const conv = message.roomId ?? message.agentId ?? runtime.agentId;
|
|
506
|
+
const result = await svc.writeFile(conv, filepath, content);
|
|
507
|
+
if (!result.ok) {
|
|
508
|
+
if (callback)
|
|
509
|
+
await callback({ content: { text: result.error } });
|
|
510
|
+
return { success: false, text: result.error };
|
|
511
|
+
}
|
|
512
|
+
const msg = `Wrote ${filepath} (${content.length} chars)`;
|
|
513
|
+
if (callback)
|
|
514
|
+
await callback({ content: { text: msg } });
|
|
515
|
+
return { success: true, text: msg };
|
|
516
|
+
}
|
|
517
|
+
};
|
|
518
|
+
// providers/coderStatusProvider.ts
|
|
519
|
+
import {
|
|
520
|
+
addHeader,
|
|
521
|
+
logger
|
|
522
|
+
} from "@elizaos/core";
|
|
523
|
+
var MAX_OUTPUT_LENGTH = 8000;
|
|
524
|
+
var TRUNCATE_SEGMENT_LENGTH = 4000;
|
|
525
|
+
var spec9 = requireProviderSpec("coderStatusProvider");
|
|
526
|
+
var coderStatusProvider = {
|
|
527
|
+
name: spec9.name,
|
|
528
|
+
description: "Provides current working directory, allowed directory, and recent shell/file operations",
|
|
529
|
+
position: 99,
|
|
530
|
+
get: async (runtime, message, _state) => {
|
|
531
|
+
const svc = runtime.getService("coder");
|
|
532
|
+
if (!svc) {
|
|
533
|
+
logger.warn("[coderStatusProvider] Coder service not found");
|
|
534
|
+
return {
|
|
535
|
+
values: {
|
|
536
|
+
coderStatus: "Coder service is not available",
|
|
537
|
+
currentWorkingDirectory: "N/A",
|
|
538
|
+
allowedDirectory: "N/A"
|
|
539
|
+
},
|
|
540
|
+
text: addHeader("# Coder Status", "Coder service is not available"),
|
|
541
|
+
data: { historyCount: 0, cwd: "N/A", allowedDir: "N/A" }
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
const conversationId = message.roomId ?? message.agentId ?? runtime.agentId;
|
|
545
|
+
const history = svc.getCommandHistory(conversationId, 10);
|
|
546
|
+
const cwd = svc.getCurrentDirectory(conversationId);
|
|
547
|
+
const allowedDir = svc.getAllowedDirectory();
|
|
548
|
+
let historyText = "No commands in history.";
|
|
549
|
+
if (history.length > 0) {
|
|
550
|
+
historyText = history.map((entry) => {
|
|
551
|
+
let entryStr = `[${new Date(entry.timestamp).toISOString()}] ${entry.workingDirectory}> ${entry.command}`;
|
|
552
|
+
if (entry.stdout) {
|
|
553
|
+
if (entry.stdout.length > MAX_OUTPUT_LENGTH) {
|
|
554
|
+
entryStr += `
|
|
555
|
+
Output: ${entry.stdout.substring(0, TRUNCATE_SEGMENT_LENGTH)}
|
|
556
|
+
... [TRUNCATED] ...
|
|
557
|
+
${entry.stdout.substring(entry.stdout.length - TRUNCATE_SEGMENT_LENGTH)}`;
|
|
558
|
+
} else {
|
|
559
|
+
entryStr += `
|
|
560
|
+
Output: ${entry.stdout}`;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
if (entry.stderr) {
|
|
564
|
+
if (entry.stderr.length > MAX_OUTPUT_LENGTH) {
|
|
565
|
+
entryStr += `
|
|
566
|
+
Error: ${entry.stderr.substring(0, TRUNCATE_SEGMENT_LENGTH)}
|
|
567
|
+
... [TRUNCATED] ...
|
|
568
|
+
${entry.stderr.substring(entry.stderr.length - TRUNCATE_SEGMENT_LENGTH)}`;
|
|
569
|
+
} else {
|
|
570
|
+
entryStr += `
|
|
571
|
+
Error: ${entry.stderr}`;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
entryStr += `
|
|
575
|
+
Exit Code: ${entry.exitCode}`;
|
|
576
|
+
if (entry.fileOperations && entry.fileOperations.length > 0) {
|
|
577
|
+
entryStr += `
|
|
578
|
+
File Operations:`;
|
|
579
|
+
entry.fileOperations.forEach((op) => {
|
|
580
|
+
entryStr += `
|
|
581
|
+
- ${op.type}: ${op.target}`;
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
return entryStr;
|
|
585
|
+
}).join(`
|
|
586
|
+
|
|
587
|
+
`);
|
|
588
|
+
}
|
|
589
|
+
const recentFileOps = history.filter((h) => (h.fileOperations?.length ?? 0) > 0).flatMap((h) => h.fileOperations ?? []).slice(-8);
|
|
590
|
+
const fileOpsText = recentFileOps.length > 0 ? `
|
|
591
|
+
|
|
592
|
+
${addHeader("# Recent File Operations", recentFileOps.map((op) => `- ${op.type}: ${op.target}`).join(`
|
|
593
|
+
`))}` : "";
|
|
594
|
+
const text = `Current Directory: ${cwd}
|
|
595
|
+
Allowed Directory: ${allowedDir}
|
|
596
|
+
|
|
597
|
+
${addHeader("# Recent Commands (Last 10)", historyText)}${fileOpsText}`;
|
|
598
|
+
return {
|
|
599
|
+
values: {
|
|
600
|
+
coderStatus: historyText,
|
|
601
|
+
currentWorkingDirectory: cwd,
|
|
602
|
+
allowedDirectory: allowedDir
|
|
603
|
+
},
|
|
604
|
+
text,
|
|
605
|
+
data: {
|
|
606
|
+
historyCount: history.length,
|
|
607
|
+
cwd,
|
|
608
|
+
allowedDir
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
};
|
|
613
|
+
// services/coderService.ts
|
|
614
|
+
import { execSync } from "node:child_process";
|
|
615
|
+
import * as fs from "node:fs/promises";
|
|
616
|
+
import * as path4 from "node:path";
|
|
617
|
+
import { logger as logger2, Service } from "@elizaos/core";
|
|
618
|
+
|
|
619
|
+
// utils/config.ts
|
|
620
|
+
import * as path2 from "node:path";
|
|
621
|
+
function parseBool(value) {
|
|
622
|
+
const v = (value ?? "").trim().toLowerCase();
|
|
623
|
+
return v === "1" || v === "true" || v === "yes" || v === "on";
|
|
624
|
+
}
|
|
625
|
+
function parseIntMs(value, fallback) {
|
|
626
|
+
const parsed = Number.parseInt((value ?? "").trim(), 10);
|
|
627
|
+
if (!Number.isFinite(parsed) || parsed <= 0)
|
|
628
|
+
return fallback;
|
|
629
|
+
return parsed;
|
|
630
|
+
}
|
|
631
|
+
function loadCoderConfig() {
|
|
632
|
+
const enabled = parseBool(process.env.CODER_ENABLED);
|
|
633
|
+
const allowedDirectoryRaw = (process.env.CODER_ALLOWED_DIRECTORY ?? "").trim();
|
|
634
|
+
const allowedDirectory = allowedDirectoryRaw ? path2.resolve(allowedDirectoryRaw) : process.cwd();
|
|
635
|
+
const timeoutMs = parseIntMs(process.env.CODER_TIMEOUT, 30000);
|
|
636
|
+
const forbiddenCommands = (process.env.CODER_FORBIDDEN_COMMANDS ?? "").split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
637
|
+
return { enabled, allowedDirectory, timeoutMs, forbiddenCommands };
|
|
638
|
+
}
|
|
639
|
+
// utils/pathUtils.ts
|
|
640
|
+
import * as path3 from "node:path";
|
|
641
|
+
var DEFAULT_FORBIDDEN_COMMANDS = [
|
|
642
|
+
"rm -rf /",
|
|
643
|
+
"rm -rf ~",
|
|
644
|
+
"sudo rm",
|
|
645
|
+
"mkfs",
|
|
646
|
+
"dd if=/dev"
|
|
647
|
+
];
|
|
648
|
+
function extractBaseCommand(command) {
|
|
649
|
+
const trimmed = command.trim();
|
|
650
|
+
if (!trimmed)
|
|
651
|
+
return "";
|
|
652
|
+
const first = trimmed.split(/\s+/)[0];
|
|
653
|
+
return first ?? "";
|
|
654
|
+
}
|
|
655
|
+
function isSafeCommand(command) {
|
|
656
|
+
const c = command.trim();
|
|
657
|
+
if (!c)
|
|
658
|
+
return false;
|
|
659
|
+
if (c.includes("&&") || c.includes("||") || c.includes(";"))
|
|
660
|
+
return false;
|
|
661
|
+
if (c.includes("$(") || c.includes("`"))
|
|
662
|
+
return false;
|
|
663
|
+
return true;
|
|
664
|
+
}
|
|
665
|
+
function isForbiddenCommand(command, additionalForbidden) {
|
|
666
|
+
const c = command.toLowerCase();
|
|
667
|
+
for (const f of DEFAULT_FORBIDDEN_COMMANDS) {
|
|
668
|
+
if (c.includes(f.toLowerCase()))
|
|
669
|
+
return true;
|
|
670
|
+
}
|
|
671
|
+
for (const f of additionalForbidden) {
|
|
672
|
+
if (f.trim().length === 0)
|
|
673
|
+
continue;
|
|
674
|
+
if (c.includes(f.toLowerCase()))
|
|
675
|
+
return true;
|
|
676
|
+
}
|
|
677
|
+
return false;
|
|
678
|
+
}
|
|
679
|
+
function validatePath(targetPath, allowedDirectory, currentDirectory) {
|
|
680
|
+
const base = currentDirectory && currentDirectory.length > 0 ? currentDirectory : allowedDirectory;
|
|
681
|
+
const resolved = path3.resolve(base, targetPath);
|
|
682
|
+
const allowed = path3.resolve(allowedDirectory);
|
|
683
|
+
const rel = path3.relative(allowed, resolved);
|
|
684
|
+
if (rel === "")
|
|
685
|
+
return resolved;
|
|
686
|
+
if (rel.startsWith("..") || path3.isAbsolute(rel))
|
|
687
|
+
return null;
|
|
688
|
+
return resolved;
|
|
689
|
+
}
|
|
690
|
+
// services/coderService.ts
|
|
691
|
+
class CoderService extends Service {
|
|
692
|
+
static serviceType = "coder";
|
|
693
|
+
coderConfig;
|
|
694
|
+
currentDirectoryByConversation = new Map;
|
|
695
|
+
commandHistoryByConversation = new Map;
|
|
696
|
+
maxHistoryPerConversation = 100;
|
|
697
|
+
constructor(runtime) {
|
|
698
|
+
super(runtime);
|
|
699
|
+
this.coderConfig = loadCoderConfig();
|
|
700
|
+
}
|
|
701
|
+
static async start(runtime) {
|
|
702
|
+
const instance = new CoderService(runtime);
|
|
703
|
+
logger2.info("Coder service initialized");
|
|
704
|
+
return instance;
|
|
705
|
+
}
|
|
706
|
+
async stop() {
|
|
707
|
+
logger2.info("Coder service stopped");
|
|
708
|
+
}
|
|
709
|
+
get capabilityDescription() {
|
|
710
|
+
return "Filesystem + shell + git tools within a restricted directory";
|
|
711
|
+
}
|
|
712
|
+
getAllowedDirectory() {
|
|
713
|
+
return this.coderConfig.allowedDirectory;
|
|
714
|
+
}
|
|
715
|
+
getCurrentDirectory(conversationId) {
|
|
716
|
+
return this.currentDirectoryByConversation.get(conversationId) ?? this.coderConfig.allowedDirectory;
|
|
717
|
+
}
|
|
718
|
+
setCurrentDirectory(conversationId, dir) {
|
|
719
|
+
this.currentDirectoryByConversation.set(conversationId, dir);
|
|
720
|
+
}
|
|
721
|
+
getCommandHistory(conversationId, limit) {
|
|
722
|
+
const all = this.commandHistoryByConversation.get(conversationId) ?? [];
|
|
723
|
+
if (limit <= 0)
|
|
724
|
+
return [];
|
|
725
|
+
return all.slice(-limit);
|
|
726
|
+
}
|
|
727
|
+
addToHistory(conversationId, command, result, fileOperations) {
|
|
728
|
+
if (!conversationId)
|
|
729
|
+
return;
|
|
730
|
+
const list = this.commandHistoryByConversation.get(conversationId) ?? [];
|
|
731
|
+
list.push({
|
|
732
|
+
timestamp: Date.now(),
|
|
733
|
+
workingDirectory: result.executedIn,
|
|
734
|
+
command,
|
|
735
|
+
stdout: result.stdout,
|
|
736
|
+
stderr: result.stderr,
|
|
737
|
+
exitCode: result.exitCode,
|
|
738
|
+
fileOperations
|
|
739
|
+
});
|
|
740
|
+
const trimmed = list.length > this.maxHistoryPerConversation ? list.slice(-this.maxHistoryPerConversation) : list;
|
|
741
|
+
this.commandHistoryByConversation.set(conversationId, trimmed);
|
|
742
|
+
}
|
|
743
|
+
ensureEnabled() {
|
|
744
|
+
if (this.coderConfig.enabled)
|
|
745
|
+
return null;
|
|
746
|
+
return "Coder plugin is disabled. Set CODER_ENABLED=true to enable.";
|
|
747
|
+
}
|
|
748
|
+
resolveWithin(conversationId, targetPath) {
|
|
749
|
+
const cwd = this.getCurrentDirectory(conversationId);
|
|
750
|
+
const validated = validatePath(targetPath, this.coderConfig.allowedDirectory, cwd);
|
|
751
|
+
if (!validated)
|
|
752
|
+
return { error: "Cannot access path outside allowed directory" };
|
|
753
|
+
const rel = path4.relative(this.coderConfig.allowedDirectory, validated);
|
|
754
|
+
return { fullPath: validated, relPath: rel.length === 0 ? "." : rel };
|
|
755
|
+
}
|
|
756
|
+
async changeDirectory(conversationId, targetPath) {
|
|
757
|
+
const disabled = this.ensureEnabled();
|
|
758
|
+
if (disabled) {
|
|
759
|
+
return {
|
|
760
|
+
success: false,
|
|
761
|
+
stdout: "",
|
|
762
|
+
stderr: disabled,
|
|
763
|
+
exitCode: 1,
|
|
764
|
+
error: "Coder disabled",
|
|
765
|
+
executedIn: this.getCurrentDirectory(conversationId)
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
const resolved = this.resolveWithin(conversationId, targetPath);
|
|
769
|
+
if ("error" in resolved) {
|
|
770
|
+
return {
|
|
771
|
+
success: false,
|
|
772
|
+
stdout: "",
|
|
773
|
+
stderr: resolved.error,
|
|
774
|
+
exitCode: 1,
|
|
775
|
+
error: "Permission denied",
|
|
776
|
+
executedIn: this.getCurrentDirectory(conversationId)
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
try {
|
|
780
|
+
const stat2 = await fs.stat(resolved.fullPath);
|
|
781
|
+
if (!stat2.isDirectory()) {
|
|
782
|
+
return {
|
|
783
|
+
success: false,
|
|
784
|
+
stdout: "",
|
|
785
|
+
stderr: "Not a directory",
|
|
786
|
+
exitCode: 1,
|
|
787
|
+
error: "Not a directory",
|
|
788
|
+
executedIn: this.getCurrentDirectory(conversationId)
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
this.setCurrentDirectory(conversationId, resolved.fullPath);
|
|
792
|
+
return {
|
|
793
|
+
success: true,
|
|
794
|
+
stdout: `Changed directory to: ${resolved.fullPath}`,
|
|
795
|
+
stderr: "",
|
|
796
|
+
exitCode: 0,
|
|
797
|
+
executedIn: resolved.fullPath
|
|
798
|
+
};
|
|
799
|
+
} catch (err) {
|
|
800
|
+
const e = err;
|
|
801
|
+
return {
|
|
802
|
+
success: false,
|
|
803
|
+
stdout: "",
|
|
804
|
+
stderr: e.message,
|
|
805
|
+
exitCode: 1,
|
|
806
|
+
error: "Failed to change directory",
|
|
807
|
+
executedIn: this.getCurrentDirectory(conversationId)
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
async readFile(conversationId, filepath) {
|
|
812
|
+
const disabled = this.ensureEnabled();
|
|
813
|
+
if (disabled)
|
|
814
|
+
return { ok: false, error: disabled };
|
|
815
|
+
const resolved = this.resolveWithin(conversationId, filepath);
|
|
816
|
+
if ("error" in resolved)
|
|
817
|
+
return { ok: false, error: resolved.error };
|
|
818
|
+
try {
|
|
819
|
+
const stat2 = await fs.stat(resolved.fullPath);
|
|
820
|
+
if (stat2.isDirectory())
|
|
821
|
+
return { ok: false, error: "Path is a directory" };
|
|
822
|
+
const content = await fs.readFile(resolved.fullPath, "utf-8");
|
|
823
|
+
return { ok: true, content };
|
|
824
|
+
} catch (err) {
|
|
825
|
+
const e = err;
|
|
826
|
+
return {
|
|
827
|
+
ok: false,
|
|
828
|
+
error: e.code === "ENOENT" ? "File not found" : e.message
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
async writeFile(conversationId, filepath, content) {
|
|
833
|
+
const disabled = this.ensureEnabled();
|
|
834
|
+
if (disabled)
|
|
835
|
+
return { ok: false, error: disabled };
|
|
836
|
+
const resolved = this.resolveWithin(conversationId, filepath);
|
|
837
|
+
if ("error" in resolved)
|
|
838
|
+
return { ok: false, error: resolved.error };
|
|
839
|
+
try {
|
|
840
|
+
await fs.mkdir(path4.dirname(resolved.fullPath), { recursive: true });
|
|
841
|
+
await fs.writeFile(resolved.fullPath, content, "utf-8");
|
|
842
|
+
return { ok: true };
|
|
843
|
+
} catch (err) {
|
|
844
|
+
const e = err;
|
|
845
|
+
return { ok: false, error: e.message };
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
async editFile(conversationId, filepath, oldStr, newStr) {
|
|
849
|
+
const disabled = this.ensureEnabled();
|
|
850
|
+
if (disabled)
|
|
851
|
+
return { ok: false, error: disabled };
|
|
852
|
+
const resolved = this.resolveWithin(conversationId, filepath);
|
|
853
|
+
if ("error" in resolved)
|
|
854
|
+
return { ok: false, error: resolved.error };
|
|
855
|
+
try {
|
|
856
|
+
const content = await fs.readFile(resolved.fullPath, "utf-8");
|
|
857
|
+
if (!content.includes(oldStr))
|
|
858
|
+
return { ok: false, error: "Could not find old_str in file" };
|
|
859
|
+
const next = content.replace(oldStr, newStr);
|
|
860
|
+
await fs.writeFile(resolved.fullPath, next, "utf-8");
|
|
861
|
+
return { ok: true };
|
|
862
|
+
} catch (err) {
|
|
863
|
+
const e = err;
|
|
864
|
+
return {
|
|
865
|
+
ok: false,
|
|
866
|
+
error: e.code === "ENOENT" ? "File not found" : e.message
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
async listFiles(conversationId, dirPath) {
|
|
871
|
+
const disabled = this.ensureEnabled();
|
|
872
|
+
if (disabled)
|
|
873
|
+
return { ok: false, error: disabled };
|
|
874
|
+
const resolved = this.resolveWithin(conversationId, dirPath);
|
|
875
|
+
if ("error" in resolved)
|
|
876
|
+
return { ok: false, error: resolved.error };
|
|
877
|
+
try {
|
|
878
|
+
const entries = await fs.readdir(resolved.fullPath, {
|
|
879
|
+
withFileTypes: true
|
|
880
|
+
});
|
|
881
|
+
const items = entries.filter((e) => !e.name.startsWith(".")).map((e) => `${e.name}${e.isDirectory() ? "/" : ""}`).sort((a, b) => a.localeCompare(b));
|
|
882
|
+
return { ok: true, items };
|
|
883
|
+
} catch (err) {
|
|
884
|
+
const e = err;
|
|
885
|
+
return {
|
|
886
|
+
ok: false,
|
|
887
|
+
error: e.code === "ENOENT" ? "Directory not found" : e.message
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
async searchFiles(conversationId, pattern, dirPath, maxMatches) {
|
|
892
|
+
const disabled = this.ensureEnabled();
|
|
893
|
+
if (disabled)
|
|
894
|
+
return { ok: false, error: disabled };
|
|
895
|
+
const resolved = this.resolveWithin(conversationId, dirPath);
|
|
896
|
+
if ("error" in resolved)
|
|
897
|
+
return { ok: false, error: resolved.error };
|
|
898
|
+
const needle = pattern.trim();
|
|
899
|
+
if (!needle)
|
|
900
|
+
return { ok: false, error: "Missing pattern" };
|
|
901
|
+
const limit = Number.isFinite(maxMatches) && maxMatches > 0 ? Math.min(500, Math.floor(maxMatches)) : 50;
|
|
902
|
+
const matches = [];
|
|
903
|
+
await this.searchInDirectory(resolved.fullPath, needle.toLowerCase(), matches, limit);
|
|
904
|
+
return { ok: true, matches };
|
|
905
|
+
}
|
|
906
|
+
async searchInDirectory(dir, needleLower, matches, maxMatches) {
|
|
907
|
+
if (matches.length >= maxMatches)
|
|
908
|
+
return;
|
|
909
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
910
|
+
for (const entry of entries) {
|
|
911
|
+
if (matches.length >= maxMatches)
|
|
912
|
+
break;
|
|
913
|
+
if (entry.name.startsWith("."))
|
|
914
|
+
continue;
|
|
915
|
+
const full = path4.join(dir, entry.name);
|
|
916
|
+
if (entry.isDirectory()) {
|
|
917
|
+
if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "coverage" || entry.name === ".git") {
|
|
918
|
+
continue;
|
|
919
|
+
}
|
|
920
|
+
await this.searchInDirectory(full, needleLower, matches, maxMatches);
|
|
921
|
+
continue;
|
|
922
|
+
}
|
|
923
|
+
if (!entry.isFile())
|
|
924
|
+
continue;
|
|
925
|
+
const content = await fs.readFile(full, "utf-8");
|
|
926
|
+
const lines = content.split(`
|
|
927
|
+
`);
|
|
928
|
+
for (let i = 0;i < lines.length; i++) {
|
|
929
|
+
if (matches.length >= maxMatches)
|
|
930
|
+
break;
|
|
931
|
+
const line = lines[i] ?? "";
|
|
932
|
+
if (!line.toLowerCase().includes(needleLower))
|
|
933
|
+
continue;
|
|
934
|
+
matches.push({
|
|
935
|
+
file: path4.relative(this.coderConfig.allowedDirectory, full),
|
|
936
|
+
line: i + 1,
|
|
937
|
+
content: line.trim().slice(0, 240)
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
async executeShell(command, conversationId) {
|
|
943
|
+
const disabled = this.ensureEnabled();
|
|
944
|
+
if (disabled) {
|
|
945
|
+
return {
|
|
946
|
+
success: false,
|
|
947
|
+
stdout: "",
|
|
948
|
+
stderr: disabled,
|
|
949
|
+
exitCode: 1,
|
|
950
|
+
error: "Coder disabled",
|
|
951
|
+
executedIn: this.getCurrentDirectory(conversationId)
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
const trimmed = command.trim();
|
|
955
|
+
if (!trimmed) {
|
|
956
|
+
return {
|
|
957
|
+
success: false,
|
|
958
|
+
stdout: "",
|
|
959
|
+
stderr: "Invalid command",
|
|
960
|
+
exitCode: 1,
|
|
961
|
+
error: "Empty command",
|
|
962
|
+
executedIn: this.getCurrentDirectory(conversationId)
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
if (!isSafeCommand(trimmed)) {
|
|
966
|
+
return {
|
|
967
|
+
success: false,
|
|
968
|
+
stdout: "",
|
|
969
|
+
stderr: "Command contains forbidden patterns",
|
|
970
|
+
exitCode: 1,
|
|
971
|
+
error: "Security policy violation",
|
|
972
|
+
executedIn: this.getCurrentDirectory(conversationId)
|
|
973
|
+
};
|
|
974
|
+
}
|
|
975
|
+
if (isForbiddenCommand(trimmed, this.coderConfig.forbiddenCommands)) {
|
|
976
|
+
return {
|
|
977
|
+
success: false,
|
|
978
|
+
stdout: "",
|
|
979
|
+
stderr: "Command is forbidden by security policy",
|
|
980
|
+
exitCode: 1,
|
|
981
|
+
error: "Forbidden command",
|
|
982
|
+
executedIn: this.getCurrentDirectory(conversationId)
|
|
983
|
+
};
|
|
984
|
+
}
|
|
985
|
+
const cwd = this.getCurrentDirectory(conversationId);
|
|
986
|
+
try {
|
|
987
|
+
const stdout = execSync(trimmed, {
|
|
988
|
+
cwd,
|
|
989
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
990
|
+
encoding: "utf-8",
|
|
991
|
+
timeout: this.coderConfig.timeoutMs,
|
|
992
|
+
maxBuffer: 10 * 1024 * 1024
|
|
993
|
+
}).toString();
|
|
994
|
+
const result = {
|
|
995
|
+
success: true,
|
|
996
|
+
stdout,
|
|
997
|
+
stderr: "",
|
|
998
|
+
exitCode: 0,
|
|
999
|
+
executedIn: cwd
|
|
1000
|
+
};
|
|
1001
|
+
this.addToHistory(conversationId, trimmed, result);
|
|
1002
|
+
return result;
|
|
1003
|
+
} catch (err) {
|
|
1004
|
+
const e = err;
|
|
1005
|
+
const stderr = (e.stderr ?? e.message).toString();
|
|
1006
|
+
const stdout = (e.stdout ?? "").toString();
|
|
1007
|
+
const exitCode = typeof e.status === "number" ? e.status : 1;
|
|
1008
|
+
const result = {
|
|
1009
|
+
success: false,
|
|
1010
|
+
stdout,
|
|
1011
|
+
stderr,
|
|
1012
|
+
exitCode,
|
|
1013
|
+
error: "Command failed",
|
|
1014
|
+
executedIn: cwd
|
|
1015
|
+
};
|
|
1016
|
+
this.addToHistory(conversationId, trimmed, result);
|
|
1017
|
+
return result;
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
async git(args, conversationId) {
|
|
1021
|
+
return this.executeShell(`git ${args}`, conversationId);
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
// index.ts
|
|
1026
|
+
var coderPlugin = {
|
|
1027
|
+
name: "eliza-coder",
|
|
1028
|
+
description: "Coder tools: filesystem, shell, and git (restricted)",
|
|
1029
|
+
services: [CoderService],
|
|
1030
|
+
actions: [
|
|
1031
|
+
readFile,
|
|
1032
|
+
listFiles,
|
|
1033
|
+
searchFiles,
|
|
1034
|
+
writeFile,
|
|
1035
|
+
editFile,
|
|
1036
|
+
changeDirectory,
|
|
1037
|
+
executeShell,
|
|
1038
|
+
git
|
|
1039
|
+
],
|
|
1040
|
+
providers: [coderStatusProvider]
|
|
1041
|
+
};
|
|
1042
|
+
var typescript_default = coderPlugin;
|
|
1043
|
+
export {
|
|
1044
|
+
writeFile,
|
|
1045
|
+
validatePath,
|
|
1046
|
+
searchFiles,
|
|
1047
|
+
readFile,
|
|
1048
|
+
loadCoderConfig,
|
|
1049
|
+
listFiles,
|
|
1050
|
+
isSafeCommand,
|
|
1051
|
+
isForbiddenCommand,
|
|
1052
|
+
git,
|
|
1053
|
+
extractBaseCommand,
|
|
1054
|
+
executeShell,
|
|
1055
|
+
editFile,
|
|
1056
|
+
typescript_default as default,
|
|
1057
|
+
coderStatusProvider,
|
|
1058
|
+
coderPlugin,
|
|
1059
|
+
changeDirectory,
|
|
1060
|
+
DEFAULT_FORBIDDEN_COMMANDS,
|
|
1061
|
+
CoderService
|
|
1062
|
+
};
|
|
1063
|
+
|
|
1064
|
+
//# debugId=11D69657230A26B464756E2164756E21
|