@zetaloop/chappie 0.2.1 → 0.3.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/README.md +27 -7
- package/docs/tools.md +55 -74
- package/package.json +3 -1
- package/src/broker.ts +193 -35
- package/src/config.ts +21 -0
- package/src/delivery.ts +25 -56
- package/src/index.ts +15 -10
- package/src/instructions.md +7 -5
- package/src/ipc.ts +142 -40
- package/src/provider.ts +3 -5
- package/src/question.html +384 -0
- package/src/questions.ts +95 -0
- package/src/server.ts +206 -62
- package/src/session.ts +111 -41
- package/src/state.ts +98 -7
- package/src/stdio.ts +11 -2
- package/src/tools.ts +3 -22
- package/src/transfer.ts +3 -3
package/src/config.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import * as z from "zod";
|
|
4
|
+
|
|
5
|
+
const configSchema = z.object({
|
|
6
|
+
ask: z.boolean().optional(),
|
|
7
|
+
connect: z.string().min(1).optional(),
|
|
8
|
+
latestWorkflow: z.boolean().optional(),
|
|
9
|
+
listen: z.union([z.boolean(), z.number().int().min(1).max(65535)]).optional(),
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
export async function readConfig(agentDir: string) {
|
|
13
|
+
let contents: string;
|
|
14
|
+
try {
|
|
15
|
+
contents = await readFile(join(agentDir, "chappie.json"), "utf8");
|
|
16
|
+
} catch (error) {
|
|
17
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return {};
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
return configSchema.parse(JSON.parse(contents));
|
|
21
|
+
}
|
package/src/delivery.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
1
|
import type { ToolResultMessage } from "@earendil-works/pi-ai";
|
|
3
2
|
import {
|
|
4
3
|
contentWithImageReferences,
|
|
@@ -10,48 +9,35 @@ export interface DeliveryRecord {
|
|
|
10
9
|
chatId: string;
|
|
11
10
|
sessionId: string;
|
|
12
11
|
cwd: string;
|
|
13
|
-
sessionFile?: string;
|
|
14
|
-
toolCallIds: string[];
|
|
15
|
-
inlineResults?: ToolResultMessage[];
|
|
16
|
-
error?: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface ResolvedDelivery extends DeliveryRecord {
|
|
20
12
|
toolResults: ToolResultMessage[];
|
|
13
|
+
error?: string;
|
|
21
14
|
}
|
|
22
15
|
|
|
23
|
-
export
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
toolResults = delivery.toolCallIds.flatMap((id) => {
|
|
47
|
-
const result = found.get(id);
|
|
48
|
-
return result ? [result] : [];
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
return { ...delivery, toolResults };
|
|
16
|
+
export function toolResultsContent(
|
|
17
|
+
toolResults: ToolResultMessage[],
|
|
18
|
+
sessionId: string,
|
|
19
|
+
) {
|
|
20
|
+
return toolResults.flatMap((result) => [
|
|
21
|
+
{
|
|
22
|
+
type: "text" as const,
|
|
23
|
+
text: JSON.stringify({
|
|
24
|
+
toolCallId: result.toolCallId,
|
|
25
|
+
toolName: result.toolName,
|
|
26
|
+
isError: result.isError,
|
|
27
|
+
}),
|
|
28
|
+
},
|
|
29
|
+
...contentWithImageReferences(sessionId, result.content),
|
|
30
|
+
...resourceDescriptors(result.details).map((resource) => ({
|
|
31
|
+
type: "resource_link" as const,
|
|
32
|
+
uri: resource.uri,
|
|
33
|
+
name: resource.name,
|
|
34
|
+
mimeType: resource.mimeType,
|
|
35
|
+
size: resource.size,
|
|
36
|
+
})),
|
|
37
|
+
]);
|
|
52
38
|
}
|
|
53
39
|
|
|
54
|
-
export function deliveryContent(deliveries:
|
|
40
|
+
export function deliveryContent(deliveries: DeliveryRecord[]) {
|
|
55
41
|
return deliveries.flatMap((delivery) => [
|
|
56
42
|
{
|
|
57
43
|
type: "text" as const,
|
|
@@ -62,23 +48,6 @@ export function deliveryContent(deliveries: ResolvedDelivery[]) {
|
|
|
62
48
|
error: delivery.error,
|
|
63
49
|
}),
|
|
64
50
|
},
|
|
65
|
-
...delivery.toolResults.
|
|
66
|
-
{
|
|
67
|
-
type: "text" as const,
|
|
68
|
-
text: JSON.stringify({
|
|
69
|
-
toolCallId: result.toolCallId,
|
|
70
|
-
toolName: result.toolName,
|
|
71
|
-
isError: result.isError,
|
|
72
|
-
}),
|
|
73
|
-
},
|
|
74
|
-
...contentWithImageReferences(delivery.sessionId, result.content),
|
|
75
|
-
...resourceDescriptors(result.details).map((resource) => ({
|
|
76
|
-
type: "resource_link" as const,
|
|
77
|
-
uri: resource.uri,
|
|
78
|
-
name: resource.name,
|
|
79
|
-
mimeType: resource.mimeType,
|
|
80
|
-
size: resource.size,
|
|
81
|
-
})),
|
|
82
|
-
]),
|
|
51
|
+
...toolResultsContent(delivery.toolResults, delivery.sessionId),
|
|
83
52
|
]);
|
|
84
53
|
}
|
package/src/index.ts
CHANGED
|
@@ -19,16 +19,21 @@ export default async function chappie(pi: ExtensionAPI): Promise<void> {
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
const agentDir = getAgentDir();
|
|
23
|
+
const [
|
|
24
|
+
{ readConfig },
|
|
25
|
+
{ createChappieProvider },
|
|
26
|
+
{ LocalSession },
|
|
27
|
+
{ transfer },
|
|
28
|
+
] = await Promise.all([
|
|
29
|
+
import("./config.ts"),
|
|
30
|
+
import("./provider.ts"),
|
|
31
|
+
import("./session.ts"),
|
|
32
|
+
import("./transfer.ts"),
|
|
33
|
+
]);
|
|
34
|
+
const config = await readConfig(agentDir);
|
|
35
|
+
const session = new LocalSession(pi, agentDir, config.connect);
|
|
29
36
|
session.install();
|
|
30
37
|
pi.registerTool(transfer);
|
|
31
|
-
pi.registerProvider(
|
|
32
|
-
createChappieProvider((output, context) => session.start(output, context)),
|
|
33
|
-
);
|
|
38
|
+
pi.registerProvider(createChappieProvider((output) => session.start(output)));
|
|
34
39
|
}
|
package/src/instructions.md
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
Chappie connects this ChatGPT conversation to
|
|
1
|
+
Chappie connects this ChatGPT conversation to Pi sessions on one or more devices. Resume work with init using the task's Pi sessionId, including in a new chat or branch. When only a project or session name is known, find its device/cwd/name in sessions, then call init. If the target is absent or ambiguous, ask the user to resolve it. Pi sessions appear while the chappie/chatgpt provider is selected.
|
|
2
2
|
|
|
3
|
-
For
|
|
3
|
+
init sets this chat's default Pi session. For a task without a specified target, omitting sessionId reuses that default or selects the first online session with no saved bindings. These sessions may already contain work. Defaults survive broker restarts, and several chats can share one Pi session. Other tools' sessionId affects only that call.
|
|
4
4
|
|
|
5
5
|
The active model is this existing ChatGPT conversation. A Pi tool that starts another chappie/chatgpt agent has no ChatGPT conversation to attach to and will wait indefinitely. Subagents targeting another configured model keep that provider's normal behavior.
|
|
6
6
|
|
|
7
7
|
Prefer ChatGPT's web search, connectors, and cloud tools for remote research and cloud-side work. Use Chappie for local files, processes, Pi extensions, and Pi user interfaces. Pi project-memory tools operate on their local stores; Pi context-reduction tools do not change this ChatGPT conversation.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Respond promptly to new Pi user input with a substantive reply, interaction, or immediate action that makes the response apparent in Pi before continuing lengthy work. Address inputs received together in one response; an immediate answer or result serves as its own acknowledgment.
|
|
10
10
|
|
|
11
|
-
Use
|
|
11
|
+
Use chat for assistant messages in Pi, including progress, explanations, and results. Use read, bash, edit, write, and transfer directly. init.tools is a Pi tool catalog; tools returns full definitions for call. Invoke Chappie's MCP tools directly. Each call array is one native Pi batch; separate calls are separate Pi turns. For Pi interaction, call an installed interactive tool.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
transfer pairs files from ChatGPT with Pi destination paths in order. Omit files to return resource links for Pi paths or chappie:// image references. Relative paths use the Pi working directory; overwrite: true replaces existing targets. The host may request confirmation when retrieving exported bytes.
|
|
14
|
+
|
|
15
|
+
Tool results identify the executing Pi sessionId and cwd. A shell command can access another directory without changing its Pi session. structuredContent.text includes the complete text, new Pi input, webAnswer, and deferred results; images and resources are native content blocks. Continue from received results rather than repeating work. Host deadlines include queueing and execution; use local persistent processes for longer work.
|
package/src/ipc.ts
CHANGED
|
@@ -20,13 +20,15 @@ import type {
|
|
|
20
20
|
import type { DeliveryRecord } from "./delivery.ts";
|
|
21
21
|
import type { ResourceData } from "./resources.ts";
|
|
22
22
|
|
|
23
|
+
const defaultPort = 24274;
|
|
24
|
+
|
|
23
25
|
export type SessionStatus = "idle" | "ready" | "executing";
|
|
24
26
|
|
|
25
27
|
export interface SessionDescription {
|
|
26
28
|
id: string;
|
|
27
29
|
cwd: string;
|
|
30
|
+
device: string;
|
|
28
31
|
name?: string;
|
|
29
|
-
sessionFile?: string;
|
|
30
32
|
status: SessionStatus;
|
|
31
33
|
}
|
|
32
34
|
|
|
@@ -43,7 +45,11 @@ export interface SessionInput {
|
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
export type SessionResult =
|
|
46
|
-
| {
|
|
48
|
+
| {
|
|
49
|
+
inspection: SessionInspection;
|
|
50
|
+
inputs: SessionInput[];
|
|
51
|
+
globalAgents?: string;
|
|
52
|
+
}
|
|
47
53
|
| { message: AssistantMessage; cwd: string; inputs: SessionInput[] }
|
|
48
54
|
| {
|
|
49
55
|
message: AssistantMessage;
|
|
@@ -63,6 +69,7 @@ export type SessionMessage =
|
|
|
63
69
|
export type BrokerMessage =
|
|
64
70
|
| { type: "synced"; id: number; sessionId: string }
|
|
65
71
|
| { type: "stored"; id: string }
|
|
72
|
+
| { type: "notice"; sessionId: string; message: string }
|
|
66
73
|
| { type: "inspect"; id: number; sessionId: string }
|
|
67
74
|
| {
|
|
68
75
|
type: "chat";
|
|
@@ -78,7 +85,7 @@ export type BrokerMessage =
|
|
|
78
85
|
sessionId: string;
|
|
79
86
|
calls: ToolCall[];
|
|
80
87
|
}
|
|
81
|
-
| { type: "cancel"; id: number; sessionId: string }
|
|
88
|
+
| { type: "cancel"; id: number; sessionId: string; reason: string }
|
|
82
89
|
| { type: "readResource"; id: number; sessionId: string; uri: string }
|
|
83
90
|
| { type: "ackInputs"; sessionId: string; ids: string[] };
|
|
84
91
|
|
|
@@ -172,7 +179,7 @@ export class IpcServer {
|
|
|
172
179
|
peer: JsonLinePeer<SessionMessage, BrokerMessage>,
|
|
173
180
|
) => void;
|
|
174
181
|
readonly #peers = new Set<JsonLinePeer<SessionMessage, BrokerMessage>>();
|
|
175
|
-
#
|
|
182
|
+
readonly #servers = new Set<Server>();
|
|
176
183
|
|
|
177
184
|
constructor(
|
|
178
185
|
agentDir: string,
|
|
@@ -187,10 +194,34 @@ export class IpcServer {
|
|
|
187
194
|
this.#onClose = onClose;
|
|
188
195
|
}
|
|
189
196
|
|
|
190
|
-
async start(): Promise<void> {
|
|
191
|
-
if (this.#
|
|
197
|
+
async start(network: boolean | number = false): Promise<void> {
|
|
198
|
+
if (this.#servers.size > 0) return;
|
|
192
199
|
if (process.platform !== "win32") await prepareUnixSocket(this.#endpoint);
|
|
193
|
-
|
|
200
|
+
try {
|
|
201
|
+
const local = this.#createServer();
|
|
202
|
+
await listenServer(local, this.#endpoint);
|
|
203
|
+
this.#servers.add(local);
|
|
204
|
+
if (!network) return;
|
|
205
|
+
|
|
206
|
+
const remote = this.#createServer();
|
|
207
|
+
await listenServer(remote, network === true ? defaultPort : network);
|
|
208
|
+
this.#servers.add(remote);
|
|
209
|
+
} catch (error) {
|
|
210
|
+
await this.close();
|
|
211
|
+
throw error;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async close(): Promise<void> {
|
|
216
|
+
for (const peer of this.#peers) peer.close();
|
|
217
|
+
this.#peers.clear();
|
|
218
|
+
const servers = [...this.#servers];
|
|
219
|
+
this.#servers.clear();
|
|
220
|
+
await Promise.all(servers.map(closeServer));
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
#createServer(): Server {
|
|
224
|
+
return createServer((socket) => {
|
|
194
225
|
let peer: JsonLinePeer<SessionMessage, BrokerMessage>;
|
|
195
226
|
peer = new JsonLinePeer(
|
|
196
227
|
socket,
|
|
@@ -202,25 +233,6 @@ export class IpcServer {
|
|
|
202
233
|
);
|
|
203
234
|
this.#peers.add(peer);
|
|
204
235
|
});
|
|
205
|
-
await new Promise<void>((resolveListen, rejectListen) => {
|
|
206
|
-
server.once("error", rejectListen);
|
|
207
|
-
server.listen(this.#endpoint, () => {
|
|
208
|
-
server.off("error", rejectListen);
|
|
209
|
-
resolveListen();
|
|
210
|
-
});
|
|
211
|
-
});
|
|
212
|
-
this.#server = server;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
async close(): Promise<void> {
|
|
216
|
-
for (const peer of this.#peers) peer.close();
|
|
217
|
-
this.#peers.clear();
|
|
218
|
-
const server = this.#server;
|
|
219
|
-
this.#server = undefined;
|
|
220
|
-
if (!server) return;
|
|
221
|
-
await new Promise<void>((resolveClose, rejectClose) =>
|
|
222
|
-
server.close((error) => (error ? rejectClose(error) : resolveClose())),
|
|
223
|
-
);
|
|
224
236
|
}
|
|
225
237
|
}
|
|
226
238
|
|
|
@@ -232,14 +244,21 @@ interface ConnectionCallbacks {
|
|
|
232
244
|
|
|
233
245
|
export class IpcClient {
|
|
234
246
|
readonly #endpoint: string;
|
|
247
|
+
readonly #remote: string | undefined;
|
|
235
248
|
readonly #callbacks: ConnectionCallbacks;
|
|
236
249
|
#peer: JsonLinePeer<BrokerMessage, SessionMessage> | undefined;
|
|
237
250
|
#opening: Promise<void> | undefined;
|
|
251
|
+
#controller: AbortController | undefined;
|
|
238
252
|
#retry: NodeJS.Timeout | undefined;
|
|
239
253
|
#closed = false;
|
|
240
254
|
|
|
241
|
-
constructor(
|
|
255
|
+
constructor(
|
|
256
|
+
agentDir: string,
|
|
257
|
+
remote: string | undefined,
|
|
258
|
+
callbacks: ConnectionCallbacks,
|
|
259
|
+
) {
|
|
242
260
|
this.#endpoint = ipcEndpoint(agentDir);
|
|
261
|
+
this.#remote = remote;
|
|
243
262
|
this.#callbacks = callbacks;
|
|
244
263
|
}
|
|
245
264
|
|
|
@@ -258,8 +277,11 @@ export class IpcClient {
|
|
|
258
277
|
if (this.#opening) return this.#opening;
|
|
259
278
|
clearTimeout(this.#retry);
|
|
260
279
|
this.#retry = undefined;
|
|
261
|
-
|
|
280
|
+
const controller = new AbortController();
|
|
281
|
+
this.#controller = controller;
|
|
282
|
+
this.#opening = this.#open(controller.signal).finally(() => {
|
|
262
283
|
this.#opening = undefined;
|
|
284
|
+
if (this.#controller === controller) this.#controller = undefined;
|
|
263
285
|
});
|
|
264
286
|
return this.#opening;
|
|
265
287
|
}
|
|
@@ -275,24 +297,25 @@ export class IpcClient {
|
|
|
275
297
|
this.#closed = true;
|
|
276
298
|
clearTimeout(this.#retry);
|
|
277
299
|
this.#retry = undefined;
|
|
300
|
+
this.#controller?.abort(new Error("Chappie IPC client is closed"));
|
|
301
|
+
this.#controller = undefined;
|
|
278
302
|
this.#peer?.close();
|
|
279
303
|
this.#peer = undefined;
|
|
280
304
|
}
|
|
281
305
|
|
|
282
|
-
async #open(): Promise<void> {
|
|
283
|
-
|
|
284
|
-
let failure = new Error("Chappie
|
|
285
|
-
|
|
286
|
-
socket
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
socket.destroy();
|
|
306
|
+
async #open(signal: AbortSignal): Promise<void> {
|
|
307
|
+
let socket: Socket | undefined;
|
|
308
|
+
let failure = new Error("Chappie disconnected");
|
|
309
|
+
try {
|
|
310
|
+
socket = this.#remote
|
|
311
|
+
? createConnection(networkEndpoint(this.#remote))
|
|
312
|
+
: createConnection(this.#endpoint);
|
|
313
|
+
await connectSocket(socket, signal);
|
|
314
|
+
} catch (error) {
|
|
315
|
+
socket?.destroy();
|
|
293
316
|
this.#scheduleReconnect();
|
|
294
317
|
throw error;
|
|
295
|
-
}
|
|
318
|
+
}
|
|
296
319
|
|
|
297
320
|
let peer: JsonLinePeer<BrokerMessage, SessionMessage>;
|
|
298
321
|
peer = new JsonLinePeer(
|
|
@@ -327,6 +350,85 @@ export class IpcClient {
|
|
|
327
350
|
}
|
|
328
351
|
}
|
|
329
352
|
|
|
353
|
+
function listenServer(
|
|
354
|
+
server: Server,
|
|
355
|
+
endpoint: string | number,
|
|
356
|
+
): Promise<void> {
|
|
357
|
+
return new Promise<void>((resolveListen, rejectListen) => {
|
|
358
|
+
const onError = (error: Error): void => {
|
|
359
|
+
server.off("listening", onListening);
|
|
360
|
+
rejectListen(error);
|
|
361
|
+
};
|
|
362
|
+
const onListening = (): void => {
|
|
363
|
+
server.off("error", onError);
|
|
364
|
+
resolveListen();
|
|
365
|
+
};
|
|
366
|
+
server.once("error", onError);
|
|
367
|
+
server.once("listening", onListening);
|
|
368
|
+
server.listen(endpoint);
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function closeServer(server: Server): Promise<void> {
|
|
373
|
+
return new Promise<void>((resolveClose, rejectClose) =>
|
|
374
|
+
server.close((error) => (error ? rejectClose(error) : resolveClose())),
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
interface NetworkEndpoint {
|
|
379
|
+
host: string;
|
|
380
|
+
port: number;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function networkEndpoint(value: string): NetworkEndpoint {
|
|
384
|
+
const url = new URL(`tcp://${value}`);
|
|
385
|
+
if (url.username || url.password || url.pathname || url.search || url.hash)
|
|
386
|
+
throw new Error(`Invalid Chappie broker address: ${value}`);
|
|
387
|
+
const host = url.hostname.startsWith("[")
|
|
388
|
+
? url.hostname.slice(1, -1)
|
|
389
|
+
: url.hostname;
|
|
390
|
+
if (!host) throw new Error(`Invalid Chappie broker address: ${value}`);
|
|
391
|
+
return { host, port: url.port ? Number(url.port) : defaultPort };
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function connectSocket(socket: Socket, signal: AbortSignal): Promise<void> {
|
|
395
|
+
return new Promise<void>((resolveConnect, rejectConnect) => {
|
|
396
|
+
const cleanup = (): void => {
|
|
397
|
+
signal.removeEventListener("abort", onAbort);
|
|
398
|
+
socket.off("connect", onConnect);
|
|
399
|
+
socket.off("error", onError);
|
|
400
|
+
};
|
|
401
|
+
const onConnect = (): void => {
|
|
402
|
+
cleanup();
|
|
403
|
+
resolveConnect();
|
|
404
|
+
};
|
|
405
|
+
const onError = (error: Error): void => {
|
|
406
|
+
cleanup();
|
|
407
|
+
rejectConnect(error);
|
|
408
|
+
};
|
|
409
|
+
const onAbort = (): void => {
|
|
410
|
+
cleanup();
|
|
411
|
+
socket.destroy();
|
|
412
|
+
rejectConnect(abortError(signal));
|
|
413
|
+
};
|
|
414
|
+
if (signal.aborted) {
|
|
415
|
+
onAbort();
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
419
|
+
socket.once("connect", onConnect);
|
|
420
|
+
socket.once("error", onError);
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function abortError(signal: AbortSignal): Error {
|
|
425
|
+
return signal.reason instanceof Error
|
|
426
|
+
? signal.reason
|
|
427
|
+
: new Error(
|
|
428
|
+
typeof signal.reason === "string" ? signal.reason : "Request cancelled",
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
|
|
330
432
|
async function prepareUnixSocket(endpoint: string): Promise<void> {
|
|
331
433
|
try {
|
|
332
434
|
await lstat(endpoint);
|
package/src/provider.ts
CHANGED
|
@@ -147,19 +147,17 @@ export class ProviderOutput {
|
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
export function createChappieProvider(
|
|
150
|
-
start: (output: ProviderOutput
|
|
150
|
+
start: (output: ProviderOutput) => Promise<void>,
|
|
151
151
|
) {
|
|
152
152
|
const stream = (
|
|
153
153
|
model: Model<Api>,
|
|
154
|
-
|
|
154
|
+
_context: Context,
|
|
155
155
|
options?: StreamOptions,
|
|
156
156
|
) => {
|
|
157
157
|
const output = new ProviderOutput(model, options?.signal);
|
|
158
158
|
if (!output.closed) {
|
|
159
159
|
queueMicrotask(() => {
|
|
160
|
-
void start(output
|
|
161
|
-
output.fail(error),
|
|
162
|
-
);
|
|
160
|
+
void start(output).catch((error: unknown) => output.fail(error));
|
|
163
161
|
});
|
|
164
162
|
}
|
|
165
163
|
return output.stream;
|