@repogo/plugins 0.1.42 → 0.1.44
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 +213 -75
- package/dist/context.d.ts +20 -14
- package/dist/define.d.ts +54 -24
- package/dist/generated.d.ts +4 -3
- package/dist/index.d.ts +2 -1
- package/dist/index.js +66 -20
- package/dist/remote.d.ts +7 -0
- package/dist/runtime.d.ts +15 -10
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +76 -60
- 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 +139 -83
- package/package.json +2 -2
package/dist/define.d.ts
CHANGED
|
@@ -11,34 +11,58 @@ export type StreamDefinition = z.ZodType | {
|
|
|
11
11
|
retain: Retain;
|
|
12
12
|
};
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
15
|
-
* changes.
|
|
16
|
-
*
|
|
17
|
-
* Unmarked methods
|
|
14
|
+
* What a method does. `read` only reads, so cards may run it on their own and
|
|
15
|
+
* again when its data changes. `write` changes data, and runs only when the
|
|
16
|
+
* user acts. `destructive` can't be undone, and must ask first (`approve`).
|
|
17
|
+
* Unmarked methods write: repeating one is never assumed safe.
|
|
18
18
|
*/
|
|
19
|
-
export type
|
|
19
|
+
export type Effect = "read" | "write" | "destructive";
|
|
20
|
+
/**
|
|
21
|
+
* The chat's permission modes, strictest first, as RepoGo names them for every
|
|
22
|
+
* agent. `approveUpTo` names the least strict one a method still asks in.
|
|
23
|
+
*/
|
|
24
|
+
export declare const permissionModes: readonly ["approval-required", "auto-accept-edits", "full-access"];
|
|
25
|
+
export type PermissionMode = (typeof permissionModes)[number];
|
|
20
26
|
export type Method<I extends z.ZodType = z.ZodType, O extends z.ZodType = z.ZodType> = {
|
|
21
27
|
input: I;
|
|
22
28
|
/** What it returns; the agent and cards read field names from it. */
|
|
23
29
|
output?: O;
|
|
24
|
-
|
|
30
|
+
effect: Effect;
|
|
31
|
+
/** Set by `stream`: a read that sends items as they happen. */
|
|
32
|
+
stream?: true;
|
|
25
33
|
description?: string;
|
|
26
34
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
35
|
+
* The question asked before it runs, such as "Delete {{name}}?", filled from
|
|
36
|
+
* its input; required for a destructive method. Methods without one run on tap.
|
|
37
|
+
*/
|
|
38
|
+
approve?: string;
|
|
39
|
+
/**
|
|
40
|
+
* The least strict chat mode in which an agent's call still asks `approve`:
|
|
41
|
+
* it asks in that mode and every stricter one, and runs unasked in the rest.
|
|
42
|
+
* Unset is "full-access", every mode. A card's tap always asks.
|
|
29
43
|
*/
|
|
30
|
-
|
|
31
|
-
/** Who may call it
|
|
32
|
-
http?:
|
|
33
|
-
/** Set by `cached`: the
|
|
44
|
+
approveUpTo?: PermissionMode;
|
|
45
|
+
/** Who may call it over HTTP, at each address; an address it doesn't name serves it to no one. */
|
|
46
|
+
http?: HTTPAddresses;
|
|
47
|
+
/** Set by `cached`: the read answers from what it saved, and checks again when that's old. */
|
|
34
48
|
cache?: CacheOptions;
|
|
35
49
|
run: (input: z.infer<I>, ctx: Context) => unknown | Promise<unknown>;
|
|
36
50
|
};
|
|
37
51
|
/**
|
|
38
|
-
* `key`: the
|
|
39
|
-
* method
|
|
52
|
+
* `key`: the caller sends the plugin's key as a bearer token, which the host
|
|
53
|
+
* checks before the method runs. `none`: anyone who reaches the address may call.
|
|
40
54
|
*/
|
|
41
55
|
export type HTTPAccess = "key" | "none";
|
|
56
|
+
/**
|
|
57
|
+
* `public`: the environment's own address, through the tunnel. `device`: this
|
|
58
|
+
* machine's loopback and its containers' gateway, where the network is the boundary.
|
|
59
|
+
*/
|
|
60
|
+
export type HTTPAddresses = {
|
|
61
|
+
device?: HTTPAccess;
|
|
62
|
+
public?: HTTPAccess;
|
|
63
|
+
};
|
|
64
|
+
/** `true` is `{ public: "key" }`. */
|
|
65
|
+
export type HTTPOption = true | HTTPAddresses;
|
|
42
66
|
export type Handler = (payload: any, ctx: Context) => unknown | Promise<unknown>;
|
|
43
67
|
export type PluginDefinition = {
|
|
44
68
|
/** One line, shown to the agent in the index of plugins. */
|
|
@@ -67,22 +91,26 @@ export type PluginDefinition = {
|
|
|
67
91
|
private?: string[];
|
|
68
92
|
};
|
|
69
93
|
export declare function definePlugin<T extends PluginDefinition>(plugin: T): T;
|
|
70
|
-
|
|
71
|
-
kind?: MethodKind;
|
|
94
|
+
type MethodOptions<O extends z.ZodType> = {
|
|
72
95
|
output?: O;
|
|
73
96
|
description?: string;
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
97
|
+
http?: HTTPOption;
|
|
98
|
+
approveUpTo?: PermissionMode;
|
|
99
|
+
} & ({
|
|
100
|
+
effect: "destructive";
|
|
101
|
+
approve: string;
|
|
102
|
+
} | {
|
|
103
|
+
effect?: "read" | "write";
|
|
104
|
+
approve?: string;
|
|
105
|
+
});
|
|
106
|
+
export declare function method<I extends z.ZodType, O extends z.ZodType = z.ZodType>(input: I, run: (input: z.infer<I>, ctx: Context) => unknown | Promise<unknown>, options?: MethodOptions<O>): Method<I, O>;
|
|
79
107
|
/**
|
|
80
|
-
* A
|
|
108
|
+
* A read that answers at once from what it saved last time, for answers that
|
|
81
109
|
* are slow to get: a CLI's list, another service's API. When the saved answer
|
|
82
110
|
* is older than `fresh`, `run` goes again in the background, and cards showing
|
|
83
111
|
* it redraw only if the answer changed. With nothing saved, the first call
|
|
84
112
|
* waits for `run`. Each project keeps its own answers unless `shared`.
|
|
85
|
-
* `run` is read-only, like
|
|
113
|
+
* `run` is read-only, like any read's; it has `timeout` (a minute unless it says).
|
|
86
114
|
*/
|
|
87
115
|
export declare function cached<I extends z.ZodType, O extends z.ZodType = z.ZodType>(input: I, run: (input: z.infer<I>, ctx: Context) => unknown | Promise<unknown>, options: {
|
|
88
116
|
fresh: Duration;
|
|
@@ -90,13 +118,15 @@ export declare function cached<I extends z.ZodType, O extends z.ZodType = z.ZodT
|
|
|
90
118
|
timeout?: Duration;
|
|
91
119
|
output?: O;
|
|
92
120
|
description?: string;
|
|
121
|
+
http?: HTTPOption;
|
|
93
122
|
}): Method<I, O>;
|
|
94
123
|
/**
|
|
95
124
|
* A method that sends items as they happen: a log tail, a build's output. Each
|
|
96
125
|
* `yield` is one item; a card shows them as a list that grows. It runs while
|
|
97
126
|
* someone watches and `ctx.signal` aborts when the last one leaves, so pass it
|
|
98
|
-
* on (`Bun.spawn(cmd, { signal: ctx.signal })`). Like
|
|
127
|
+
* on (`Bun.spawn(cmd, { signal: ctx.signal })`). Like any read, it may not write.
|
|
99
128
|
*/
|
|
100
129
|
export declare function stream<I extends z.ZodType>(input: I, run: (input: z.infer<I>, ctx: Context) => AsyncIterable<unknown>, options?: {
|
|
101
130
|
description?: string;
|
|
102
131
|
}): Method<I>;
|
|
132
|
+
export {};
|
package/dist/generated.d.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import type { Bucket } from "./buckets";
|
|
3
|
-
import type {
|
|
3
|
+
import type { Effect } from "./define";
|
|
4
4
|
import type { Kv } from "./kv";
|
|
5
5
|
import type { Stream } from "./streams";
|
|
6
6
|
export type Generated = {
|
|
7
|
-
|
|
7
|
+
effect: Effect;
|
|
8
|
+
stream?: true;
|
|
8
9
|
description: string;
|
|
9
10
|
input: z.ZodType;
|
|
10
11
|
output?: z.ZodType;
|
|
11
|
-
|
|
12
|
+
approve?: string;
|
|
12
13
|
run: (input: any, signal: AbortSignal) => unknown;
|
|
13
14
|
};
|
|
14
15
|
export declare function kvMethods(name: string, kv: Kv<any>, schema: z.ZodType): Record<string, Generated>;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { cached, definePlugin, method, stream } from "./define";
|
|
2
|
+
export { remotePlugin } from "./remote";
|
|
2
3
|
export { upload, type UploadedFile } from "./uploads";
|
|
3
|
-
export type { PluginDefinition, Method,
|
|
4
|
+
export type { PluginDefinition, Method, Effect, PermissionMode, HTTPAccess, HTTPAddresses, HTTPOption, StreamDefinition } from "./define";
|
|
4
5
|
export type { Activity, ActivityProgress, ActivityTimer, ActivityTint, AgentOptions, AgentReply, Caller, Context, CronEntry, CronStatus, Manifest, ManifestCron, ManifestMethod, ManifestStorage } from "./context";
|
|
5
6
|
export type { LogLevel, LogRecord } from "./logs";
|
|
6
7
|
export type { BucketFile, FileStatus, PutFile } from "./buckets";
|
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,45 +44,54 @@ 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 = {}) {
|
|
55
|
+
if ("kind" in options)
|
|
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");
|
|
50
64
|
return {
|
|
51
65
|
input,
|
|
52
66
|
run,
|
|
53
|
-
|
|
67
|
+
effect: options.effect ?? "write",
|
|
54
68
|
output: options.output,
|
|
55
69
|
description: options.description,
|
|
56
|
-
|
|
57
|
-
|
|
70
|
+
approve: options.approve,
|
|
71
|
+
approveUpTo: options.approveUpTo,
|
|
72
|
+
http: httpAddresses(options.http)
|
|
58
73
|
};
|
|
59
74
|
}
|
|
60
75
|
function cached(input, run, options) {
|
|
61
76
|
return {
|
|
62
77
|
input,
|
|
63
78
|
run,
|
|
64
|
-
|
|
79
|
+
effect: "read",
|
|
65
80
|
output: options.output,
|
|
66
81
|
description: options.description,
|
|
82
|
+
http: httpAddresses(options.http),
|
|
67
83
|
cache: { fresh: parseDuration(options.fresh), shared: options.shared ?? false, timeout: parseDuration(options.timeout ?? "1m") }
|
|
68
84
|
};
|
|
69
85
|
}
|
|
70
86
|
function stream(input, run, options = {}) {
|
|
71
|
-
return { input, run,
|
|
72
|
-
}
|
|
73
|
-
// src/uploads.ts
|
|
74
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
75
|
-
import { z } from "zod";
|
|
76
|
-
var callFiles = new AsyncLocalStorage;
|
|
77
|
-
function upload() {
|
|
78
|
-
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), {
|
|
79
|
-
message: "Not a file uploaded for this call."
|
|
80
|
-
});
|
|
87
|
+
return { input, run, effect: "read", stream: true, description: options.description };
|
|
81
88
|
}
|
|
82
|
-
// src/
|
|
89
|
+
// src/remote.ts
|
|
83
90
|
import { z as z2 } from "zod";
|
|
84
91
|
|
|
92
|
+
// src/errors.ts
|
|
93
|
+
import { z } from "zod";
|
|
94
|
+
|
|
85
95
|
class MethodError extends Error {
|
|
86
96
|
code;
|
|
87
97
|
fieldErrors;
|
|
@@ -95,7 +105,7 @@ function toCallError(error) {
|
|
|
95
105
|
if (error instanceof MethodError) {
|
|
96
106
|
return { code: error.code, message: error.message, ...error.fieldErrors ? { fieldErrors: error.fieldErrors } : {} };
|
|
97
107
|
}
|
|
98
|
-
if (error instanceof
|
|
108
|
+
if (error instanceof z.ZodError) {
|
|
99
109
|
const fieldErrors = {};
|
|
100
110
|
for (const issue of error.issues)
|
|
101
111
|
fieldErrors["/" + issue.path.join("/")] ??= issue.message;
|
|
@@ -106,8 +116,43 @@ function toCallError(error) {
|
|
|
106
116
|
return { code: "INTERNAL", message: error instanceof Error ? error.message : String(error) };
|
|
107
117
|
}
|
|
108
118
|
|
|
109
|
-
// 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";
|
|
110
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";
|
|
111
156
|
|
|
112
157
|
// src/workflow/scope.ts
|
|
113
158
|
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
@@ -136,15 +181,16 @@ class NonRetryable extends Error {
|
|
|
136
181
|
}
|
|
137
182
|
}
|
|
138
183
|
export {
|
|
139
|
-
|
|
184
|
+
z4 as z,
|
|
140
185
|
workflow,
|
|
141
186
|
upload,
|
|
142
187
|
toCallError,
|
|
143
188
|
stream,
|
|
189
|
+
remotePlugin,
|
|
144
190
|
method,
|
|
145
191
|
definePlugin,
|
|
146
192
|
cached,
|
|
147
193
|
NonRetryable,
|
|
148
194
|
MethodError
|
|
149
195
|
};
|
|
150
|
-
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,17 +2,19 @@ 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 {
|
|
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";
|
|
9
9
|
export type RegisteredMethod = {
|
|
10
10
|
name: string;
|
|
11
11
|
plugin: string;
|
|
12
|
-
|
|
12
|
+
effect: Effect;
|
|
13
|
+
stream?: true;
|
|
13
14
|
description?: string;
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
approve?: string;
|
|
16
|
+
approveUpTo?: PermissionMode;
|
|
17
|
+
http?: HTTPAddresses;
|
|
16
18
|
input: z.ZodType;
|
|
17
19
|
output?: z.ZodType;
|
|
18
20
|
/** `raw` is the input as sent, before defaults filled it in. */
|
|
@@ -35,9 +37,11 @@ type NoticeListener = (notice: SentNotice) => void;
|
|
|
35
37
|
export type HostCaller = (plugin: string, method: string, params: unknown) => Promise<unknown>;
|
|
36
38
|
/** A schedule as the runtime keeps it: read once, with its last run. */
|
|
37
39
|
type CronJob = {
|
|
38
|
-
/** The plugin, method, schedule and
|
|
40
|
+
/** The plugin, method, schedule, zone and root: an entry the host sends again keeps its job, and its state. */
|
|
39
41
|
signature: string;
|
|
40
42
|
plugin: string;
|
|
43
|
+
/** The project the run gets as `ctx.root`; none keeps the Worker's own. */
|
|
44
|
+
root?: string;
|
|
41
45
|
/** `<plugin>.<method>`. */
|
|
42
46
|
method: string;
|
|
43
47
|
schedule: string;
|
|
@@ -71,11 +75,11 @@ export declare class Runtime {
|
|
|
71
75
|
private constructor();
|
|
72
76
|
/** The caller's project while a call runs, for plugins that serve every project. */
|
|
73
77
|
private callRoot;
|
|
74
|
-
/** 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. */
|
|
75
79
|
private callSignal;
|
|
76
80
|
/** Who started the outermost call, `ctx.caller` to the code it runs. */
|
|
77
81
|
private callCaller;
|
|
78
|
-
/** 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. */
|
|
79
83
|
private callConfirmed;
|
|
80
84
|
/** Read through `ctx.plugins()` and written as each cron run starts and ends; this runtime's own, so other Workers aren't stirred. */
|
|
81
85
|
private cronsTag;
|
|
@@ -98,9 +102,9 @@ export declare class Runtime {
|
|
|
98
102
|
caller: Caller;
|
|
99
103
|
}): Promise<unknown>;
|
|
100
104
|
/**
|
|
101
|
-
* Calls a method and reports what it read and wrote. `expect: "
|
|
105
|
+
* Calls a method and reports what it read and wrote. `expect: "read"` is how
|
|
102
106
|
* a card runs a method on its own: it refuses anything that might write, and
|
|
103
|
-
* so does everything the
|
|
107
|
+
* so does everything the read calls.
|
|
104
108
|
*/
|
|
105
109
|
/**
|
|
106
110
|
* `confirmed` is false for a tap the user hasn't answered; `ctx.call` leaves it out.
|
|
@@ -108,12 +112,13 @@ export declare class Runtime {
|
|
|
108
112
|
* pass on to the code the method calls.
|
|
109
113
|
*/
|
|
110
114
|
call(name: string, input?: unknown, options?: {
|
|
111
|
-
expect?:
|
|
115
|
+
expect?: "read";
|
|
112
116
|
confirmed?: boolean;
|
|
113
117
|
root?: string;
|
|
114
118
|
caller?: Caller;
|
|
115
119
|
chat?: string;
|
|
116
120
|
files?: UploadedFile[];
|
|
121
|
+
signal?: AbortSignal;
|
|
117
122
|
}): Promise<CallResult>;
|
|
118
123
|
/**
|
|
119
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
|
*/
|