@repogo/plugins 0.1.43 → 0.1.45

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.
@@ -18,6 +18,7 @@ __export(exports_src, {
18
18
  upload: () => upload,
19
19
  toCallError: () => toCallError,
20
20
  stream: () => stream,
21
+ remotePlugin: () => remotePlugin,
21
22
  method: () => method,
22
23
  definePlugin: () => definePlugin,
23
24
  cached: () => cached,
@@ -43,20 +44,32 @@ function parseDuration(duration) {
43
44
  }
44
45
 
45
46
  // src/define.ts
47
+ var permissionModes = ["approval-required", "auto-accept-edits", "full-access"];
48
+ function httpAddresses(option) {
49
+ return option === true ? { public: "key" } : option;
50
+ }
46
51
  function definePlugin(plugin) {
47
52
  return plugin;
48
53
  }
49
54
  function method(input, run, options = {}) {
50
55
  if ("kind" in options)
51
56
  throw new Error('method: kind is now effect: "read", "write" or "destructive"');
57
+ if ("confirm" in options)
58
+ throw new Error('method: confirm is now approve: "Delete {{id}}?"');
59
+ if (options.approveUpTo !== undefined && !permissionModes.includes(options.approveUpTo)) {
60
+ throw new Error(`method: approveUpTo is one of ${permissionModes.join(", ")}`);
61
+ }
62
+ if (options.approveUpTo !== undefined && !options.approve)
63
+ throw new Error("method: approveUpTo needs an approve question to ask");
52
64
  return {
53
65
  input,
54
66
  run,
55
67
  effect: options.effect ?? "write",
56
68
  output: options.output,
57
69
  description: options.description,
58
- confirm: options.confirm,
59
- http: options.http === true ? "key" : options.http?.auth
70
+ approve: options.approve,
71
+ approveUpTo: options.approveUpTo,
72
+ http: httpAddresses(options.http)
60
73
  };
61
74
  }
62
75
  function cached(input, run, options) {
@@ -66,15 +79,13 @@ function cached(input, run, options) {
66
79
  effect: "read",
67
80
  output: options.output,
68
81
  description: options.description,
82
+ http: httpAddresses(options.http),
69
83
  cache: { fresh: parseDuration(options.fresh), shared: options.shared ?? false, timeout: parseDuration(options.timeout ?? "1m") }
70
84
  };
71
85
  }
72
86
  function stream(input, run, options = {}) {
73
87
  return { input, run, effect: "read", stream: true, description: options.description };
74
88
  }
75
- // src/uploads.ts
76
- import { AsyncLocalStorage } from "async_hooks";
77
-
78
89
  // node_modules/zod/v4/classic/external.js
79
90
  var exports_external = {};
80
91
  __export(exports_external, {
@@ -19494,13 +19505,6 @@ function bigint3(params) {
19494
19505
  function date4(params) {
19495
19506
  return _coercedDate(ZodDate, params);
19496
19507
  }
19497
- // src/uploads.ts
19498
- var callFiles = new AsyncLocalStorage;
19499
- function upload() {
19500
- return exports_external.object({ id: exports_external.string(), name: exports_external.string(), type: exports_external.string(), size: exports_external.number(), path: exports_external.string() }).refine((file2) => (callFiles.getStore() ?? []).some((held) => held.id === file2.id && held.path === file2.path), {
19501
- message: "Not a file uploaded for this call."
19502
- });
19503
- }
19504
19508
  // src/errors.ts
19505
19509
  class MethodError extends Error {
19506
19510
  code;
@@ -19525,6 +19529,40 @@ function toCallError(error62) {
19525
19529
  }
19526
19530
  return { code: "INTERNAL", message: error62 instanceof Error ? error62.message : String(error62) };
19527
19531
  }
19532
+
19533
+ // src/remote.ts
19534
+ var codes = { 400: "VALIDATION", 403: "FORBIDDEN", 404: "NOT_FOUND" };
19535
+ async function remotePlugin(url2, name) {
19536
+ const base = `${url2.replace(/\/$/, "")}/${name}`;
19537
+ const response = await fetch(base);
19538
+ if (!response.ok)
19539
+ throw new Error(`remotePlugin: ${base} answered ${response.status}`);
19540
+ const listing = await response.json();
19541
+ const methods = {};
19542
+ for (const m of listing.methods) {
19543
+ methods[m.name] = method(exports_external.fromJSONSchema(m.input), (input2) => call(`${base}/${m.name}`, input2), {
19544
+ effect: m.effect,
19545
+ description: m.description ?? undefined,
19546
+ output: m.output ? exports_external.fromJSONSchema(m.output) : undefined
19547
+ });
19548
+ }
19549
+ return { description: listing.description ?? undefined, methods };
19550
+ }
19551
+ async function call(url2, input2) {
19552
+ const response = await fetch(url2, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(input2) });
19553
+ const answer = await response.json().catch(() => null);
19554
+ if (response.ok)
19555
+ return answer;
19556
+ throw new MethodError(codes[response.status] ?? "INTERNAL", answer?.error ?? `${url2} answered ${response.status}`, answer?.fields ?? undefined);
19557
+ }
19558
+ // src/uploads.ts
19559
+ import { AsyncLocalStorage } from "async_hooks";
19560
+ var callFiles = new AsyncLocalStorage;
19561
+ function upload() {
19562
+ return exports_external.object({ id: exports_external.string(), name: exports_external.string(), type: exports_external.string(), size: exports_external.number(), path: exports_external.string() }).refine((file2) => (callFiles.getStore() ?? []).some((held) => held.id === file2.id && held.path === file2.path), {
19563
+ message: "Not a file uploaded for this call."
19564
+ });
19565
+ }
19528
19566
  // src/workflow/scope.ts
19529
19567
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
19530
19568
  var attemptScope = new AsyncLocalStorage2;
@@ -20111,14 +20149,14 @@ class AnswerCache {
20111
20149
  checked_at integer not null
20112
20150
  )`);
20113
20151
  }
20114
- async read(key, options, fetch) {
20152
+ async read(key, options, fetch2) {
20115
20153
  const tag = `${this.namespace}.cached.${Bun.hash(key).toString(36)}`;
20116
20154
  recordRead(tag);
20117
20155
  const [row] = await this.database.sql.unsafe(`select value, checked_at from cached_answers where key = $1`, [key]);
20118
20156
  if (!row)
20119
- return this.refresh(key, tag, options, fetch);
20157
+ return this.refresh(key, tag, options, fetch2);
20120
20158
  if (Date.now() - row.checked_at >= options.fresh) {
20121
- this.refresh(key, tag, options, fetch).catch((error62) => record2("error", [error62]));
20159
+ this.refresh(key, tag, options, fetch2).catch((error62) => record2("error", [error62]));
20122
20160
  }
20123
20161
  return JSON.parse(row.value);
20124
20162
  }
@@ -20126,12 +20164,12 @@ class AnswerCache {
20126
20164
  while (this.inflight.size)
20127
20165
  await Promise.allSettled([...this.inflight.values()]);
20128
20166
  }
20129
- refresh(key, tag, options, fetch) {
20167
+ refresh(key, tag, options, fetch2) {
20130
20168
  const running = this.inflight.get(key);
20131
20169
  if (running)
20132
20170
  return running;
20133
20171
  const started = Date.now();
20134
- const work = isolated(async () => withTimeout(Promise.resolve(fetch()), options.timeout)).then(async (value) => {
20172
+ const work = isolated(async () => withTimeout(Promise.resolve(fetch2()), options.timeout)).then(async (value) => {
20135
20173
  await this.save(key, tag, value ?? null, started);
20136
20174
  return value ?? null;
20137
20175
  }, async (error62) => {
@@ -20557,7 +20595,7 @@ function kvMethods(name, kv, schema) {
20557
20595
  description: `Delete a ${name} key`,
20558
20596
  input: exports_external.object({ key }),
20559
20597
  output: exports_external.null(),
20560
- confirm: "Delete this key?",
20598
+ approve: "Delete this key?",
20561
20599
  run: async ({ key: key2 }) => (await kv.delete(key2), null)
20562
20600
  }
20563
20601
  };
@@ -20606,7 +20644,7 @@ function streamMethods(name, stream2, schema) {
20606
20644
  description: `Delete a ${name} key and its lines`,
20607
20645
  input: exports_external.object({ key }),
20608
20646
  output: exports_external.null(),
20609
- confirm: "Delete this stream and its lines?",
20647
+ approve: "Delete this stream and its lines?",
20610
20648
  run: async ({ key: key2 }) => (await stream2.delete(key2), null)
20611
20649
  }
20612
20650
  };
@@ -20687,7 +20725,7 @@ function bucketMethods(name, bucket, schema) {
20687
20725
  description: `Delete a ${name} file and its meta`,
20688
20726
  input: exports_external.object({ id }),
20689
20727
  output: exports_external.null(),
20690
- confirm: "Delete this file?",
20728
+ approve: "Delete this file?",
20691
20729
  run: async ({ id: id2 }) => (await bucket.delete(id2), null)
20692
20730
  }
20693
20731
  };
@@ -21096,7 +21134,7 @@ var RunMetadataSchema = exports_external.object({
21096
21134
  root: exports_external.string().refine(isAbsolute2),
21097
21135
  namespace: exports_external.string().regex(/^[a-z0-9][a-z0-9.-]*$/),
21098
21136
  plugin: exports_external.string().regex(/^[a-z0-9][a-z0-9-]*$/),
21099
- caller: exports_external.enum(["card", "agent", "cron", "code", "http"]),
21137
+ caller: exports_external.enum(["card", "agent", "cron", "code", "http", "device"]),
21100
21138
  workflow: exports_external.string().regex(/^[a-z][a-zA-Z0-9_-]*$/),
21101
21139
  input: exports_external.json(),
21102
21140
  key: exports_external.string().nullable(),
@@ -21119,13 +21157,13 @@ function workflowMethods(plugin, namespace, name, definition, ctx, bridge, direc
21119
21157
  return bridge({ operation, dir, plugin, namespace, workflow: name, root: ctx.root, caller: ctx.caller, input: input2, settings: workflowSettings(definition) });
21120
21158
  };
21121
21159
  const id = exports_external.object({ id: exports_external.string().uuid() }).strict();
21122
- const spec = (method2, effect, input2, description, confirm, output2) => ({
21160
+ const spec = (method2, effect, input2, description, approve, output2) => ({
21123
21161
  name: `${plugin}.${name}.${method2}`,
21124
21162
  plugin,
21125
21163
  effect,
21126
21164
  input: input2,
21127
21165
  description,
21128
- confirm,
21166
+ approve,
21129
21167
  output: output2,
21130
21168
  run: async (input3, raw) => {
21131
21169
  if (effect === "read")
@@ -21137,7 +21175,7 @@ function workflowMethods(plugin, namespace, name, definition, ctx, bridge, direc
21137
21175
  return [
21138
21176
  spec("list", "read", exports_external.object({ limit: exports_external.number().int().min(1).max(100).default(20), status: exports_external.string().optional(), cursor: exports_external.string().optional() }).strict(), "List workflow runs", undefined, exports_external.object({ items: exports_external.array(RunSummarySchema), cursor: exports_external.string().nullable() })),
21139
21177
  spec("get", "read", id, "Get one workflow run"),
21140
- spec("start", "write", exports_external.object({ input: definition.input, key: exports_external.string().min(1).max(200).optional() }).strict(), definition.description ?? "Start a durable workflow", definition.confirm, RunSummarySchema),
21178
+ spec("start", "write", exports_external.object({ input: definition.input, key: exports_external.string().min(1).max(200).optional() }).strict(), definition.description ?? "Start a durable workflow", definition.approve, RunSummarySchema),
21141
21179
  spec("retry", "write", id, "Retry a safely failed query step", undefined, RunSummarySchema),
21142
21180
  spec("restart", "write", id, "Start a run that needs attention over from the beginning, on the current code", undefined, RunSummarySchema),
21143
21181
  spec("cancel", "write", id, "Request cancellation of a workflow", undefined, RunSummarySchema),
@@ -21331,7 +21369,8 @@ class Runtime {
21331
21369
  effect: method2.effect,
21332
21370
  stream: method2.stream,
21333
21371
  description: method2.description,
21334
- confirm: method2.confirm,
21372
+ approve: method2.approve,
21373
+ approveUpTo: method2.approveUpTo,
21335
21374
  http: method2.http,
21336
21375
  input: method2.input,
21337
21376
  output: method2.output,
@@ -21362,6 +21401,10 @@ class Runtime {
21362
21401
  })))));
21363
21402
  }
21364
21403
  async call(name, input2 = {}, options = {}) {
21404
+ if (options.signal) {
21405
+ const { signal, ...rest } = options;
21406
+ return this.callSignal.run(signal, () => this.call(name, input2, rest));
21407
+ }
21365
21408
  if (options.files) {
21366
21409
  const { files, ...rest } = options;
21367
21410
  const fields = input2 ?? {};
@@ -21390,12 +21433,12 @@ class Runtime {
21390
21433
  }
21391
21434
  const raw = input2 ?? {};
21392
21435
  const parsed = method2.input.parse(raw);
21393
- if (method2.confirm && (options.confirmed === false || workflowScope.getStore()))
21394
- throw new MethodError("CONFIRM", fillConfirm(method2.confirm, parsed));
21436
+ if (method2.approve && (options.confirmed === false || workflowScope.getStore()))
21437
+ throw new MethodError("CONFIRM", fillQuestion(method2.approve, parsed));
21395
21438
  if (method2.stream)
21396
21439
  return { result: await this.sample(name, input2), reads: [], invalidates: [] };
21397
21440
  const confirmed = this.callConfirmed.getStore() ?? false;
21398
- const run = await tracked(method2.effect === "read", (tags) => this.notifyInvalidated(tags), () => this.callConfirmed.run(confirmed || method2.confirm !== undefined, async () => {
21441
+ const run = await tracked(method2.effect === "read", (tags) => this.notifyInvalidated(tags), () => this.callConfirmed.run(confirmed || method2.approve !== undefined, async () => {
21399
21442
  const result = await method2.run(parsed, raw);
21400
21443
  await this.events.emit(name, { input: parsed, result });
21401
21444
  return result;
@@ -21504,7 +21547,7 @@ class Runtime {
21504
21547
  throw new Error(`${entry.method} isn't one of ${entry.plugin}'s methods`);
21505
21548
  if (method2.stream)
21506
21549
  throw new Error(`${name} is a stream; a cron runs a read or a write`);
21507
- if (method2.confirm)
21550
+ if (method2.approve)
21508
21551
  throw new Error(`${name} asks before it runs, so nothing may run it on a schedule`);
21509
21552
  if (!method2.input.safeParse({}).success)
21510
21553
  throw new Error(`${name} needs input; a cron calls it with {}`);
@@ -21609,7 +21652,8 @@ class Runtime {
21609
21652
  effect: method2.effect,
21610
21653
  stream: method2.stream ?? false,
21611
21654
  description: method2.description ?? null,
21612
- confirm: method2.confirm ?? null,
21655
+ approve: method2.approve ?? null,
21656
+ approveUpTo: method2.approve ? method2.approveUpTo ?? "full-access" : null,
21613
21657
  http: method2.http ?? null,
21614
21658
  input: exports_external.toJSONSchema(method2.input, { io: "input", unrepresentable: "any" }),
21615
21659
  output: method2.output ? exports_external.toJSONSchema(method2.output, { io: "output", unrepresentable: "any" }) : null
@@ -21683,8 +21727,8 @@ class Runtime {
21683
21727
  register(method2) {
21684
21728
  if (this.methods.has(method2.name))
21685
21729
  throw new Error(`${method2.name} is defined twice; rename the method or the data`);
21686
- if (method2.effect === "destructive" && !method2.confirm) {
21687
- throw new Error(`${method2.name} is destructive, so it needs a confirm question, such as { confirm: "Delete {{id}}?" }`);
21730
+ if (method2.effect === "destructive" && !method2.approve) {
21731
+ throw new Error(`${method2.name} is destructive, so it needs an approve question, such as { approve: "Delete {{id}}?" }`);
21688
21732
  }
21689
21733
  this.methods.set(method2.name, method2);
21690
21734
  }
@@ -21702,12 +21746,12 @@ class Runtime {
21702
21746
  const prefix = `${plugin}.${name}`;
21703
21747
  const id = exports_external.object({ id: exports_external.string() });
21704
21748
  const record3 = schema.extend({ id: exports_external.string(), createdAt: exports_external.string(), updatedAt: exports_external.string() });
21705
- const crud = (method2, effect, input2, output2, description, run, confirm) => this.register({
21749
+ const crud = (method2, effect, input2, output2, description, run, approve) => this.register({
21706
21750
  name: `${prefix}.${method2}`,
21707
21751
  plugin,
21708
21752
  effect,
21709
21753
  description,
21710
- confirm,
21754
+ approve,
21711
21755
  input: input2,
21712
21756
  output: output2,
21713
21757
  run: async (value, raw) => run(value, raw)
@@ -21727,7 +21771,7 @@ class Runtime {
21727
21771
  }, "Delete this record?");
21728
21772
  }
21729
21773
  }
21730
- function fillConfirm(question, input2) {
21774
+ function fillQuestion(question, input2) {
21731
21775
  const fields = input2 ?? {};
21732
21776
  return question.replace(/\{\{\s*([A-Za-z0-9_]+)\s*\}\}/g, (_, field2) => {
21733
21777
  const value = Object.hasOwn(fields, field2) ? fields[field2] : undefined;
@@ -21752,7 +21796,7 @@ function mcpFailure(server, tool, error62) {
21752
21796
  case "NOT_READ_ONLY":
21753
21797
  return new MethodError("FORBIDDEN", `${server}.${tool} changes data, so it runs from a write, not a read.`);
21754
21798
  case "CONFIRM":
21755
- return new MethodError("FORBIDDEN", `${server}.${tool} may destroy data, so it runs only from a method with a confirm.`);
21799
+ return new MethodError("FORBIDDEN", `${server}.${tool} may destroy data, so it runs only from a method with an approve.`);
21756
21800
  }
21757
21801
  return new MethodError("INTERNAL", error62.message);
21758
21802
  }
@@ -22860,12 +22904,12 @@ function runExecutor() {
22860
22904
  return;
22861
22905
  }
22862
22906
  if (message.type === "hosted") {
22863
- const call = waiting.get(message.id);
22907
+ const call2 = waiting.get(message.id);
22864
22908
  waiting.delete(message.id);
22865
22909
  if (message.error)
22866
- call?.reject(new MethodError(message.error.code, message.error.message));
22910
+ call2?.reject(new MethodError(message.error.code, message.error.message));
22867
22911
  else
22868
- call?.resolve(message.result);
22912
+ call2?.resolve(message.result);
22869
22913
  return;
22870
22914
  }
22871
22915
  if (launched)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@repogo/plugins",
3
- "version": "0.1.43",
3
+ "version": "0.1.45",
4
4
  "description": "Write RepoGo plugins: collections and methods over SQLite that agents and cards call",
5
5
  "type": "module",
6
6
  "exports": {
@@ -27,7 +27,7 @@
27
27
  "typecheck": "tsc --noEmit"
28
28
  },
29
29
  "dependencies": {
30
- "zod": "^4.1.0"
30
+ "zod": "^4.6.0"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/bun": "latest",