runline 0.4.0 → 0.5.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/core/engine.js +137 -7
- package/package.json +4 -2
- package/vendor/README.md +19 -0
- package/vendor/minisearch.umd.js +2013 -0
package/dist/core/engine.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
1
2
|
import { getQuickJS, shouldInterruptAfterDeadline, } from "quickjs-emscripten";
|
|
2
3
|
import { applyEnvOverrides, updateConnectionConfig } from "../config/loader.js";
|
|
3
4
|
export class ExecutionEngine {
|
|
@@ -215,6 +216,14 @@ function formatError(cause) {
|
|
|
215
216
|
}
|
|
216
217
|
return String(cause);
|
|
217
218
|
}
|
|
219
|
+
// MiniSearch UMD bundle, vendored at the package root and inlined into the
|
|
220
|
+
// sandbox source. UMD attaches `MiniSearch` to globalThis in a non-CJS /
|
|
221
|
+
// non-AMD env (QuickJS), so pasting the file is enough.
|
|
222
|
+
//
|
|
223
|
+
// `../../vendor/...` resolves identically from src/core/engine.ts (dev) and
|
|
224
|
+
// dist/core/engine.js (published) because tsc preserves the `core/` subdir.
|
|
225
|
+
// See vendor/README.md for the upgrade procedure.
|
|
226
|
+
const __minisearchSource = readFileSync(new URL("../../vendor/minisearch.umd.js", import.meta.url), "utf8");
|
|
218
227
|
function buildHelpData(plugins) {
|
|
219
228
|
const data = {};
|
|
220
229
|
for (const p of plugins) {
|
|
@@ -223,7 +232,11 @@ function buildHelpData(plugins) {
|
|
|
223
232
|
description: a.description,
|
|
224
233
|
inputs: Object.fromEntries(Object.entries(a.inputSchema ?? {}).map(([k, v]) => [
|
|
225
234
|
k,
|
|
226
|
-
|
|
235
|
+
{
|
|
236
|
+
type: v.type,
|
|
237
|
+
required: !!v.required,
|
|
238
|
+
description: v.description,
|
|
239
|
+
},
|
|
227
240
|
])),
|
|
228
241
|
}));
|
|
229
242
|
}
|
|
@@ -247,15 +260,14 @@ const __fmt = (v) => {
|
|
|
247
260
|
try { return JSON.stringify(v); } catch { return String(v); }
|
|
248
261
|
};
|
|
249
262
|
|
|
263
|
+
// Inlined MiniSearch UMD — attaches MiniSearch to globalThis inside the sandbox.
|
|
264
|
+
${__minisearchSource}
|
|
265
|
+
|
|
250
266
|
const __help = ${JSON.stringify(helpData)};
|
|
251
267
|
|
|
252
268
|
const __makeProxy = (path = []) => new Proxy(() => undefined, {
|
|
253
269
|
get(_t, prop) {
|
|
254
270
|
if (prop === 'then' || typeof prop === 'symbol') return undefined;
|
|
255
|
-
if (prop === 'help') {
|
|
256
|
-
const pluginName = path[0];
|
|
257
|
-
return () => pluginName && __help[pluginName] ? __help[pluginName] : __help;
|
|
258
|
-
}
|
|
259
271
|
return __makeProxy([...path, String(prop)]);
|
|
260
272
|
},
|
|
261
273
|
apply(_t, _this, args) {
|
|
@@ -265,8 +277,126 @@ const __makeProxy = (path = []) => new Proxy(() => undefined, {
|
|
|
265
277
|
.then((raw) => raw === undefined ? undefined : JSON.parse(raw));
|
|
266
278
|
},
|
|
267
279
|
});
|
|
268
|
-
|
|
269
|
-
|
|
280
|
+
|
|
281
|
+
// Flat index of every "plugin.action" path → { plugin, entry }
|
|
282
|
+
const __index = (() => {
|
|
283
|
+
const out = Object.create(null);
|
|
284
|
+
for (const plugin of Object.keys(__help)) {
|
|
285
|
+
for (const e of __help[plugin]) {
|
|
286
|
+
out[plugin + '.' + e.action] = { plugin, entry: e };
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return out;
|
|
290
|
+
})();
|
|
291
|
+
|
|
292
|
+
const __formatSignature = (plugin, entry) => {
|
|
293
|
+
const fields = Object.entries(entry.inputs || {})
|
|
294
|
+
.map(([k, v]) => k + (v.required ? '' : '?') + ': ' + v.type)
|
|
295
|
+
.join(', ');
|
|
296
|
+
return plugin + '.' + entry.action + (fields ? '({ ' + fields + ' })' : '()');
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
// Build a MiniSearch index over every action path. Indexed at sandbox
|
|
300
|
+
// startup, queried by actions.find().
|
|
301
|
+
const __search = (() => {
|
|
302
|
+
const docs = [];
|
|
303
|
+
for (const path of Object.keys(__index)) {
|
|
304
|
+
const { plugin, entry } = __index[path];
|
|
305
|
+
docs.push({
|
|
306
|
+
id: path,
|
|
307
|
+
path,
|
|
308
|
+
plugin,
|
|
309
|
+
action: entry.action,
|
|
310
|
+
description: entry.description || '',
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
const ms = new MiniSearch({
|
|
314
|
+
fields: ['path', 'plugin', 'action', 'description'],
|
|
315
|
+
storeFields: ['path', 'description'],
|
|
316
|
+
searchOptions: {
|
|
317
|
+
prefix: true,
|
|
318
|
+
fuzzy: 0.2,
|
|
319
|
+
boost: { path: 3, action: 2, plugin: 2 },
|
|
320
|
+
},
|
|
321
|
+
});
|
|
322
|
+
ms.addAll(docs);
|
|
323
|
+
return ms;
|
|
324
|
+
})();
|
|
325
|
+
|
|
326
|
+
const __actionsApi = {
|
|
327
|
+
list(plugin) {
|
|
328
|
+
const paths = Object.keys(__index);
|
|
329
|
+
return plugin ? paths.filter((p) => p.startsWith(plugin + '.')) : paths;
|
|
330
|
+
},
|
|
331
|
+
describe(path) {
|
|
332
|
+
const hit = __index[path];
|
|
333
|
+
if (!hit) {
|
|
334
|
+
const near = __actionsApi.find(path, 3);
|
|
335
|
+
const hint = near.length ? ' Did you mean: ' + near.map((n) => n.path).join(', ') + '?' : '';
|
|
336
|
+
throw new Error('Unknown action: ' + path + '.' + hint);
|
|
337
|
+
}
|
|
338
|
+
return {
|
|
339
|
+
path,
|
|
340
|
+
plugin: hit.plugin,
|
|
341
|
+
action: hit.entry.action,
|
|
342
|
+
description: hit.entry.description,
|
|
343
|
+
signature: __formatSignature(hit.plugin, hit.entry),
|
|
344
|
+
inputs: hit.entry.inputs,
|
|
345
|
+
};
|
|
346
|
+
},
|
|
347
|
+
find(query, limit = 5) {
|
|
348
|
+
const q = String(query || '').trim();
|
|
349
|
+
if (!q) return [];
|
|
350
|
+
return __search.search(q).slice(0, limit).map((r) => ({
|
|
351
|
+
path: r.path,
|
|
352
|
+
description: r.description || undefined,
|
|
353
|
+
score: r.score,
|
|
354
|
+
}));
|
|
355
|
+
},
|
|
356
|
+
check(path, args) {
|
|
357
|
+
const hit = __index[path];
|
|
358
|
+
if (!hit) {
|
|
359
|
+
const near = __actionsApi.find(path, 3).map((n) => n.path);
|
|
360
|
+
return { ok: false, error: 'Unknown action: ' + path, suggestions: near };
|
|
361
|
+
}
|
|
362
|
+
const inputs = hit.entry.inputs || {};
|
|
363
|
+
const provided = args && typeof args === 'object' ? args : {};
|
|
364
|
+
const missing = [];
|
|
365
|
+
const unknown = [];
|
|
366
|
+
const typeErrors = [];
|
|
367
|
+
for (const [k, spec] of Object.entries(inputs)) {
|
|
368
|
+
if (spec.required && !(k in provided)) missing.push(k);
|
|
369
|
+
}
|
|
370
|
+
for (const k of Object.keys(provided)) {
|
|
371
|
+
if (!(k in inputs)) unknown.push(k);
|
|
372
|
+
else {
|
|
373
|
+
const expected = inputs[k].type;
|
|
374
|
+
const actual = Array.isArray(provided[k]) ? 'array' : typeof provided[k];
|
|
375
|
+
if (expected !== actual && !(provided[k] === null || provided[k] === undefined)) {
|
|
376
|
+
typeErrors.push({ field: k, expected, actual });
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return {
|
|
381
|
+
ok: missing.length === 0 && unknown.length === 0 && typeErrors.length === 0,
|
|
382
|
+
missing,
|
|
383
|
+
unknown,
|
|
384
|
+
typeErrors,
|
|
385
|
+
signature: __formatSignature(hit.plugin, hit.entry),
|
|
386
|
+
};
|
|
387
|
+
},
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
// Unknown keys (plugin names) fall through to the call proxy, so
|
|
391
|
+
// actions.github.issue.create(...) keeps working alongside the explicit
|
|
392
|
+
// list/find/describe/check/help helpers.
|
|
393
|
+
const actions = new Proxy(__actionsApi, {
|
|
394
|
+
get(target, prop) {
|
|
395
|
+
if (prop in target || typeof prop === 'symbol') return target[prop];
|
|
396
|
+
return __makeProxy([String(prop)]);
|
|
397
|
+
},
|
|
398
|
+
});
|
|
399
|
+
|
|
270
400
|
${pluginNames.map((n) => `const ${n} = __makeProxy(['${n}']);`).join("\n")}
|
|
271
401
|
|
|
272
402
|
const console = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "runline",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Code mode for agents — turn any API or command into a callable action",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
"runline": "./dist/main.js"
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
|
-
"dist"
|
|
28
|
+
"dist",
|
|
29
|
+
"vendor"
|
|
29
30
|
],
|
|
30
31
|
"scripts": {
|
|
31
32
|
"build": "tsc && bun --filter runline-plugins build && rm -rf dist/plugins && cp -R ../runline-plugins/dist dist/plugins",
|
|
@@ -58,6 +59,7 @@
|
|
|
58
59
|
"@types/bun": "^1.2.17",
|
|
59
60
|
"@types/node": "^25.5.0",
|
|
60
61
|
"@types/proper-lockfile": "^4.1.4",
|
|
62
|
+
"minisearch": "^7.2.0",
|
|
61
63
|
"typescript": "^5.8.0"
|
|
62
64
|
},
|
|
63
65
|
"dependencies": {
|
package/vendor/README.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# vendor
|
|
2
|
+
|
|
3
|
+
Third-party bundles inlined into the QuickJS sandbox at runtime.
|
|
4
|
+
|
|
5
|
+
These files are committed deliberately: the sandbox eval'd source is part of runline's behavior, and PR diffs should show exactly what changes when a bundle is bumped.
|
|
6
|
+
|
|
7
|
+
## minisearch.umd.js
|
|
8
|
+
|
|
9
|
+
UMD build of [minisearch](https://github.com/lucaong/minisearch). Read by `src/core/engine.ts` and inlined into the sandbox so `MiniSearch` is available as a global to agent code (powers `actions.find()`).
|
|
10
|
+
|
|
11
|
+
To upgrade:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm i -D minisearch@latest
|
|
15
|
+
cp node_modules/minisearch/dist/umd/index.js vendor/minisearch.umd.js
|
|
16
|
+
git commit -am "vendor: bump minisearch"
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The `minisearch` devDependency exists only as a convenience for that `cp` — nothing imports it at runtime.
|