runline 0.3.3 → 0.5.0

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.
@@ -1,3 +1,5 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { createRequire } from "node:module";
1
3
  import { getQuickJS, shouldInterruptAfterDeadline, } from "quickjs-emscripten";
2
4
  import { applyEnvOverrides, updateConnectionConfig } from "../config/loader.js";
3
5
  export class ExecutionEngine {
@@ -215,6 +217,15 @@ function formatError(cause) {
215
217
  }
216
218
  return String(cause);
217
219
  }
220
+ // MiniSearch UMD bundle, loaded once and inlined into the sandbox source.
221
+ // UMD assigns to globalThis.MiniSearch when run in a non-CJS / non-AMD env
222
+ // (which QuickJS is), so we just paste the file in and use the global.
223
+ const __minisearchSource = (() => {
224
+ const req = createRequire(import.meta.url);
225
+ const pkg = req.resolve("minisearch/package.json");
226
+ const path = pkg.replace(/package\.json$/, "dist/umd/index.js");
227
+ return readFileSync(path, "utf8");
228
+ })();
218
229
  function buildHelpData(plugins) {
219
230
  const data = {};
220
231
  for (const p of plugins) {
@@ -223,7 +234,11 @@ function buildHelpData(plugins) {
223
234
  description: a.description,
224
235
  inputs: Object.fromEntries(Object.entries(a.inputSchema ?? {}).map(([k, v]) => [
225
236
  k,
226
- `${v.type}${v.required ? " (required)" : ""}${v.description ? " — " + v.description : ""}`,
237
+ {
238
+ type: v.type,
239
+ required: !!v.required,
240
+ description: v.description,
241
+ },
227
242
  ])),
228
243
  }));
229
244
  }
@@ -247,15 +262,14 @@ const __fmt = (v) => {
247
262
  try { return JSON.stringify(v); } catch { return String(v); }
248
263
  };
249
264
 
265
+ // Inlined MiniSearch UMD — attaches MiniSearch to globalThis inside the sandbox.
266
+ ${__minisearchSource}
267
+
250
268
  const __help = ${JSON.stringify(helpData)};
251
269
 
252
270
  const __makeProxy = (path = []) => new Proxy(() => undefined, {
253
271
  get(_t, prop) {
254
272
  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
273
  return __makeProxy([...path, String(prop)]);
260
274
  },
261
275
  apply(_t, _this, args) {
@@ -265,8 +279,126 @@ const __makeProxy = (path = []) => new Proxy(() => undefined, {
265
279
  .then((raw) => raw === undefined ? undefined : JSON.parse(raw));
266
280
  },
267
281
  });
268
- const actions = __makeProxy();
269
- const help = () => __help;
282
+
283
+ // Flat index of every "plugin.action" path → { plugin, entry }
284
+ const __index = (() => {
285
+ const out = Object.create(null);
286
+ for (const plugin of Object.keys(__help)) {
287
+ for (const e of __help[plugin]) {
288
+ out[plugin + '.' + e.action] = { plugin, entry: e };
289
+ }
290
+ }
291
+ return out;
292
+ })();
293
+
294
+ const __formatSignature = (plugin, entry) => {
295
+ const fields = Object.entries(entry.inputs || {})
296
+ .map(([k, v]) => k + (v.required ? '' : '?') + ': ' + v.type)
297
+ .join(', ');
298
+ return plugin + '.' + entry.action + (fields ? '({ ' + fields + ' })' : '()');
299
+ };
300
+
301
+ // Build a MiniSearch index over every action path. Indexed at sandbox
302
+ // startup, queried by actions.find().
303
+ const __search = (() => {
304
+ const docs = [];
305
+ for (const path of Object.keys(__index)) {
306
+ const { plugin, entry } = __index[path];
307
+ docs.push({
308
+ id: path,
309
+ path,
310
+ plugin,
311
+ action: entry.action,
312
+ description: entry.description || '',
313
+ });
314
+ }
315
+ const ms = new MiniSearch({
316
+ fields: ['path', 'plugin', 'action', 'description'],
317
+ storeFields: ['path', 'description'],
318
+ searchOptions: {
319
+ prefix: true,
320
+ fuzzy: 0.2,
321
+ boost: { path: 3, action: 2, plugin: 2 },
322
+ },
323
+ });
324
+ ms.addAll(docs);
325
+ return ms;
326
+ })();
327
+
328
+ const __actionsApi = {
329
+ list(plugin) {
330
+ const paths = Object.keys(__index);
331
+ return plugin ? paths.filter((p) => p.startsWith(plugin + '.')) : paths;
332
+ },
333
+ describe(path) {
334
+ const hit = __index[path];
335
+ if (!hit) {
336
+ const near = __actionsApi.find(path, 3);
337
+ const hint = near.length ? ' Did you mean: ' + near.map((n) => n.path).join(', ') + '?' : '';
338
+ throw new Error('Unknown action: ' + path + '.' + hint);
339
+ }
340
+ return {
341
+ path,
342
+ plugin: hit.plugin,
343
+ action: hit.entry.action,
344
+ description: hit.entry.description,
345
+ signature: __formatSignature(hit.plugin, hit.entry),
346
+ inputs: hit.entry.inputs,
347
+ };
348
+ },
349
+ find(query, limit = 5) {
350
+ const q = String(query || '').trim();
351
+ if (!q) return [];
352
+ return __search.search(q).slice(0, limit).map((r) => ({
353
+ path: r.path,
354
+ description: r.description || undefined,
355
+ score: r.score,
356
+ }));
357
+ },
358
+ check(path, args) {
359
+ const hit = __index[path];
360
+ if (!hit) {
361
+ const near = __actionsApi.find(path, 3).map((n) => n.path);
362
+ return { ok: false, error: 'Unknown action: ' + path, suggestions: near };
363
+ }
364
+ const inputs = hit.entry.inputs || {};
365
+ const provided = args && typeof args === 'object' ? args : {};
366
+ const missing = [];
367
+ const unknown = [];
368
+ const typeErrors = [];
369
+ for (const [k, spec] of Object.entries(inputs)) {
370
+ if (spec.required && !(k in provided)) missing.push(k);
371
+ }
372
+ for (const k of Object.keys(provided)) {
373
+ if (!(k in inputs)) unknown.push(k);
374
+ else {
375
+ const expected = inputs[k].type;
376
+ const actual = Array.isArray(provided[k]) ? 'array' : typeof provided[k];
377
+ if (expected !== actual && !(provided[k] === null || provided[k] === undefined)) {
378
+ typeErrors.push({ field: k, expected, actual });
379
+ }
380
+ }
381
+ }
382
+ return {
383
+ ok: missing.length === 0 && unknown.length === 0 && typeErrors.length === 0,
384
+ missing,
385
+ unknown,
386
+ typeErrors,
387
+ signature: __formatSignature(hit.plugin, hit.entry),
388
+ };
389
+ },
390
+ };
391
+
392
+ // Unknown keys (plugin names) fall through to the call proxy, so
393
+ // actions.github.issue.create(...) keeps working alongside the explicit
394
+ // list/find/describe/check/help helpers.
395
+ const actions = new Proxy(__actionsApi, {
396
+ get(target, prop) {
397
+ if (prop in target || typeof prop === 'symbol') return target[prop];
398
+ return __makeProxy([String(prop)]);
399
+ },
400
+ });
401
+
270
402
  ${pluginNames.map((n) => `const ${n} = __makeProxy(['${n}']);`).join("\n")}
271
403
 
272
404
  const console = {
package/dist/main.js CHANGED
File without changes
@@ -270,7 +270,7 @@ const REPLY_METADATA_HEADERS = [
270
270
  /**
271
271
  * Translate a friendly filter bag into Gmail's list query shape.
272
272
  *
273
- * Mirrors n8n's `prepareQuery` helper: `sender`, `readStatus`,
273
+ * `sender`, `readStatus`,
274
274
  * `receivedAfter`, `receivedBefore` fold into the `q=` search
275
275
  * expression (which itself can be combined with an explicit `q`).
276
276
  * `labelIds`, `includeSpamTrash`, `pageToken`, `maxResults` pass
@@ -582,7 +582,7 @@ export default function gmail(rl) {
582
582
  },
583
583
  },
584
584
  async execute(input, ctx) {
585
- const p = input;
585
+ const p = (input ?? {});
586
586
  const email = {
587
587
  to: normalizeAddressList(p.to),
588
588
  cc: normalizeAddressList(p.cc),
@@ -613,7 +613,7 @@ export default function gmail(rl) {
613
613
  attachments: { type: "array", required: false },
614
614
  },
615
615
  async execute(input, ctx) {
616
- const p = input;
616
+ const p = (input ?? {});
617
617
  return replyToMessage(ctx, p.messageId, p);
618
618
  },
619
619
  });
@@ -634,7 +634,7 @@ export default function gmail(rl) {
634
634
  },
635
635
  },
636
636
  async execute(input, ctx) {
637
- const p = input;
637
+ const p = (input ?? {});
638
638
  const qs = { format: p.format ?? "full" };
639
639
  if (p.metadataHeaders)
640
640
  qs.metadataHeaders = p.metadataHeaders;
@@ -672,7 +672,7 @@ export default function gmail(rl) {
672
672
  },
673
673
  },
674
674
  async execute(input, ctx) {
675
- const p = input;
675
+ const p = (input ?? {});
676
676
  const qs = buildListQuery(p);
677
677
  if (p.returnAll) {
678
678
  return paginateAll(ctx, "/messages", "messages", qs);
@@ -777,7 +777,7 @@ export default function gmail(rl) {
777
777
  },
778
778
  },
779
779
  async execute(input, ctx) {
780
- const p = input;
780
+ const p = (input ?? {});
781
781
  const qs = { format: p.format ?? "full" };
782
782
  if (p.metadataHeaders)
783
783
  qs.metadataHeaders = p.metadataHeaders;
@@ -803,7 +803,7 @@ export default function gmail(rl) {
803
803
  returnAll: { type: "boolean", required: false },
804
804
  },
805
805
  async execute(input, ctx) {
806
- const p = input;
806
+ const p = (input ?? {});
807
807
  const qs = buildListQuery(p);
808
808
  if (p.returnAll)
809
809
  return paginateAll(ctx, "/threads", "threads", qs);
@@ -875,7 +875,7 @@ export default function gmail(rl) {
875
875
  attachments: { type: "array", required: false },
876
876
  },
877
877
  async execute(input, ctx) {
878
- const p = input;
878
+ const p = (input ?? {});
879
879
  const thread = (await gmailRequest(ctx, "GET", `/threads/${p.id}`, undefined, { format: "minimal" }));
880
880
  const last = thread.messages?.[thread.messages.length - 1];
881
881
  if (!last?.id) {
@@ -905,7 +905,7 @@ export default function gmail(rl) {
905
905
  attachments: { type: "array", required: false },
906
906
  },
907
907
  async execute(input, ctx) {
908
- const p = input;
908
+ const p = (input ?? {});
909
909
  const from = p.from ?? p.fromAlias;
910
910
  const email = {
911
911
  to: normalizeAddressList(p.to) ?? "",
@@ -943,7 +943,7 @@ export default function gmail(rl) {
943
943
  format: { type: "string", required: false },
944
944
  },
945
945
  async execute(input, ctx) {
946
- const p = input;
946
+ const p = (input ?? {});
947
947
  const qs = { format: p.format ?? "full" };
948
948
  return gmailRequest(ctx, "GET", `/drafts/${p.id}`, undefined, qs);
949
949
  },
@@ -958,7 +958,7 @@ export default function gmail(rl) {
958
958
  returnAll: { type: "boolean", required: false },
959
959
  },
960
960
  async execute(input, ctx) {
961
- const p = input;
961
+ const p = (input ?? {});
962
962
  const qs = {};
963
963
  if (p.q)
964
964
  qs.q = p.q;
@@ -1006,7 +1006,7 @@ export default function gmail(rl) {
1006
1006
  },
1007
1007
  },
1008
1008
  async execute(input, ctx) {
1009
- const p = input;
1009
+ const p = (input ?? {});
1010
1010
  const body = { name: p.name };
1011
1011
  if (p.labelListVisibility)
1012
1012
  body.labelListVisibility = p.labelListVisibility;
@@ -1047,7 +1047,7 @@ export default function gmail(rl) {
1047
1047
  messageListVisibility: { type: "string", required: false },
1048
1048
  },
1049
1049
  async execute(input, ctx) {
1050
- const p = input;
1050
+ const p = (input ?? {});
1051
1051
  const body = {};
1052
1052
  if (p.name)
1053
1053
  body.name = p.name;