@workser/runtime 0.0.0-stage → 0.1.0
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/LICENSE +21 -0
- package/README.md +71 -2
- package/dist/cjs/client/follow.js +103 -0
- package/dist/cjs/client/index.js +6 -0
- package/dist/cjs/contract/actions.js +223 -0
- package/dist/cjs/contract/catalog.js +335 -0
- package/dist/cjs/contract/index.js +20 -0
- package/dist/cjs/contract/ordered-feed.js +94 -0
- package/dist/cjs/contract/types.js +9 -0
- package/dist/cjs/engine/engine.js +2 -0
- package/dist/cjs/index.js +27 -0
- package/dist/cjs/internal/async-queue.js +56 -0
- package/dist/cjs/internal/sse.js +170 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/place/place.js +2 -0
- package/dist/cjs/runner/index.js +6 -0
- package/dist/cjs/runner/journal.js +82 -0
- package/dist/cjs/runner/runner-client.js +477 -0
- package/dist/client/follow.d.ts +45 -0
- package/dist/client/follow.js +100 -0
- package/dist/client/follow.js.map +1 -0
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.js +2 -0
- package/dist/client/index.js.map +1 -0
- package/dist/contract/actions.d.ts +73 -0
- package/dist/contract/actions.js +217 -0
- package/dist/contract/actions.js.map +1 -0
- package/dist/contract/catalog.d.ts +97 -0
- package/dist/contract/catalog.js +328 -0
- package/dist/contract/catalog.js.map +1 -0
- package/dist/contract/index.d.ts +4 -0
- package/dist/contract/index.js +5 -0
- package/dist/contract/index.js.map +1 -0
- package/dist/contract/ordered-feed.d.ts +56 -0
- package/dist/contract/ordered-feed.js +91 -0
- package/dist/contract/ordered-feed.js.map +1 -0
- package/dist/contract/types.d.ts +311 -0
- package/dist/contract/types.js +9 -0
- package/dist/contract/types.js.map +1 -0
- package/dist/engine/engine.d.ts +39 -0
- package/dist/engine/engine.js +2 -0
- package/dist/engine/engine.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/async-queue.d.ts +14 -0
- package/dist/internal/async-queue.js +53 -0
- package/dist/internal/async-queue.js.map +1 -0
- package/dist/internal/sse.d.ts +57 -0
- package/dist/internal/sse.js +164 -0
- package/dist/internal/sse.js.map +1 -0
- package/dist/place/place.d.ts +81 -0
- package/dist/place/place.js +2 -0
- package/dist/place/place.js.map +1 -0
- package/dist/runner/index.d.ts +2 -0
- package/dist/runner/index.js +2 -0
- package/dist/runner/index.js.map +1 -0
- package/dist/runner/journal.d.ts +32 -0
- package/dist/runner/journal.js +79 -0
- package/dist/runner/journal.js.map +1 -0
- package/dist/runner/runner-client.d.ts +185 -0
- package/dist/runner/runner-client.js +473 -0
- package/dist/runner/runner-client.js.map +1 -0
- package/package.json +30 -4
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shapes a run is made of, as core-api sends and accepts them.
|
|
3
|
+
*
|
|
4
|
+
* One shape per thing, with optional fields, rather than tagged unions:
|
|
5
|
+
* core-api builds without strictNullChecks, where a union doesn't narrow, and
|
|
6
|
+
* the same types are used on both sides of the wire.
|
|
7
|
+
*/
|
|
8
|
+
/** Which kind of worker runs the job. */
|
|
9
|
+
export type RunLane = 'service' | 'workbench' | 'computer';
|
|
10
|
+
/** Where the work happens. */
|
|
11
|
+
export type RunPlace = 'service' | 'desktop' | 'sandbox' | 'cloud_computer' | 'cloud_browser';
|
|
12
|
+
export type RunStatus = 'QUEUED' | 'STARTING' | 'RUNNING' | 'COMPLETED' | 'FAILED' | 'CANCELLED';
|
|
13
|
+
/** `GET v1/runner/runs/:id` — what a runner needs to start or resume. */
|
|
14
|
+
export interface RunBrief {
|
|
15
|
+
protocol: number;
|
|
16
|
+
run: {
|
|
17
|
+
id: string;
|
|
18
|
+
status: RunStatus;
|
|
19
|
+
agent_id: string;
|
|
20
|
+
session_id: string;
|
|
21
|
+
project_id: string | null;
|
|
22
|
+
project_task_id: string | null;
|
|
23
|
+
lane: RunLane | null;
|
|
24
|
+
place: RunPlace | null;
|
|
25
|
+
kind: string | null;
|
|
26
|
+
/** Credits this run may spend, as a decimal string (bigint on the server). */
|
|
27
|
+
budget_credits: string | null;
|
|
28
|
+
parent_run_id: string | null;
|
|
29
|
+
input: Record<string, unknown>;
|
|
30
|
+
/**
|
|
31
|
+
* The engine session an earlier runner of this run had (its last
|
|
32
|
+
* `engine.session` event), so a restart can reattach. Null when none.
|
|
33
|
+
*/
|
|
34
|
+
resume?: RunResume | null;
|
|
35
|
+
};
|
|
36
|
+
cursors: {
|
|
37
|
+
/** Last event seq core-api stored for this runner. Send from +1. */
|
|
38
|
+
runner_seq: number;
|
|
39
|
+
/** Last event position in the run's log. */
|
|
40
|
+
event_pos: number;
|
|
41
|
+
/** Last command position. */
|
|
42
|
+
command_pos: number;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* The business's company brain (core-api migration 247): the prompt section
|
|
46
|
+
* every job follows, and the never rules with a check. Absent from an older
|
|
47
|
+
* core-api, or when the run belongs to no project.
|
|
48
|
+
*/
|
|
49
|
+
company_brain?: {
|
|
50
|
+
block: string;
|
|
51
|
+
never_rules: Array<{
|
|
52
|
+
id: string;
|
|
53
|
+
text: string;
|
|
54
|
+
guard: Record<string, unknown>;
|
|
55
|
+
}>;
|
|
56
|
+
} | null;
|
|
57
|
+
}
|
|
58
|
+
/** Where an earlier runner of this run left its engine session. */
|
|
59
|
+
export interface RunResume {
|
|
60
|
+
engine: string;
|
|
61
|
+
session_id: string;
|
|
62
|
+
workdir: string;
|
|
63
|
+
}
|
|
64
|
+
/** `POST v1/runs/:id/runner` — handing a run to a runner. */
|
|
65
|
+
export interface AttachRunnerResult {
|
|
66
|
+
run_id: string;
|
|
67
|
+
/** Shown once. Good for this run only. */
|
|
68
|
+
token: string;
|
|
69
|
+
expires_at: string;
|
|
70
|
+
protocol: number;
|
|
71
|
+
}
|
|
72
|
+
/** What core-api tells a runner. */
|
|
73
|
+
export type RunnerCommandKind = 'cancel' | 'pause' | 'resume' | 'answer' | 'approval' | 'message';
|
|
74
|
+
/**
|
|
75
|
+
* One command. `payload` by kind:
|
|
76
|
+
* - cancel: { reason }
|
|
77
|
+
* - pause / resume: {}
|
|
78
|
+
* - answer: { approval_id, answer } (a reply to a question)
|
|
79
|
+
* - approval: { approval_id, decision: 'approved' | 'denied', auto? }
|
|
80
|
+
* - message: { text } (the owner said something mid-run)
|
|
81
|
+
*/
|
|
82
|
+
export interface RunnerCommand {
|
|
83
|
+
pos: number;
|
|
84
|
+
kind: RunnerCommandKind;
|
|
85
|
+
payload: RunnerCommandPayload;
|
|
86
|
+
created_at: string;
|
|
87
|
+
}
|
|
88
|
+
export interface RunnerCommandPayload {
|
|
89
|
+
reason?: string;
|
|
90
|
+
approval_id?: string;
|
|
91
|
+
answer?: string;
|
|
92
|
+
decision?: 'approved' | 'denied';
|
|
93
|
+
auto?: boolean;
|
|
94
|
+
text?: string;
|
|
95
|
+
[key: string]: unknown;
|
|
96
|
+
}
|
|
97
|
+
/** Things a runner may ask the owner. Credentials are never asked this way. */
|
|
98
|
+
export type AskKind = 'question' | 'permission' | 'spend';
|
|
99
|
+
/** `POST v1/runner/runs/:id/asks` body. */
|
|
100
|
+
export interface Ask {
|
|
101
|
+
kind: AskKind;
|
|
102
|
+
title: string;
|
|
103
|
+
message: string;
|
|
104
|
+
/** For a multiple-choice question. The reply must be one of these. */
|
|
105
|
+
choices?: string[];
|
|
106
|
+
action_summary?: string;
|
|
107
|
+
/** Hashed server-side to fingerprint the action; never stored. */
|
|
108
|
+
payload?: Record<string, unknown>;
|
|
109
|
+
timeout_seconds?: number;
|
|
110
|
+
}
|
|
111
|
+
export interface AskResult {
|
|
112
|
+
approval_id: string;
|
|
113
|
+
status: 'pending' | 'approved' | 'denied' | 'expired';
|
|
114
|
+
auto_approved: boolean;
|
|
115
|
+
expires_at: string | null;
|
|
116
|
+
}
|
|
117
|
+
/** A file or result another run can use, passed by reference, never copied. */
|
|
118
|
+
export interface ArtifactRef {
|
|
119
|
+
artifact_id: string;
|
|
120
|
+
name?: string;
|
|
121
|
+
kind?: 'file' | 'diff' | 'test_report' | 'screenshot' | 'media' | 'data';
|
|
122
|
+
mime_type?: string;
|
|
123
|
+
version?: number;
|
|
124
|
+
}
|
|
125
|
+
/** A done-check's outcome (tests, typecheck, deck validates, numbers match). */
|
|
126
|
+
export interface CheckResult {
|
|
127
|
+
name: string;
|
|
128
|
+
status: 'pass' | 'fail' | 'skip';
|
|
129
|
+
detail?: string;
|
|
130
|
+
}
|
|
131
|
+
/** Tokens and cost as a runner saw them. Billing uses the gateway's figure. */
|
|
132
|
+
export interface Usage {
|
|
133
|
+
model?: string;
|
|
134
|
+
tokens_in?: number;
|
|
135
|
+
tokens_out?: number;
|
|
136
|
+
cost_usd?: number;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* One agent asking another for a sub-job. The contract, not a chat message:
|
|
140
|
+
* what done means, what goes in, what must come back, and what it may spend.
|
|
141
|
+
*/
|
|
142
|
+
export interface Handoff {
|
|
143
|
+
goal: string;
|
|
144
|
+
/** Who does it. Default: the same agent as the parent. Same org always. */
|
|
145
|
+
agent_id?: string;
|
|
146
|
+
inputs: {
|
|
147
|
+
artifacts?: ArtifactRef[];
|
|
148
|
+
/** Small typed values. Anything large goes in as an artifact. */
|
|
149
|
+
data?: Record<string, unknown>;
|
|
150
|
+
};
|
|
151
|
+
/** What the child must return, e.g. a JSON Schema for `HandoffResult.data`. */
|
|
152
|
+
expected: {
|
|
153
|
+
description: string;
|
|
154
|
+
schema?: Record<string, unknown>;
|
|
155
|
+
artifacts?: string[];
|
|
156
|
+
};
|
|
157
|
+
/** Carved from the parent's budget; never more than it has left. */
|
|
158
|
+
budget_credits?: number;
|
|
159
|
+
/** ISO time the parent stops waiting. */
|
|
160
|
+
deadline?: string;
|
|
161
|
+
/** Who approves the child's risky actions. */
|
|
162
|
+
approval_policy?: 'ask_owner' | 'inherit' | 'auto';
|
|
163
|
+
place?: RunPlace;
|
|
164
|
+
lane?: RunLane;
|
|
165
|
+
}
|
|
166
|
+
export interface HandoffResult {
|
|
167
|
+
child_run_id: string;
|
|
168
|
+
status: 'completed' | 'failed' | 'cancelled';
|
|
169
|
+
summary?: string;
|
|
170
|
+
data?: Record<string, unknown>;
|
|
171
|
+
artifacts?: ArtifactRef[];
|
|
172
|
+
error?: string;
|
|
173
|
+
}
|
|
174
|
+
/** `GET v1/projects/:projectId/runs/stream` first frame, one row per run. */
|
|
175
|
+
export interface ProjectRunRow {
|
|
176
|
+
id: string;
|
|
177
|
+
status: RunStatus;
|
|
178
|
+
lane: RunLane | null;
|
|
179
|
+
place: RunPlace | null;
|
|
180
|
+
kind: string | null;
|
|
181
|
+
project_task_id: string | null;
|
|
182
|
+
parent_run_id: string | null;
|
|
183
|
+
waiting_on: string | null;
|
|
184
|
+
event_pos: number;
|
|
185
|
+
created_at: string;
|
|
186
|
+
updated_at: string;
|
|
187
|
+
completed_at: string | null;
|
|
188
|
+
}
|
|
189
|
+
export interface ProjectSnapshot {
|
|
190
|
+
project_id: string;
|
|
191
|
+
runs: ProjectRunRow[];
|
|
192
|
+
}
|
|
193
|
+
/** A `run.summary` frame: one meaningful event of one run, trimmed. */
|
|
194
|
+
export interface ProjectRunSummary {
|
|
195
|
+
run_id: string;
|
|
196
|
+
pos: number;
|
|
197
|
+
type: string;
|
|
198
|
+
at: string;
|
|
199
|
+
data: Record<string, unknown>;
|
|
200
|
+
}
|
|
201
|
+
/** An event as streamed by `GET v1/runs/:id/events`. */
|
|
202
|
+
export interface RunEventFrame {
|
|
203
|
+
event_id: string;
|
|
204
|
+
type: string;
|
|
205
|
+
/** The writer's own number. Not an order. */
|
|
206
|
+
seq: number;
|
|
207
|
+
/** The run's order, assigned by core-api. Resume after this. */
|
|
208
|
+
pos: number;
|
|
209
|
+
data: Record<string, unknown>;
|
|
210
|
+
}
|
|
211
|
+
/** `POST v1/runner/runs/:id/events` reply. */
|
|
212
|
+
export interface RunnerBatchResult {
|
|
213
|
+
accepted: number;
|
|
214
|
+
duplicates: number;
|
|
215
|
+
cursor: number;
|
|
216
|
+
}
|
|
217
|
+
/** `POST v1/runner/runs/:id/heartbeat` reply. */
|
|
218
|
+
export interface HeartbeatResult {
|
|
219
|
+
status: RunStatus | null;
|
|
220
|
+
runner_seq: number;
|
|
221
|
+
command_pos: number;
|
|
222
|
+
}
|
|
223
|
+
/** `POST v1/runner/runs/:id/artifacts/uploads` — where to PUT the bytes. */
|
|
224
|
+
export interface UploadGrant {
|
|
225
|
+
upload_url: string;
|
|
226
|
+
/** Pass back to `registerArtifact`. Always under this run's own prefix. */
|
|
227
|
+
storage_key: string;
|
|
228
|
+
expires_in: number;
|
|
229
|
+
}
|
|
230
|
+
/** What `registerArtifact` sends. */
|
|
231
|
+
export interface RegisterArtifact {
|
|
232
|
+
name: string;
|
|
233
|
+
storage_key: string;
|
|
234
|
+
kind?: ArtifactRef['kind'];
|
|
235
|
+
mime_type?: string;
|
|
236
|
+
size_bytes?: number;
|
|
237
|
+
/** Hex SHA-256 of the bytes; core-api checks it when it can read the file. */
|
|
238
|
+
sha256?: string;
|
|
239
|
+
/** The version this replaces, when another run wrote the current one. */
|
|
240
|
+
supersedes?: string;
|
|
241
|
+
description?: string;
|
|
242
|
+
}
|
|
243
|
+
/** A file in the project store, as a runner sees it. */
|
|
244
|
+
export interface ProjectFile extends ArtifactRef {
|
|
245
|
+
project_id: string;
|
|
246
|
+
writer_run_id: string | null;
|
|
247
|
+
sha256: string | null;
|
|
248
|
+
size_bytes: number | null;
|
|
249
|
+
supersedes_id: string | null;
|
|
250
|
+
created_at: string;
|
|
251
|
+
/** Short-lived; only on `getArtifact`. */
|
|
252
|
+
download_url?: string;
|
|
253
|
+
expires_in?: number;
|
|
254
|
+
}
|
|
255
|
+
/** `POST v1/runner/runs/:id/handoffs` answer. */
|
|
256
|
+
export interface HandoffCreated {
|
|
257
|
+
child_run_id: string;
|
|
258
|
+
depth: number;
|
|
259
|
+
budget_credits: number | null;
|
|
260
|
+
status: RunStatus;
|
|
261
|
+
}
|
|
262
|
+
/** `GET v1/runner/runs/:id/handoffs/:childId`. */
|
|
263
|
+
export interface HandoffStatus {
|
|
264
|
+
child_run_id: string;
|
|
265
|
+
status: RunStatus;
|
|
266
|
+
handoff: Handoff & {
|
|
267
|
+
goal_hash?: string;
|
|
268
|
+
};
|
|
269
|
+
/** Set once the child has ended. */
|
|
270
|
+
result: HandoffResult | null;
|
|
271
|
+
}
|
|
272
|
+
/** One tool a runner may call: a name, what it does, its arguments. Never a secret. */
|
|
273
|
+
export interface RunnerTool {
|
|
274
|
+
/** `request_subjob`, `email_send`, or `composio.<SLUG>`. */
|
|
275
|
+
name: string;
|
|
276
|
+
source: 'workser' | 'composio';
|
|
277
|
+
description: string;
|
|
278
|
+
/** Changes something outside the job: the owner is asked first. */
|
|
279
|
+
side_effect: boolean;
|
|
280
|
+
/** Moves money: say `amount_usd`; over the owner's limit a spend yes is needed. */
|
|
281
|
+
spends: boolean;
|
|
282
|
+
input_schema: Record<string, unknown>;
|
|
283
|
+
}
|
|
284
|
+
export interface RunnerToolList {
|
|
285
|
+
tools: RunnerTool[];
|
|
286
|
+
/** Dollars above which a spending call needs the owner's yes. */
|
|
287
|
+
ask_before_paying_usd: number;
|
|
288
|
+
}
|
|
289
|
+
export interface ToolCall {
|
|
290
|
+
arguments?: Record<string, unknown>;
|
|
291
|
+
/** The approved `permission` ask for exactly this call. */
|
|
292
|
+
approval_id?: string;
|
|
293
|
+
/** Spending tools: how much. */
|
|
294
|
+
amount_usd?: number;
|
|
295
|
+
/** The approved `spend` ask, when the amount is over the limit. */
|
|
296
|
+
spend_approval_id?: string;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* `ok` with the tool's answer, or `approval_required`: wait for the owner's
|
|
300
|
+
* answer (an `approval` command), then call again with the id in
|
|
301
|
+
* `approval_id` (permission) or `spend_approval_id` (spend).
|
|
302
|
+
*/
|
|
303
|
+
export interface ToolCallResult {
|
|
304
|
+
status: 'ok' | 'approval_required';
|
|
305
|
+
result?: unknown;
|
|
306
|
+
approval_id?: string;
|
|
307
|
+
kind?: 'permission' | 'spend';
|
|
308
|
+
message?: string;
|
|
309
|
+
/** The result holds an outside message (a sign-in code): do not keep it in the job record or saved state. */
|
|
310
|
+
private_output?: boolean;
|
|
311
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shapes a run is made of, as core-api sends and accepts them.
|
|
3
|
+
*
|
|
4
|
+
* One shape per thing, with optional fields, rather than tagged unions:
|
|
5
|
+
* core-api builds without strictNullChecks, where a union doesn't narrow, and
|
|
6
|
+
* the same types are used on both sides of the wire.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/contract/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { RunBrief, RunnerCommand } from '../contract/types.js';
|
|
2
|
+
import type { Place } from '../place/place.js';
|
|
3
|
+
/**
|
|
4
|
+
* The thing that thinks: the agent loop (our OpenCode fork first). It gets the
|
|
5
|
+
* brief and a Place, and reports everything it does through `emit`, which the
|
|
6
|
+
* runner turns into run events.
|
|
7
|
+
*/
|
|
8
|
+
export interface Engine {
|
|
9
|
+
/** A short name for `run.started`, e.g. `opencode`. */
|
|
10
|
+
readonly name: string;
|
|
11
|
+
start(brief: RunBrief, place: Place, emit: EngineEmit): Promise<EngineSession>;
|
|
12
|
+
/** Pick up a session saved by `EngineSession.save()` after a pause or restart. */
|
|
13
|
+
resume(state: EngineSessionState, place: Place, emit: EngineEmit): Promise<EngineSession>;
|
|
14
|
+
}
|
|
15
|
+
/** One running job. */
|
|
16
|
+
export interface EngineSession {
|
|
17
|
+
/** A new message from the owner, mid-run. */
|
|
18
|
+
send(message: string): Promise<void>;
|
|
19
|
+
/** A command from core-api: an answer, a decision, pause, resume. Stop comes through `stop`. */
|
|
20
|
+
answer(command: RunnerCommand): Promise<void>;
|
|
21
|
+
/** Stop as soon as it is safe; the promise ends when it has. */
|
|
22
|
+
stop(reason?: string): Promise<void>;
|
|
23
|
+
/** Everything needed to resume later. Small; large state lives on the Place. */
|
|
24
|
+
save(): Promise<EngineSessionState>;
|
|
25
|
+
/** Ends with how the job ended. */
|
|
26
|
+
readonly done: Promise<EngineOutcome>;
|
|
27
|
+
}
|
|
28
|
+
export type EngineEmit = (type: string, data?: Record<string, unknown>) => void;
|
|
29
|
+
export interface EngineSessionState {
|
|
30
|
+
engine: string;
|
|
31
|
+
version: number;
|
|
32
|
+
data: Record<string, unknown>;
|
|
33
|
+
}
|
|
34
|
+
export interface EngineOutcome {
|
|
35
|
+
status: 'completed' | 'failed' | 'cancelled';
|
|
36
|
+
summary?: string;
|
|
37
|
+
error_kind?: string;
|
|
38
|
+
message?: string;
|
|
39
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/engine/engine.ts"],"names":[],"mappings":""}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @workser/runtime — the run contract every Workser runner, screen and service
|
|
3
|
+
* speaks. See README.md for runner protocol v1.
|
|
4
|
+
*/
|
|
5
|
+
export * from './contract/index.js';
|
|
6
|
+
export * from './runner/index.js';
|
|
7
|
+
export * from './client/index.js';
|
|
8
|
+
export type * from './place/place.js';
|
|
9
|
+
export type * from './engine/engine.js';
|
|
10
|
+
export { SseParser, connectSse, type SseMessage } from './internal/sse.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @workser/runtime — the run contract every Workser runner, screen and service
|
|
3
|
+
* speaks. See README.md for runner protocol v1.
|
|
4
|
+
*/
|
|
5
|
+
export * from './contract/index.js';
|
|
6
|
+
export * from './runner/index.js';
|
|
7
|
+
export * from './client/index.js';
|
|
8
|
+
export { SseParser, connectSse } from './internal/sse.js';
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAGlC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAmB,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** A push-to-pull bridge: callbacks push, an async iterator pulls. */
|
|
2
|
+
export declare class AsyncQueue<T> implements AsyncIterableIterator<T> {
|
|
3
|
+
private readonly onReturn;
|
|
4
|
+
private readonly items;
|
|
5
|
+
private readonly waiters;
|
|
6
|
+
private ended;
|
|
7
|
+
private failure;
|
|
8
|
+
constructor(onReturn?: () => void);
|
|
9
|
+
push(item: T): void;
|
|
10
|
+
end(error?: unknown): void;
|
|
11
|
+
next(): Promise<IteratorResult<T>>;
|
|
12
|
+
return(): Promise<IteratorResult<T>>;
|
|
13
|
+
[Symbol.asyncIterator](): AsyncIterableIterator<T>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/** A push-to-pull bridge: callbacks push, an async iterator pulls. */
|
|
2
|
+
export class AsyncQueue {
|
|
3
|
+
onReturn;
|
|
4
|
+
items = [];
|
|
5
|
+
waiters = [];
|
|
6
|
+
ended = false;
|
|
7
|
+
failure = null;
|
|
8
|
+
constructor(onReturn = () => undefined) {
|
|
9
|
+
this.onReturn = onReturn;
|
|
10
|
+
}
|
|
11
|
+
push(item) {
|
|
12
|
+
if (this.ended)
|
|
13
|
+
return;
|
|
14
|
+
const waiter = this.waiters.shift();
|
|
15
|
+
if (waiter)
|
|
16
|
+
waiter({ value: item, done: false });
|
|
17
|
+
else
|
|
18
|
+
this.items.push(item);
|
|
19
|
+
}
|
|
20
|
+
end(error) {
|
|
21
|
+
if (this.ended)
|
|
22
|
+
return;
|
|
23
|
+
this.ended = true;
|
|
24
|
+
if (error)
|
|
25
|
+
this.failure = error;
|
|
26
|
+
for (const waiter of this.waiters.splice(0)) {
|
|
27
|
+
waiter({ value: undefined, done: true });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
next() {
|
|
31
|
+
const item = this.items.shift();
|
|
32
|
+
if (item !== undefined)
|
|
33
|
+
return Promise.resolve({ value: item, done: false });
|
|
34
|
+
if (this.ended) {
|
|
35
|
+
if (this.failure) {
|
|
36
|
+
const error = this.failure;
|
|
37
|
+
this.failure = null;
|
|
38
|
+
return Promise.reject(error);
|
|
39
|
+
}
|
|
40
|
+
return Promise.resolve({ value: undefined, done: true });
|
|
41
|
+
}
|
|
42
|
+
return new Promise((resolve) => this.waiters.push(resolve));
|
|
43
|
+
}
|
|
44
|
+
return() {
|
|
45
|
+
this.end();
|
|
46
|
+
this.onReturn();
|
|
47
|
+
return Promise.resolve({ value: undefined, done: true });
|
|
48
|
+
}
|
|
49
|
+
[Symbol.asyncIterator]() {
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=async-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async-queue.js","sourceRoot":"","sources":["../../src/internal/async-queue.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,MAAM,OAAO,UAAU;IAMQ;IALZ,KAAK,GAAQ,EAAE,CAAC;IAChB,OAAO,GAA+C,EAAE,CAAC;IAClE,KAAK,GAAG,KAAK,CAAC;IACd,OAAO,GAAY,IAAI,CAAC;IAEhC,YAA6B,WAAuB,GAAG,EAAE,CAAC,SAAS;QAAtC,aAAQ,GAAR,QAAQ,CAA8B;IAAG,CAAC;IAEvE,IAAI,CAAC,IAAO;QACV,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO;QACvB,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACpC,IAAI,MAAM;YAAE,MAAM,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;;YAC5C,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IAED,GAAG,CAAC,KAAe;QACjB,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO;QACvB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,KAAK;YAAE,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QAChC,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5C,MAAM,CAAC,EAAE,KAAK,EAAE,SAAkB,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;IAED,IAAI;QACF,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QAChC,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC7E,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBACjB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC;gBAC3B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;gBACpB,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC/B,CAAC;YACD,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,SAAkB,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACpE,CAAC;QACD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IAC9D,CAAC;IAED,MAAM;QACJ,IAAI,CAAC,GAAG,EAAE,CAAC;QACX,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChB,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,SAAkB,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IACpE,CAAC;IAED,CAAC,MAAM,CAAC,aAAa,CAAC;QACpB,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-Sent Events over `fetch`, for Node 18+ and browsers alike.
|
|
3
|
+
*
|
|
4
|
+
* Not `EventSource`: it can't send an `Authorization` header (a runner's token
|
|
5
|
+
* has to go somewhere), it doesn't exist in Node, and its reconnect can't be
|
|
6
|
+
* told to resume from a cursor we choose. This follows the HTML spec's parsing
|
|
7
|
+
* rules (line endings, `data` joining, comments, `id`, `retry`).
|
|
8
|
+
*/
|
|
9
|
+
export interface SseMessage {
|
|
10
|
+
/** The `event:` field, `message` when absent. */
|
|
11
|
+
event: string;
|
|
12
|
+
data: string;
|
|
13
|
+
/** The last `id:` seen on this connection, carried forward per the spec. */
|
|
14
|
+
id: string;
|
|
15
|
+
/** A `retry:` the server asked for, in ms, when this message carried one. */
|
|
16
|
+
retry?: number;
|
|
17
|
+
}
|
|
18
|
+
/** Incremental parser: feed it text chunks, get whole messages back. */
|
|
19
|
+
export declare class SseParser {
|
|
20
|
+
private buffer;
|
|
21
|
+
private data;
|
|
22
|
+
private event;
|
|
23
|
+
private lastId;
|
|
24
|
+
private retry;
|
|
25
|
+
private sawCr;
|
|
26
|
+
/** Parse one chunk. A message split across chunks is held until complete. */
|
|
27
|
+
push(chunk: string): SseMessage[];
|
|
28
|
+
private line;
|
|
29
|
+
private dispatch;
|
|
30
|
+
}
|
|
31
|
+
export type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
|
|
32
|
+
export interface SseConnectOptions {
|
|
33
|
+
fetch: FetchLike;
|
|
34
|
+
/** Built fresh for every (re)connect, so the cursor can move. */
|
|
35
|
+
url: () => string;
|
|
36
|
+
headers: () => Record<string, string>;
|
|
37
|
+
signal: AbortSignal;
|
|
38
|
+
onMessage: (message: SseMessage) => void;
|
|
39
|
+
/** First wait before reconnecting, doubled per failure up to `maxDelayMs`. */
|
|
40
|
+
baseDelayMs?: number;
|
|
41
|
+
maxDelayMs?: number;
|
|
42
|
+
/** Called on a failed connect or a dropped stream. Return false to stop. */
|
|
43
|
+
onError?: (error: unknown) => boolean | void;
|
|
44
|
+
/** A response status that means "stop, don't retry" (e.g. 401, 403, 404). */
|
|
45
|
+
fatalStatuses?: number[];
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Keep an SSE connection open until `signal` aborts: connect, parse, and on
|
|
49
|
+
* any drop reconnect with backoff. The caller's `url()` decides where to
|
|
50
|
+
* resume from, so reconnects continue rather than replay.
|
|
51
|
+
*/
|
|
52
|
+
export declare function connectSse(options: SseConnectOptions): Promise<void>;
|
|
53
|
+
export declare class SseHttpError extends Error {
|
|
54
|
+
readonly status: number;
|
|
55
|
+
constructor(status: number);
|
|
56
|
+
}
|
|
57
|
+
export declare function sleep(ms: number, signal?: AbortSignal): Promise<void>;
|