@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/README.md +169 -44
- package/dist/context.d.ts +14 -12
- package/dist/define.d.ts +33 -11
- package/dist/generated.d.ts +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +61 -17
- package/dist/remote.d.ts +7 -0
- package/dist/runtime.d.ts +7 -5
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +25 -19
- package/dist/workflow/contracts.d.ts +3 -1
- package/dist/workflow/definition.d.ts +1 -1
- package/dist/workflow/events.d.ts +1 -0
- package/dist/workflow-executor.js +83 -39
- package/package.json +2 -2
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: () =>
|
|
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
|
-
|
|
59
|
-
|
|
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/
|
|
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
|
|
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/
|
|
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
|
-
|
|
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,
|
|
196
|
+
export { parseDuration, MethodError, toCallError, callFiles, attemptScope, workflowScope, workflowContext, exports_src };
|
package/dist/remote.d.ts
ADDED
|
@@ -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,
|
|
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
|
-
|
|
16
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
1801
|
-
throw new MethodError("CONFIRM",
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
2094
|
-
throw new Error(`${method.name} is destructive, so it needs
|
|
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,
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
26
|
+
approve?: string;
|
|
27
27
|
timeout?: Duration;
|
|
28
28
|
maxOffline?: Duration;
|
|
29
29
|
retention?: Duration;
|