@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.
package/dist/index.js CHANGED
@@ -13,11 +13,12 @@ var __export = (target, all) => {
13
13
  // src/index.ts
14
14
  var exports_src = {};
15
15
  __export(exports_src, {
16
- z: () => z3,
16
+ z: () => z4,
17
17
  workflow: () => workflow,
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,24 +79,19 @@ 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
- import { z } from "zod";
78
- var callFiles = new AsyncLocalStorage;
79
- function upload() {
80
- return z.object({ id: z.string(), name: z.string(), type: z.string(), size: z.number(), path: z.string() }).refine((file) => (callFiles.getStore() ?? []).some((held) => held.id === file.id && held.path === file.path), {
81
- message: "Not a file uploaded for this call."
82
- });
83
- }
84
- // src/errors.ts
89
+ // src/remote.ts
85
90
  import { z as z2 } from "zod";
86
91
 
92
+ // src/errors.ts
93
+ import { z } from "zod";
94
+
87
95
  class MethodError extends Error {
88
96
  code;
89
97
  fieldErrors;
@@ -97,7 +105,7 @@ function toCallError(error) {
97
105
  if (error instanceof MethodError) {
98
106
  return { code: error.code, message: error.message, ...error.fieldErrors ? { fieldErrors: error.fieldErrors } : {} };
99
107
  }
100
- if (error instanceof z2.ZodError) {
108
+ if (error instanceof z.ZodError) {
101
109
  const fieldErrors = {};
102
110
  for (const issue of error.issues)
103
111
  fieldErrors["/" + issue.path.join("/")] ??= issue.message;
@@ -108,8 +116,43 @@ function toCallError(error) {
108
116
  return { code: "INTERNAL", message: error instanceof Error ? error.message : String(error) };
109
117
  }
110
118
 
111
- // src/index.ts
119
+ // src/remote.ts
120
+ var codes = { 400: "VALIDATION", 403: "FORBIDDEN", 404: "NOT_FOUND" };
121
+ async function remotePlugin(url, name) {
122
+ const base = `${url.replace(/\/$/, "")}/${name}`;
123
+ const response = await fetch(base);
124
+ if (!response.ok)
125
+ throw new Error(`remotePlugin: ${base} answered ${response.status}`);
126
+ const listing = await response.json();
127
+ const methods = {};
128
+ for (const m of listing.methods) {
129
+ methods[m.name] = method(z2.fromJSONSchema(m.input), (input) => call(`${base}/${m.name}`, input), {
130
+ effect: m.effect,
131
+ description: m.description ?? undefined,
132
+ output: m.output ? z2.fromJSONSchema(m.output) : undefined
133
+ });
134
+ }
135
+ return { description: listing.description ?? undefined, methods };
136
+ }
137
+ async function call(url, input) {
138
+ const response = await fetch(url, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(input) });
139
+ const answer = await response.json().catch(() => null);
140
+ if (response.ok)
141
+ return answer;
142
+ throw new MethodError(codes[response.status] ?? "INTERNAL", answer?.error ?? `${url} answered ${response.status}`, answer?.fields ?? undefined);
143
+ }
144
+ // src/uploads.ts
145
+ import { AsyncLocalStorage } from "async_hooks";
112
146
  import { z as z3 } from "zod";
147
+ var callFiles = new AsyncLocalStorage;
148
+ function upload() {
149
+ return z3.object({ id: z3.string(), name: z3.string(), type: z3.string(), size: z3.number(), path: z3.string() }).refine((file) => (callFiles.getStore() ?? []).some((held) => held.id === file.id && held.path === file.path), {
150
+ message: "Not a file uploaded for this call."
151
+ });
152
+ }
153
+
154
+ // src/index.ts
155
+ import { z as z4 } from "zod";
113
156
 
114
157
  // src/workflow/scope.ts
115
158
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
@@ -138,15 +181,16 @@ class NonRetryable extends Error {
138
181
  }
139
182
  }
140
183
  export {
141
- z3 as z,
184
+ z4 as z,
142
185
  workflow,
143
186
  upload,
144
187
  toCallError,
145
188
  stream,
189
+ remotePlugin,
146
190
  method,
147
191
  definePlugin,
148
192
  cached,
149
193
  NonRetryable,
150
194
  MethodError
151
195
  };
152
- export { parseDuration, callFiles, MethodError, toCallError, attemptScope, workflowScope, workflowContext, exports_src };
196
+ export { parseDuration, MethodError, toCallError, callFiles, attemptScope, workflowScope, workflowContext, exports_src };
@@ -0,0 +1,7 @@
1
+ import { type PluginDefinition } from "./define";
2
+ /**
3
+ * A plugin whose methods run on another host: each method `name` opens to
4
+ * `device` at `url` (such as http://192.168.64.1:47444), with its schemas, as
5
+ * a request there. That host keeps the plugin's code, keys and data.
6
+ */
7
+ export declare function remotePlugin(url: string, name: string): Promise<PluginDefinition>;
package/dist/runtime.d.ts CHANGED
@@ -2,7 +2,7 @@ import { z } from "zod";
2
2
  import { type Schedule } from "./cron";
3
3
  import type { Caller, CronEntry, CronStatus, Manifest, Notice } from "./context";
4
4
  export type { Manifest, ManifestCron, ManifestMethod } from "./context";
5
- import type { Effect, HTTPAccess, PluginDefinition } from "./define";
5
+ import type { Effect, HTTPAddresses, PermissionMode, PluginDefinition } from "./define";
6
6
  import { EventBus } from "./events";
7
7
  import type { WorkflowBridge } from "./workflow/contracts";
8
8
  import { type UploadedFile } from "./uploads";
@@ -12,8 +12,9 @@ export type RegisteredMethod = {
12
12
  effect: Effect;
13
13
  stream?: true;
14
14
  description?: string;
15
- confirm?: string;
16
- http?: HTTPAccess;
15
+ approve?: string;
16
+ approveUpTo?: PermissionMode;
17
+ http?: HTTPAddresses;
17
18
  input: z.ZodType;
18
19
  output?: z.ZodType;
19
20
  /** `raw` is the input as sent, before defaults filled it in. */
@@ -74,11 +75,11 @@ export declare class Runtime {
74
75
  private constructor();
75
76
  /** The caller's project while a call runs, for plugins that serve every project. */
76
77
  private callRoot;
77
- /** A running stream's signal, `ctx.signal` to the code it runs. */
78
+ /** A running stream's or cancelable call's signal, `ctx.signal` to the code it runs. */
78
79
  private callSignal;
79
80
  /** Who started the outermost call, `ctx.caller` to the code it runs. */
80
81
  private callCaller;
81
- /** Whether the user answered a `confirm` for this call or one it runs within, which a destructive MCP tool needs. */
82
+ /** Whether the user answered an `approve` for this call or one it runs within, which a destructive MCP tool needs. */
82
83
  private callConfirmed;
83
84
  /** Read through `ctx.plugins()` and written as each cron run starts and ends; this runtime's own, so other Workers aren't stirred. */
84
85
  private cronsTag;
@@ -117,6 +118,7 @@ export declare class Runtime {
117
118
  caller?: Caller;
118
119
  chat?: string;
119
120
  files?: UploadedFile[];
121
+ signal?: AbortSignal;
120
122
  }): Promise<CallResult>;
121
123
  /**
122
124
  * Runs a stream method, passing each item it yields to `onItem` until it
package/dist/testing.d.ts CHANGED
@@ -41,7 +41,7 @@ export type TestMCP = (call: TestMCPCall) => unknown;
41
41
  /** Stands in for the host's methods (`chats.start`, `git.status`, …) behind `ctx.host` and `ctx.agent`; throw a MethodError to refuse. */
42
42
  export type TestHost = (method: string, params: unknown) => unknown;
43
43
  /**
44
- * `const app = await testRuntime({ notes })`, then `await app.call("notes.add", { text: "hi" })`.
44
+ * `const app = await testRuntime({ notes })`, then `await app.call("notes.notes.create", { text: "hi" })`.
45
45
  * `ctx.root` is an empty temporary folder unless `root` names a fixture project;
46
46
  * `mcp` answers `ctx.mcp`, `host` the other host methods. Console lines still print.
47
47
  */
package/dist/testing.js CHANGED
@@ -979,7 +979,7 @@ function kvMethods(name, kv, schema) {
979
979
  description: `Delete a ${name} key`,
980
980
  input: z.object({ key }),
981
981
  output: z.null(),
982
- confirm: "Delete this key?",
982
+ approve: "Delete this key?",
983
983
  run: async ({ key: key2 }) => (await kv.delete(key2), null)
984
984
  }
985
985
  };
@@ -1028,7 +1028,7 @@ function streamMethods(name, stream, schema) {
1028
1028
  description: `Delete a ${name} key and its lines`,
1029
1029
  input: z.object({ key }),
1030
1030
  output: z.null(),
1031
- confirm: "Delete this stream and its lines?",
1031
+ approve: "Delete this stream and its lines?",
1032
1032
  run: async ({ key: key2 }) => (await stream.delete(key2), null)
1033
1033
  }
1034
1034
  };
@@ -1109,7 +1109,7 @@ function bucketMethods(name, bucket, schema) {
1109
1109
  description: `Delete a ${name} file and its meta`,
1110
1110
  input: z.object({ id }),
1111
1111
  output: z.null(),
1112
- confirm: "Delete this file?",
1112
+ approve: "Delete this file?",
1113
1113
  run: async ({ id: id2 }) => (await bucket.delete(id2), null)
1114
1114
  }
1115
1115
  };
@@ -1503,7 +1503,7 @@ var RunMetadataSchema = z3.object({
1503
1503
  root: z3.string().refine(isAbsolute2),
1504
1504
  namespace: z3.string().regex(/^[a-z0-9][a-z0-9.-]*$/),
1505
1505
  plugin: z3.string().regex(/^[a-z0-9][a-z0-9-]*$/),
1506
- caller: z3.enum(["card", "agent", "cron", "code", "http"]),
1506
+ caller: z3.enum(["card", "agent", "cron", "code", "http", "device"]),
1507
1507
  workflow: z3.string().regex(/^[a-z][a-zA-Z0-9_-]*$/),
1508
1508
  input: z3.json(),
1509
1509
  key: z3.string().nullable(),
@@ -1526,13 +1526,13 @@ function workflowMethods(plugin, namespace, name, definition, ctx, bridge, direc
1526
1526
  return bridge({ operation, dir, plugin, namespace, workflow: name, root: ctx.root, caller: ctx.caller, input, settings: workflowSettings(definition) });
1527
1527
  };
1528
1528
  const id = z4.object({ id: z4.string().uuid() }).strict();
1529
- const spec = (method, effect, input, description, confirm, output) => ({
1529
+ const spec = (method, effect, input, description, approve, output) => ({
1530
1530
  name: `${plugin}.${name}.${method}`,
1531
1531
  plugin,
1532
1532
  effect,
1533
1533
  input,
1534
1534
  description,
1535
- confirm,
1535
+ approve,
1536
1536
  output,
1537
1537
  run: async (input2, raw) => {
1538
1538
  if (effect === "read")
@@ -1544,7 +1544,7 @@ function workflowMethods(plugin, namespace, name, definition, ctx, bridge, direc
1544
1544
  return [
1545
1545
  spec("list", "read", z4.object({ limit: z4.number().int().min(1).max(100).default(20), status: z4.string().optional(), cursor: z4.string().optional() }).strict(), "List workflow runs", undefined, z4.object({ items: z4.array(RunSummarySchema), cursor: z4.string().nullable() })),
1546
1546
  spec("get", "read", id, "Get one workflow run"),
1547
- spec("start", "write", z4.object({ input: definition.input, key: z4.string().min(1).max(200).optional() }).strict(), definition.description ?? "Start a durable workflow", definition.confirm, RunSummarySchema),
1547
+ spec("start", "write", z4.object({ input: definition.input, key: z4.string().min(1).max(200).optional() }).strict(), definition.description ?? "Start a durable workflow", definition.approve, RunSummarySchema),
1548
1548
  spec("retry", "write", id, "Retry a safely failed query step", undefined, RunSummarySchema),
1549
1549
  spec("restart", "write", id, "Start a run that needs attention over from the beginning, on the current code", undefined, RunSummarySchema),
1550
1550
  spec("cancel", "write", id, "Request cancellation of a workflow", undefined, RunSummarySchema),
@@ -1738,7 +1738,8 @@ class Runtime {
1738
1738
  effect: method.effect,
1739
1739
  stream: method.stream,
1740
1740
  description: method.description,
1741
- confirm: method.confirm,
1741
+ approve: method.approve,
1742
+ approveUpTo: method.approveUpTo,
1742
1743
  http: method.http,
1743
1744
  input: method.input,
1744
1745
  output: method.output,
@@ -1769,6 +1770,10 @@ class Runtime {
1769
1770
  })))));
1770
1771
  }
1771
1772
  async call(name, input = {}, options = {}) {
1773
+ if (options.signal) {
1774
+ const { signal, ...rest } = options;
1775
+ return this.callSignal.run(signal, () => this.call(name, input, rest));
1776
+ }
1772
1777
  if (options.files) {
1773
1778
  const { files, ...rest } = options;
1774
1779
  const fields = input ?? {};
@@ -1797,12 +1802,12 @@ class Runtime {
1797
1802
  }
1798
1803
  const raw = input ?? {};
1799
1804
  const parsed = method.input.parse(raw);
1800
- if (method.confirm && (options.confirmed === false || workflowScope.getStore()))
1801
- throw new MethodError("CONFIRM", fillConfirm(method.confirm, parsed));
1805
+ if (method.approve && (options.confirmed === false || workflowScope.getStore()))
1806
+ throw new MethodError("CONFIRM", fillQuestion(method.approve, parsed));
1802
1807
  if (method.stream)
1803
1808
  return { result: await this.sample(name, input), reads: [], invalidates: [] };
1804
1809
  const confirmed = this.callConfirmed.getStore() ?? false;
1805
- const run = await tracked(method.effect === "read", (tags) => this.notifyInvalidated(tags), () => this.callConfirmed.run(confirmed || method.confirm !== undefined, async () => {
1810
+ const run = await tracked(method.effect === "read", (tags) => this.notifyInvalidated(tags), () => this.callConfirmed.run(confirmed || method.approve !== undefined, async () => {
1806
1811
  const result = await method.run(parsed, raw);
1807
1812
  await this.events.emit(name, { input: parsed, result });
1808
1813
  return result;
@@ -1911,7 +1916,7 @@ class Runtime {
1911
1916
  throw new Error(`${entry.method} isn't one of ${entry.plugin}'s methods`);
1912
1917
  if (method.stream)
1913
1918
  throw new Error(`${name} is a stream; a cron runs a read or a write`);
1914
- if (method.confirm)
1919
+ if (method.approve)
1915
1920
  throw new Error(`${name} asks before it runs, so nothing may run it on a schedule`);
1916
1921
  if (!method.input.safeParse({}).success)
1917
1922
  throw new Error(`${name} needs input; a cron calls it with {}`);
@@ -2016,7 +2021,8 @@ class Runtime {
2016
2021
  effect: method.effect,
2017
2022
  stream: method.stream ?? false,
2018
2023
  description: method.description ?? null,
2019
- confirm: method.confirm ?? null,
2024
+ approve: method.approve ?? null,
2025
+ approveUpTo: method.approve ? method.approveUpTo ?? "full-access" : null,
2020
2026
  http: method.http ?? null,
2021
2027
  input: z5.toJSONSchema(method.input, { io: "input", unrepresentable: "any" }),
2022
2028
  output: method.output ? z5.toJSONSchema(method.output, { io: "output", unrepresentable: "any" }) : null
@@ -2090,8 +2096,8 @@ class Runtime {
2090
2096
  register(method) {
2091
2097
  if (this.methods.has(method.name))
2092
2098
  throw new Error(`${method.name} is defined twice; rename the method or the data`);
2093
- if (method.effect === "destructive" && !method.confirm) {
2094
- throw new Error(`${method.name} is destructive, so it needs a confirm question, such as { confirm: "Delete {{id}}?" }`);
2099
+ if (method.effect === "destructive" && !method.approve) {
2100
+ throw new Error(`${method.name} is destructive, so it needs an approve question, such as { approve: "Delete {{id}}?" }`);
2095
2101
  }
2096
2102
  this.methods.set(method.name, method);
2097
2103
  }
@@ -2109,12 +2115,12 @@ class Runtime {
2109
2115
  const prefix = `${plugin}.${name}`;
2110
2116
  const id = z5.object({ id: z5.string() });
2111
2117
  const record2 = schema.extend({ id: z5.string(), createdAt: z5.string(), updatedAt: z5.string() });
2112
- const crud = (method, effect, input, output, description, run, confirm) => this.register({
2118
+ const crud = (method, effect, input, output, description, run, approve) => this.register({
2113
2119
  name: `${prefix}.${method}`,
2114
2120
  plugin,
2115
2121
  effect,
2116
2122
  description,
2117
- confirm,
2123
+ approve,
2118
2124
  input,
2119
2125
  output,
2120
2126
  run: async (value, raw) => run(value, raw)
@@ -2134,7 +2140,7 @@ class Runtime {
2134
2140
  }, "Delete this record?");
2135
2141
  }
2136
2142
  }
2137
- function fillConfirm(question, input) {
2143
+ function fillQuestion(question, input) {
2138
2144
  const fields = input ?? {};
2139
2145
  return question.replace(/\{\{\s*([A-Za-z0-9_]+)\s*\}\}/g, (_, field2) => {
2140
2146
  const value = Object.hasOwn(fields, field2) ? fields[field2] : undefined;
@@ -2159,7 +2165,7 @@ function mcpFailure(server, tool, error) {
2159
2165
  case "NOT_READ_ONLY":
2160
2166
  return new MethodError("FORBIDDEN", `${server}.${tool} changes data, so it runs from a write, not a read.`);
2161
2167
  case "CONFIRM":
2162
- return new MethodError("FORBIDDEN", `${server}.${tool} may destroy data, so it runs only from a method with a confirm.`);
2168
+ return new MethodError("FORBIDDEN", `${server}.${tool} may destroy data, so it runs only from a method with an approve.`);
2163
2169
  }
2164
2170
  return new MethodError("INTERNAL", error.message);
2165
2171
  }
@@ -35,7 +35,7 @@ export type WorkflowTarget = {
35
35
  export type WorkflowRequest = WorkflowTarget & {
36
36
  operation: "start" | "list" | "get" | "cancel" | "cancelAll" | "retry" | "restart";
37
37
  input: Record<string, unknown>;
38
- caller?: "card" | "agent" | "cron" | "code" | "http";
38
+ caller?: "card" | "agent" | "cron" | "code" | "http" | "device";
39
39
  settings?: {
40
40
  timeoutMs: number;
41
41
  maxOfflineMs: number;
@@ -55,6 +55,7 @@ export declare const RunMetadataSchema: z.ZodObject<{
55
55
  cron: "cron";
56
56
  code: "code";
57
57
  http: "http";
58
+ device: "device";
58
59
  }>;
59
60
  workflow: z.ZodString;
60
61
  input: z.ZodJSONSchema;
@@ -79,6 +80,7 @@ export declare const RunLocationSchema: z.ZodObject<{
79
80
  cron: "cron";
80
81
  code: "code";
81
82
  http: "http";
83
+ device: "device";
82
84
  }>;
83
85
  }, z.core.$strict>;
84
86
  export type RunMetadata = z.infer<typeof RunMetadataSchema>;
@@ -23,7 +23,7 @@ export declare function workflowContext(base: Context, run: RunContext & {
23
23
  export type WorkflowOptions<O extends z.ZodType = z.ZodType> = {
24
24
  description?: string;
25
25
  output?: O;
26
- confirm?: string;
26
+ approve?: string;
27
27
  timeout?: Duration;
28
28
  maxOffline?: Duration;
29
29
  retention?: Duration;
@@ -24,6 +24,7 @@ export declare const EventInputSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
24
24
  cron: "cron";
25
25
  code: "code";
26
26
  http: "http";
27
+ device: "device";
27
28
  }>;
28
29
  }, z.core.$strict>>;
29
30
  }, z.core.$strict>, z.ZodObject<{