@repogo/plugins 0.1.34 → 0.1.35
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 +139 -25
- package/dist/collections.d.ts +11 -1
- package/dist/context.d.ts +51 -0
- package/dist/cron.d.ts +21 -0
- package/dist/define.d.ts +6 -12
- package/dist/generated.d.ts +16 -0
- package/dist/index-83w1fk10.js +28 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +7 -28
- package/dist/logs.d.ts +55 -0
- package/dist/runtime.d.ts +158 -0
- package/dist/testing.d.ts +49 -0
- package/dist/testing.js +1979 -0
- package/package.json +6 -2
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type Schedule } from "./cron";
|
|
3
|
+
import type { Caller, CronEntry, CronStatus, Manifest, Notice } from "./context";
|
|
4
|
+
export type { Manifest, ManifestCron, ManifestMethod } from "./context";
|
|
5
|
+
import type { PluginDefinition, MethodKind } from "./define";
|
|
6
|
+
import { EventBus } from "./events";
|
|
7
|
+
export type RegisteredMethod = {
|
|
8
|
+
name: string;
|
|
9
|
+
plugin: string;
|
|
10
|
+
kind: MethodKind;
|
|
11
|
+
description?: string;
|
|
12
|
+
confirm?: string;
|
|
13
|
+
input: z.ZodType;
|
|
14
|
+
output?: z.ZodType;
|
|
15
|
+
/** `raw` is the input as sent, before defaults filled it in. */
|
|
16
|
+
run: (input: unknown, raw: unknown) => Promise<unknown>;
|
|
17
|
+
};
|
|
18
|
+
/** A call's result with the data it read (what a query depends on) and wrote (what it invalidates). */
|
|
19
|
+
export type CallResult = {
|
|
20
|
+
result: unknown;
|
|
21
|
+
reads: string[];
|
|
22
|
+
invalidates: string[];
|
|
23
|
+
};
|
|
24
|
+
type InvalidationListener = (tags: string[]) => void;
|
|
25
|
+
/** A notice as the host hears it: which plugin, from which project. */
|
|
26
|
+
export type SentNotice = Notice & {
|
|
27
|
+
plugin: string;
|
|
28
|
+
root: string;
|
|
29
|
+
};
|
|
30
|
+
type NoticeListener = (notice: SentNotice) => void;
|
|
31
|
+
/** Runs a host method for a plugin's `ctx.host`; the plugins server passes it to the host. */
|
|
32
|
+
export type HostCaller = (plugin: string, method: string, params: unknown) => Promise<unknown>;
|
|
33
|
+
/** A schedule as the runtime keeps it: read once, with its last run. */
|
|
34
|
+
type CronJob = {
|
|
35
|
+
/** The plugin, method, schedule and zone: an entry the host sends again keeps its job, and its state. */
|
|
36
|
+
signature: string;
|
|
37
|
+
plugin: string;
|
|
38
|
+
/** `<plugin>.<method>`. */
|
|
39
|
+
method: string;
|
|
40
|
+
schedule: string;
|
|
41
|
+
timezone: string;
|
|
42
|
+
parsed: Schedule;
|
|
43
|
+
nextRunAt: number | null;
|
|
44
|
+
lastRunAt: number | null;
|
|
45
|
+
lastDurationMs: number | null;
|
|
46
|
+
lastError: string | null;
|
|
47
|
+
running: boolean;
|
|
48
|
+
runs: number;
|
|
49
|
+
};
|
|
50
|
+
export declare class Runtime {
|
|
51
|
+
private dataDir;
|
|
52
|
+
private root;
|
|
53
|
+
private host?;
|
|
54
|
+
readonly events: EventBus;
|
|
55
|
+
readonly methods: Map<string, RegisteredMethod>;
|
|
56
|
+
readonly plugins: Map<string, PluginDefinition>;
|
|
57
|
+
readonly crons: CronJob[];
|
|
58
|
+
private databases;
|
|
59
|
+
/** Every kv, stream and bucket by tag, shared by the plugins that name it, with its schema's JSON. */
|
|
60
|
+
private stores;
|
|
61
|
+
private invalidationListeners;
|
|
62
|
+
private noticeListeners;
|
|
63
|
+
pluginsDir: string | null;
|
|
64
|
+
private constructor();
|
|
65
|
+
/** The caller's project while a call runs, for plugins that serve every project. */
|
|
66
|
+
private callRoot;
|
|
67
|
+
/** A running stream's signal, `ctx.signal` to the code it runs. */
|
|
68
|
+
private callSignal;
|
|
69
|
+
/** Who started the outermost call, `ctx.caller` to the code it runs. */
|
|
70
|
+
private callCaller;
|
|
71
|
+
/** Whether the user answered a `confirm` for this call or one it runs within, which a destructive MCP tool needs. */
|
|
72
|
+
private callConfirmed;
|
|
73
|
+
/** Read through `ctx.plugins()` and written as each cron run starts and ends; this runtime's own, so other Workers aren't stirred. */
|
|
74
|
+
private cronsTag;
|
|
75
|
+
/**
|
|
76
|
+
* `root` is the project's checkout, `ctx.root` to its plugins; the working
|
|
77
|
+
* directory when left out. Without `host`, `ctx.host` fails.
|
|
78
|
+
*/
|
|
79
|
+
static open(options: {
|
|
80
|
+
dataDir: string;
|
|
81
|
+
root?: string;
|
|
82
|
+
host?: HostCaller;
|
|
83
|
+
}): Promise<Runtime>;
|
|
84
|
+
/** `only` names the plugins to import, for a Worker that runs schedules: their neighbours' code never runs. */
|
|
85
|
+
loadDir(pluginsDir: string, only?: string[] | null): Promise<void>;
|
|
86
|
+
add(name: string, plugin: PluginDefinition): Promise<void>;
|
|
87
|
+
/**
|
|
88
|
+
* Calls a method and reports what it read and wrote. `expect: "query"` is how
|
|
89
|
+
* a card runs a method on its own: it refuses anything that might write, and
|
|
90
|
+
* so does everything the query calls.
|
|
91
|
+
*/
|
|
92
|
+
/**
|
|
93
|
+
* `confirmed` is false for a tap the user hasn't answered; `ctx.call` leaves it out.
|
|
94
|
+
* `root` (the caller's project, for the environment's own plugins) and `caller`
|
|
95
|
+
* pass on to the code the method calls.
|
|
96
|
+
*/
|
|
97
|
+
call(name: string, input?: unknown, options?: {
|
|
98
|
+
expect?: MethodKind;
|
|
99
|
+
confirmed?: boolean;
|
|
100
|
+
root?: string;
|
|
101
|
+
caller?: Caller;
|
|
102
|
+
chat?: string;
|
|
103
|
+
}): Promise<CallResult>;
|
|
104
|
+
/**
|
|
105
|
+
* Runs a stream method, passing each item it yields to `onItem` until it
|
|
106
|
+
* returns, throws or `signal` aborts. Bad input throws here, before it starts.
|
|
107
|
+
*/
|
|
108
|
+
stream(name: string, input: unknown, options: {
|
|
109
|
+
root?: string;
|
|
110
|
+
caller?: Caller;
|
|
111
|
+
signal: AbortSignal;
|
|
112
|
+
}, onItem: (item: unknown) => void): Promise<void>;
|
|
113
|
+
/** A stream called once, by an agent or by code: what arrives within `sampleMs`, at most `sampleLimit` items. */
|
|
114
|
+
private sample;
|
|
115
|
+
/** Notices plugins send, for the host to push to the phones. */
|
|
116
|
+
onNotify(listener: NoticeListener): () => boolean;
|
|
117
|
+
/** Tags written by any call, as they commit; cards refetch the queries that read them. */
|
|
118
|
+
onInvalidate(listener: InvalidationListener): () => boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Replaces the schedules with `entries`, the host's whole set for this folder,
|
|
121
|
+
* and says how each went. One the host sent before keeps its timing and runs;
|
|
122
|
+
* one left out is never started again, though a run already going finishes.
|
|
123
|
+
*/
|
|
124
|
+
setCrons(entries: CronEntry[]): CronStatus[];
|
|
125
|
+
/** Checks an entry against what's loaded, so one that could never run says why. */
|
|
126
|
+
private cronJob;
|
|
127
|
+
/**
|
|
128
|
+
* Starts every cron due by `now` and returns the runs it started, for a test
|
|
129
|
+
* to wait on. A due time that comes while the job still runs is skipped, as is
|
|
130
|
+
* one missed by more than two minutes: nothing is caught up.
|
|
131
|
+
*/
|
|
132
|
+
tickCrons(now?: number): Promise<void>[];
|
|
133
|
+
/**
|
|
134
|
+
* The run a line is attributed to: the outermost call's id, caller and chat
|
|
135
|
+
* stay through the calls it makes; the plugin and method are the code running.
|
|
136
|
+
*/
|
|
137
|
+
private logContextFor;
|
|
138
|
+
/** Starts a chat for the plugin and waits for the agent's answer. */
|
|
139
|
+
private agent;
|
|
140
|
+
manifest(): Manifest;
|
|
141
|
+
/** The crons running now, to name in a warning when something has blocked the Worker. */
|
|
142
|
+
get runningCrons(): string[];
|
|
143
|
+
/** Whether any plugin keeps kv, streams or buckets, which `tickStorage` looks after. */
|
|
144
|
+
get hasStorage(): boolean;
|
|
145
|
+
/**
|
|
146
|
+
* Expires kv keys, trims streams and rescans buckets, each as often as it
|
|
147
|
+
* needs; the Worker calls it every few seconds. One store failing doesn't stop the rest.
|
|
148
|
+
*/
|
|
149
|
+
tickStorage(now?: number): Promise<void>;
|
|
150
|
+
close(): void;
|
|
151
|
+
private database;
|
|
152
|
+
private store;
|
|
153
|
+
private storesOf;
|
|
154
|
+
private notifyInvalidated;
|
|
155
|
+
private register;
|
|
156
|
+
private registerGenerated;
|
|
157
|
+
private registerCrud;
|
|
158
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { Caller, CronEntry, CronStatus, Manifest, Notice } from "./context";
|
|
2
|
+
import type { PluginDefinition } from "./define";
|
|
3
|
+
import { type LogRecord } from "./logs";
|
|
4
|
+
export type TestCall = {
|
|
5
|
+
/** False is a tap the user hasn't answered yet: a method with a `confirm` answers CONFIRM. */
|
|
6
|
+
confirmed?: boolean;
|
|
7
|
+
caller?: Caller;
|
|
8
|
+
root?: string;
|
|
9
|
+
/** The chat an agent calls from, kept on the call's log lines. */
|
|
10
|
+
chat?: string;
|
|
11
|
+
};
|
|
12
|
+
export type TestRuntime = {
|
|
13
|
+
/** Runs `<plugin>.<method>` and returns its answer, or throws its MethodError. */
|
|
14
|
+
call: (method: string, input?: unknown, options?: TestCall) => Promise<unknown>;
|
|
15
|
+
/** Sets the schedules, the whole set, as the host does from `~/.repogo/crons.json`. */
|
|
16
|
+
setCrons: (crons: CronEntry[]) => CronStatus[];
|
|
17
|
+
/** Runs every cron due at `now` and waits for them. */
|
|
18
|
+
tickCrons: (now?: number) => Promise<void>;
|
|
19
|
+
manifest: () => Manifest;
|
|
20
|
+
/** Every `ctx.notify` so far, in order. */
|
|
21
|
+
notices: Notice[];
|
|
22
|
+
/** Every console line a plugin wrote so far, with the call it was written in; errors a method threw too. */
|
|
23
|
+
logs: LogRecord[];
|
|
24
|
+
/** Closes the data and deletes its folder. */
|
|
25
|
+
close: () => Promise<void>;
|
|
26
|
+
};
|
|
27
|
+
/** What `ctx.mcp` asks the host: the tool, its input, and whether a query or a confirmed method asks. */
|
|
28
|
+
export type TestMCPCall = {
|
|
29
|
+
project: string;
|
|
30
|
+
server: string;
|
|
31
|
+
tool: string;
|
|
32
|
+
input: unknown;
|
|
33
|
+
read: boolean;
|
|
34
|
+
confirmed: boolean;
|
|
35
|
+
};
|
|
36
|
+
/** Stands in for the user's MCP servers: answers a tool's result, or throws to refuse as a tool error. */
|
|
37
|
+
export type TestMCP = (call: TestMCPCall) => unknown;
|
|
38
|
+
/** Stands in for the host's methods (`chats.start`, `git.status`, …) behind `ctx.host` and `ctx.agent`; throw a MethodError to refuse. */
|
|
39
|
+
export type TestHost = (method: string, params: unknown) => unknown;
|
|
40
|
+
/**
|
|
41
|
+
* `const app = await testRuntime({ notes })`, then `await app.call("notes.add", { text: "hi" })`.
|
|
42
|
+
* `ctx.root` is an empty temporary folder unless `root` names a fixture project;
|
|
43
|
+
* `mcp` answers `ctx.mcp`, `host` the other host methods. Console lines still print.
|
|
44
|
+
*/
|
|
45
|
+
export declare function testRuntime(plugins: Record<string, PluginDefinition>, options?: {
|
|
46
|
+
root?: string;
|
|
47
|
+
mcp?: TestMCP;
|
|
48
|
+
host?: TestHost;
|
|
49
|
+
}): Promise<TestRuntime>;
|