@camerontaylor/paseo-client 0.8.0-fork.1 → 0.8.0-fork.3
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/dist/connection/index.d.ts +27 -21
- package/dist/connection/index.js +89 -85
- package/dist/connection/legacy-workspaces.d.ts +19 -0
- package/dist/connection/legacy-workspaces.js +160 -0
- package/dist/connection/legacy.d.ts +31 -0
- package/dist/connection/legacy.js +245 -0
- package/dist/connection/owned.d.ts +36 -0
- package/dist/connection/owned.js +210 -0
- package/dist/daemon-client.d.ts +88 -57
- package/dist/daemon-client.js +259 -392
- package/dist/index.d.ts +39 -10
- package/dist/index.js +254 -125
- package/dist/provider-snapshots/index.d.ts +1 -3
- package/dist/provider-snapshots/index.js +23 -27
- package/dist/terminal-stream-router.d.ts +7 -10
- package/dist/terminal-stream-router.js +41 -88
- package/dist/timeline-subscription/index.d.ts +25 -0
- package/dist/timeline-subscription/index.js +56 -0
- package/package.json +7 -3
- package/dist/creation/index.d.ts +0 -47
- package/dist/creation/index.js +0 -216
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import type { OwnedSubscription } from "./connection/index.js";
|
|
2
|
+
export type { OwnedSubscription, SubscriptionObserver } from "./connection/index.js";
|
|
1
3
|
import type { DaemonClientConfig } from "./daemon-client.js";
|
|
2
4
|
import type { AgentPermissionResponse } from "@camerontaylor/paseo-protocol/agent-types";
|
|
3
|
-
import type { AgentSnapshotPayload,
|
|
5
|
+
import type { AgentSnapshotPayload, CreateAgentRequestMessage, FetchWorkspacesRequestMessage, FetchWorkspacesResponseMessage, GetProvidersSnapshotResponseMessage, ListAvailableProvidersResponse, ListCommandsResponse, ListProviderFeaturesRequestMessage, ListProviderFeaturesResponseMessage, ListProviderModelsResponseMessage, ProjectListRequestMessage, ProjectListResponseMessage, ListProviderModesResponseMessage, MutableDaemonConfig, MutableDaemonConfigPatch, ProviderDiagnosticResponseMessage, ProviderUsageListResponseMessage, ProjectPlacementPayload, WorkspaceProjectDescriptorPayload, RefreshProvidersSnapshotResponseMessage, SendAgentMessageRequest, SessionOutboundMessage, WorkspaceDescriptorPayload, WorkspaceCreateRequest } from "@camerontaylor/paseo-protocol/messages";
|
|
4
6
|
import { DaemonClient } from "./daemon-client.js";
|
|
5
7
|
import { type PaseoTerminalActions, type PaseoWorkspaceTerminalActions } from "./terminals/index.js";
|
|
6
8
|
export type { PaseoTerminal, PaseoTerminalActions, PaseoTerminalHandle, PaseoTerminalCreateOptions, PaseoTerminalListOptions, PaseoTerminalListResult, PaseoTerminalCaptureOptions, PaseoTerminalCaptureResult, PaseoWorkspaceTerminalActions, } from "./terminals/index.js";
|
|
@@ -61,6 +63,7 @@ export type PaseoProjectUpdate = Extract<SessionOutboundMessage, {
|
|
|
61
63
|
}>["payload"];
|
|
62
64
|
export type PaseoProjectUpdateHandler = (update: PaseoProjectUpdate) => void;
|
|
63
65
|
export interface PaseoAgentListResult {
|
|
66
|
+
subscription?: OwnedSubscription<PaseoAgentListResult>;
|
|
64
67
|
requestId: string;
|
|
65
68
|
subscriptionId?: string | null;
|
|
66
69
|
entries: FetchAgentsEntry[];
|
|
@@ -70,6 +73,7 @@ export type PaseoWorkspaceListOptions = Omit<FetchWorkspacesRequestMessage, "typ
|
|
|
70
73
|
requestId?: string;
|
|
71
74
|
};
|
|
72
75
|
export interface PaseoWorkspaceListResult {
|
|
76
|
+
subscription?: OwnedSubscription<PaseoWorkspaceListResult>;
|
|
73
77
|
requestId: string;
|
|
74
78
|
subscriptionId?: string | null;
|
|
75
79
|
entries: PaseoWorkspace[];
|
|
@@ -79,10 +83,8 @@ export interface PaseoWorkspaceOpenOptions {
|
|
|
79
83
|
cwd: string;
|
|
80
84
|
requestId?: string;
|
|
81
85
|
}
|
|
82
|
-
export type PaseoWorkspaceCreateOptions = Omit<WorkspaceCreateRequest, "type" | "requestId"
|
|
86
|
+
export type PaseoWorkspaceCreateOptions = Omit<WorkspaceCreateRequest, "type" | "requestId"> & {
|
|
83
87
|
requestId?: string;
|
|
84
|
-
agent?: Omit<PaseoAgentCreateOptions, "worktree" | "git" | "onEvent" | "idempotencyKey" | "requestId">;
|
|
85
|
-
onEvent?: (snapshot: CreationSnapshot) => void;
|
|
86
88
|
};
|
|
87
89
|
export interface PaseoWorkspaceArchiveResult {
|
|
88
90
|
requestId: string;
|
|
@@ -125,6 +127,12 @@ export interface PaseoProjectActions {
|
|
|
125
127
|
subscribe(handler: PaseoProjectUpdateHandler): () => void;
|
|
126
128
|
}
|
|
127
129
|
export interface PaseoWorkspaceActions {
|
|
130
|
+
list(options: PaseoWorkspaceListOptions & {
|
|
131
|
+
subscribe: {};
|
|
132
|
+
}): Promise<PaseoWorkspaceListResult & {
|
|
133
|
+
subscriptionId: string;
|
|
134
|
+
subscription: OwnedSubscription<PaseoWorkspaceListResult>;
|
|
135
|
+
}>;
|
|
128
136
|
list(options?: PaseoWorkspaceListOptions): Promise<PaseoWorkspaceListResult>;
|
|
129
137
|
ref(workspace: string | PaseoWorkspace): PaseoWorkspaceHandle;
|
|
130
138
|
open(input: string | PaseoWorkspaceOpenOptions, requestId?: string): Promise<PaseoWorkspaceHandle>;
|
|
@@ -152,9 +160,6 @@ export interface PaseoAgentConfig {
|
|
|
152
160
|
mcpServers?: PaseoAgentSessionConfig["mcpServers"];
|
|
153
161
|
}
|
|
154
162
|
export interface PaseoAgentCreateOptions {
|
|
155
|
-
idempotencyKey?: string;
|
|
156
|
-
agentId?: string;
|
|
157
|
-
onEvent?: (snapshot: CreationSnapshot) => void;
|
|
158
163
|
config: PaseoAgentConfig;
|
|
159
164
|
cwd: string;
|
|
160
165
|
parent?: string | PaseoAgentHandle;
|
|
@@ -217,6 +222,18 @@ export type PaseoAgentTimelineEvent = PaseoAgentStream | {
|
|
|
217
222
|
type: "replacement";
|
|
218
223
|
epoch: string;
|
|
219
224
|
};
|
|
225
|
+
} | {
|
|
226
|
+
agentId: string;
|
|
227
|
+
subscriptionId: string;
|
|
228
|
+
event: {
|
|
229
|
+
type: "subscription_restored";
|
|
230
|
+
};
|
|
231
|
+
} | {
|
|
232
|
+
agentId: string;
|
|
233
|
+
event: {
|
|
234
|
+
type: "error";
|
|
235
|
+
error: string;
|
|
236
|
+
};
|
|
220
237
|
};
|
|
221
238
|
export type PaseoAgentTimelineSubscription = ReturnType<DaemonClient["subscribeAgentTimeline"]>;
|
|
222
239
|
export interface PaseoAgentTimelineHandle {
|
|
@@ -231,8 +248,10 @@ export interface PaseoAgentTimelineHandle {
|
|
|
231
248
|
*/
|
|
232
249
|
refetch(options?: PaseoAgentTimelineRefetchOptions): Promise<FetchAgentTimelinePayload>;
|
|
233
250
|
/**
|
|
234
|
-
*
|
|
235
|
-
*
|
|
251
|
+
* Delivers live events only. After reconnect, subscription_restored precedes
|
|
252
|
+
* subsequent updates. History may have been missed; use refetch() to request
|
|
253
|
+
* the range you need. No history is fetched automatically. A replacement event
|
|
254
|
+
* invalidates the previous epoch. Subscription errors release this observation.
|
|
236
255
|
* Await the returned unsubscribe function's `ready` promise before starting
|
|
237
256
|
* work that must be observed. It rejects if establishment fails.
|
|
238
257
|
*/
|
|
@@ -283,6 +302,12 @@ export interface PaseoAgentHandle {
|
|
|
283
302
|
subscribe(handler: (update: PaseoAgentUpdate) => void): () => void;
|
|
284
303
|
}
|
|
285
304
|
export interface PaseoAgentActions {
|
|
305
|
+
list(options: PaseoAgentListOptions & {
|
|
306
|
+
subscribe: {};
|
|
307
|
+
}): Promise<PaseoAgentListResult & {
|
|
308
|
+
subscriptionId: string;
|
|
309
|
+
subscription: OwnedSubscription<PaseoAgentListResult>;
|
|
310
|
+
}>;
|
|
286
311
|
list(options?: PaseoAgentListOptions): Promise<PaseoAgentListResult>;
|
|
287
312
|
ref(agent: string | PaseoAgent): PaseoAgentHandle;
|
|
288
313
|
create(options: PaseoAgentCreateOptions): Promise<PaseoAgentHandle>;
|
|
@@ -365,6 +390,8 @@ export interface PaseoConfigActions {
|
|
|
365
390
|
}>;
|
|
366
391
|
}
|
|
367
392
|
export interface PaseoApi {
|
|
393
|
+
dispose(): Promise<void>;
|
|
394
|
+
observeEvents: DaemonClient["observeEvents"];
|
|
368
395
|
readonly terminals: PaseoTerminalActions;
|
|
369
396
|
readonly workspaces: PaseoWorkspaceActions;
|
|
370
397
|
readonly projects: PaseoProjectActions;
|
|
@@ -379,5 +406,7 @@ export interface PaseoClient extends PaseoApi {
|
|
|
379
406
|
getConnectionState(): ConnectionState;
|
|
380
407
|
}
|
|
381
408
|
export declare function createPaseoClient(config: PaseoClientConfig): PaseoClient;
|
|
382
|
-
export declare function createPaseoApi(daemonClient: DaemonClient
|
|
409
|
+
export declare function createPaseoApi(daemonClient: DaemonClient, scopeOptions?: {
|
|
410
|
+
signal?: AbortSignal;
|
|
411
|
+
}): PaseoApi;
|
|
383
412
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -11,37 +11,75 @@ export function createPaseoClient(config) {
|
|
|
11
11
|
clientId: config.clientId ?? createGeneratedClientId(),
|
|
12
12
|
clientType: "cli",
|
|
13
13
|
});
|
|
14
|
+
const api = createPaseoApi(daemonClient);
|
|
14
15
|
return {
|
|
15
|
-
...
|
|
16
|
+
...api,
|
|
16
17
|
connect: () => daemonClient.connect(),
|
|
17
|
-
close: () =>
|
|
18
|
+
close: async () => {
|
|
19
|
+
try {
|
|
20
|
+
await api.dispose();
|
|
21
|
+
}
|
|
22
|
+
finally {
|
|
23
|
+
await daemonClient.close();
|
|
24
|
+
}
|
|
25
|
+
},
|
|
18
26
|
ensureConnected: () => daemonClient.ensureConnected(),
|
|
19
27
|
getConnectionState: () => daemonClient.getConnectionState(),
|
|
20
28
|
};
|
|
21
29
|
}
|
|
22
|
-
function
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
...(prompt !== undefined ? { initialPrompt: prompt } : {}),
|
|
30
|
+
export function createPaseoApi(daemonClient, scopeOptions) {
|
|
31
|
+
const handles = new Set();
|
|
32
|
+
const agentListeners = new Set();
|
|
33
|
+
const workspaceListeners = new Set();
|
|
34
|
+
const lifetime = new AbortController();
|
|
35
|
+
const own = (create) => {
|
|
36
|
+
if (lifetime.signal.aborted)
|
|
37
|
+
throw new Error("Paseo API is disposed");
|
|
38
|
+
const handle = create();
|
|
39
|
+
handles.add(handle);
|
|
40
|
+
const release = handle.release.bind(handle);
|
|
41
|
+
handle.release = async () => {
|
|
42
|
+
await release();
|
|
43
|
+
handles.delete(handle);
|
|
44
|
+
};
|
|
45
|
+
return handle;
|
|
39
46
|
};
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
47
|
+
const listenAgents = (handler) => {
|
|
48
|
+
if (lifetime.signal.aborted)
|
|
49
|
+
throw new Error("Paseo API is disposed");
|
|
50
|
+
agentListeners.add(handler);
|
|
51
|
+
return () => {
|
|
52
|
+
agentListeners.delete(handler);
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
const listenWorkspaces = (handler) => {
|
|
56
|
+
if (lifetime.signal.aborted)
|
|
57
|
+
throw new Error("Paseo API is disposed");
|
|
58
|
+
workspaceListeners.add(handler);
|
|
59
|
+
return () => {
|
|
60
|
+
workspaceListeners.delete(handler);
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
const createAgentHandle = createAgentHandleFactory(daemonClient, listenAgents, (agentId, handler) => own(() => daemonClient.subscribeAgentTimeline(agentId, handler)));
|
|
43
64
|
const createAgent = async (options, placement) => {
|
|
44
|
-
const
|
|
65
|
+
const { config: agentConfig, cwd, parent, title, prompt, ...requestOptions } = options;
|
|
66
|
+
const { provider: providerModel, options: providerOptions, ...runtimeConfig } = agentConfig;
|
|
67
|
+
const { provider, model } = parseProviderModel(providerModel);
|
|
68
|
+
const effectiveCwd = placement?.cwd ?? cwd;
|
|
69
|
+
const agent = await daemonClient.createAgent({
|
|
70
|
+
...requestOptions,
|
|
71
|
+
config: {
|
|
72
|
+
...runtimeConfig,
|
|
73
|
+
provider,
|
|
74
|
+
model,
|
|
75
|
+
cwd: effectiveCwd,
|
|
76
|
+
...(title !== undefined ? { title } : {}),
|
|
77
|
+
...(providerOptions !== undefined ? { providerOptions } : {}),
|
|
78
|
+
},
|
|
79
|
+
...(placement ? { workspaceId: placement.workspaceId } : {}),
|
|
80
|
+
...(parent ? { callerAgentId: resolveAgentId(parent) } : {}),
|
|
81
|
+
...(prompt !== undefined ? { initialPrompt: prompt } : {}),
|
|
82
|
+
});
|
|
45
83
|
return createAgentHandle(agent);
|
|
46
84
|
};
|
|
47
85
|
const terminals = createTerminalActions(daemonClient, async (workspaceId) => {
|
|
@@ -51,38 +89,105 @@ export function createPaseoApi(daemonClient) {
|
|
|
51
89
|
}
|
|
52
90
|
return workspace.workspaceDirectory;
|
|
53
91
|
});
|
|
54
|
-
const createWorkspaceHandle = createWorkspaceHandleFactory(daemonClient, createAgent, terminals);
|
|
92
|
+
const createWorkspaceHandle = createWorkspaceHandleFactory(daemonClient, createAgent, terminals, listenWorkspaces);
|
|
93
|
+
let disposal = null;
|
|
94
|
+
const dispose = () => {
|
|
95
|
+
if (disposal)
|
|
96
|
+
return disposal;
|
|
97
|
+
lifetime.abort();
|
|
98
|
+
scopeOptions?.signal?.removeEventListener("abort", abort);
|
|
99
|
+
agentListeners.clear();
|
|
100
|
+
workspaceListeners.clear();
|
|
101
|
+
disposal = Promise.allSettled([...handles].map((handle) => handle.release())).then((results) => {
|
|
102
|
+
handles.clear();
|
|
103
|
+
const failures = results.flatMap((result) => result.status === "rejected" ? [result.reason] : []);
|
|
104
|
+
if (failures.length)
|
|
105
|
+
throw new AggregateError(failures, "Failed to release API subscriptions");
|
|
106
|
+
return undefined;
|
|
107
|
+
});
|
|
108
|
+
return disposal;
|
|
109
|
+
};
|
|
110
|
+
const abort = () => {
|
|
111
|
+
void dispose().catch((error) => console.error("API subscription cleanup failed", error));
|
|
112
|
+
};
|
|
113
|
+
if (scopeOptions?.signal?.aborted)
|
|
114
|
+
abort();
|
|
115
|
+
else
|
|
116
|
+
scopeOptions?.signal?.addEventListener("abort", abort, { once: true });
|
|
117
|
+
const observeEvents = (events, options) => own(() => daemonClient.observeEvents(events, options));
|
|
118
|
+
const subscribeEvent = (event, update) => {
|
|
119
|
+
const observation = observeEvents([event]);
|
|
120
|
+
observation.subscribe({ snapshot: () => { }, update });
|
|
121
|
+
return () => {
|
|
122
|
+
void observation
|
|
123
|
+
.release()
|
|
124
|
+
.catch((error) => console.error("Event subscription cleanup failed", error));
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
async function listWorkspaces(options) {
|
|
128
|
+
if (!options?.subscribe)
|
|
129
|
+
return daemonClient.fetchWorkspaces(options);
|
|
130
|
+
if (options.subscribe.subscriptionId !== undefined)
|
|
131
|
+
throw new Error("Subscription IDs are assigned by the host");
|
|
132
|
+
const subscription = own(() => daemonClient.observeWorkspaces(options));
|
|
133
|
+
subscription.subscribe({
|
|
134
|
+
snapshot: () => { },
|
|
135
|
+
update: (message) => {
|
|
136
|
+
if (message.type === "workspace_update")
|
|
137
|
+
for (const listener of workspaceListeners)
|
|
138
|
+
listener(message.payload);
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
return { ...(await subscription.ready), subscription };
|
|
142
|
+
}
|
|
143
|
+
async function listAgents(options) {
|
|
144
|
+
if (!options?.subscribe)
|
|
145
|
+
return daemonClient.fetchAgents(options);
|
|
146
|
+
if (options.subscribe.subscriptionId !== undefined)
|
|
147
|
+
throw new Error("Subscription IDs are assigned by the host");
|
|
148
|
+
const subscription = own(() => daemonClient.observeAgents(options));
|
|
149
|
+
subscription.subscribe({
|
|
150
|
+
snapshot: () => { },
|
|
151
|
+
update: (message) => {
|
|
152
|
+
if (message.type === "agent_update")
|
|
153
|
+
for (const listener of agentListeners)
|
|
154
|
+
listener(message.payload);
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
return { ...(await subscription.ready), subscription };
|
|
158
|
+
}
|
|
55
159
|
return {
|
|
160
|
+
dispose,
|
|
161
|
+
observeEvents,
|
|
56
162
|
terminals,
|
|
57
163
|
projects: {
|
|
58
164
|
list: (options) => daemonClient.listProjects(options),
|
|
59
|
-
subscribe: (handler) =>
|
|
60
|
-
|
|
61
|
-
|
|
165
|
+
subscribe: (handler) => {
|
|
166
|
+
return subscribeEvent("project.update", (message) => {
|
|
167
|
+
if (message.type === "project.update")
|
|
168
|
+
handler(message.payload);
|
|
169
|
+
});
|
|
170
|
+
},
|
|
62
171
|
},
|
|
63
172
|
workspaces: {
|
|
64
|
-
list:
|
|
173
|
+
list: listWorkspaces,
|
|
65
174
|
ref: (workspace) => createWorkspaceHandle(workspace),
|
|
66
175
|
open: (input, requestId) => openWorkspace(daemonClient, createWorkspaceHandle, input, requestId),
|
|
67
|
-
create: async ({ requestId,
|
|
68
|
-
const result = await daemonClient.createWorkspace(
|
|
176
|
+
create: async ({ requestId, ...options }) => {
|
|
177
|
+
const result = await daemonClient.createWorkspace(options, requestId);
|
|
69
178
|
if (result.error || !result.workspace) {
|
|
70
179
|
throw new Error(result.error ?? "The daemon did not create a workspace");
|
|
71
180
|
}
|
|
72
181
|
return createWorkspaceHandle(result.workspace);
|
|
73
182
|
},
|
|
74
183
|
archive: (workspace, requestId) => daemonClient.archiveWorkspace(resolveWorkspaceId(workspace), requestId),
|
|
75
|
-
subscribe:
|
|
76
|
-
handler(message.payload);
|
|
77
|
-
}),
|
|
184
|
+
subscribe: listenWorkspaces,
|
|
78
185
|
},
|
|
79
186
|
agents: {
|
|
80
|
-
list:
|
|
187
|
+
list: listAgents,
|
|
81
188
|
ref: (agent) => createAgentHandle(agent),
|
|
82
189
|
create: (options) => createAgent(options),
|
|
83
|
-
subscribe:
|
|
84
|
-
handler(message.payload);
|
|
85
|
-
}),
|
|
190
|
+
subscribe: listenAgents,
|
|
86
191
|
},
|
|
87
192
|
providers: {
|
|
88
193
|
listModels: (provider, options) => daemonClient.listProviderModels(provider, options),
|
|
@@ -93,13 +198,16 @@ export function createPaseoApi(daemonClient) {
|
|
|
93
198
|
},
|
|
94
199
|
listAvailable: (options) => daemonClient.listAvailableProviders(options),
|
|
95
200
|
snapshot: (options) => daemonClient.getProvidersSnapshot(options),
|
|
96
|
-
waitForReady: (options) => waitForProvidersReady(daemonClient, options),
|
|
201
|
+
waitForReady: (options) => waitForProvidersReady(daemonClient, observeEvents(["providers_snapshot_update"]), lifetime.signal, options),
|
|
97
202
|
refresh: (options) => daemonClient.refreshProvidersSnapshot(options),
|
|
98
203
|
diagnostic: (provider, options) => daemonClient.getProviderDiagnostic(provider, options),
|
|
99
204
|
listUsage: (options) => listProviderUsage(daemonClient, options),
|
|
100
|
-
subscribe: (handler) =>
|
|
101
|
-
|
|
102
|
-
|
|
205
|
+
subscribe: (handler) => {
|
|
206
|
+
return subscribeEvent("providers_snapshot_update", (message) => {
|
|
207
|
+
if (message.type === "providers_snapshot_update")
|
|
208
|
+
handler(message.payload);
|
|
209
|
+
});
|
|
210
|
+
},
|
|
103
211
|
},
|
|
104
212
|
config: {
|
|
105
213
|
get: (requestId) => daemonClient.getDaemonConfig(requestId),
|
|
@@ -107,7 +215,7 @@ export function createPaseoApi(daemonClient) {
|
|
|
107
215
|
},
|
|
108
216
|
};
|
|
109
217
|
}
|
|
110
|
-
function createWorkspaceHandleFactory(daemonClient, createAgent, terminals) {
|
|
218
|
+
function createWorkspaceHandleFactory(daemonClient, createAgent, terminals, listen) {
|
|
111
219
|
return (workspace) => {
|
|
112
220
|
const id = typeof workspace === "string" ? workspace : workspace.id;
|
|
113
221
|
let current = typeof workspace === "string" ? null : workspace;
|
|
@@ -167,21 +275,19 @@ function createWorkspaceHandleFactory(daemonClient, createAgent, terminals) {
|
|
|
167
275
|
}
|
|
168
276
|
return result;
|
|
169
277
|
},
|
|
170
|
-
subscribe: (handler) =>
|
|
171
|
-
const update = message.payload;
|
|
278
|
+
subscribe: (handler) => listen((update) => {
|
|
172
279
|
if (update.kind === "upsert" && update.workspace.id === id) {
|
|
173
280
|
current = update.workspace;
|
|
174
281
|
handler(update);
|
|
175
282
|
}
|
|
176
283
|
if (update.kind === "remove" && update.id === id) {
|
|
177
|
-
current = null;
|
|
178
284
|
handler(update);
|
|
179
285
|
}
|
|
180
286
|
}),
|
|
181
287
|
};
|
|
182
288
|
};
|
|
183
289
|
}
|
|
184
|
-
function createAgentHandleFactory(daemonClient) {
|
|
290
|
+
function createAgentHandleFactory(daemonClient, listen, subscribeTimeline) {
|
|
185
291
|
return (agent) => {
|
|
186
292
|
const id = typeof agent === "string" ? agent : agent.id;
|
|
187
293
|
let current = typeof agent === "string" ? null : agent;
|
|
@@ -196,13 +302,27 @@ function createAgentHandleFactory(daemonClient) {
|
|
|
196
302
|
}
|
|
197
303
|
return result;
|
|
198
304
|
},
|
|
199
|
-
subscribe: (handler) =>
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
305
|
+
subscribe: (handler) => subscribeTimeline(id, (message) => {
|
|
306
|
+
switch (message.type) {
|
|
307
|
+
case "agent_stream":
|
|
308
|
+
return handler(message.payload);
|
|
309
|
+
case "agent.timeline.subscription_restored":
|
|
310
|
+
return handler({
|
|
311
|
+
agentId: id,
|
|
312
|
+
subscriptionId: message.payload.subscriptionId,
|
|
313
|
+
event: { type: "subscription_restored" },
|
|
314
|
+
});
|
|
315
|
+
case "agent.timeline.error":
|
|
316
|
+
return handler({
|
|
317
|
+
agentId: id,
|
|
318
|
+
event: { type: "error", error: message.payload.error },
|
|
319
|
+
});
|
|
320
|
+
case "agent.timeline.replacement":
|
|
321
|
+
return handler({
|
|
322
|
+
agentId: id,
|
|
323
|
+
event: { type: "replacement", epoch: message.payload.epoch },
|
|
324
|
+
});
|
|
325
|
+
}
|
|
206
326
|
}),
|
|
207
327
|
},
|
|
208
328
|
get workspaceId() {
|
|
@@ -280,14 +400,12 @@ function createAgentHandleFactory(daemonClient) {
|
|
|
280
400
|
detach: async () => {
|
|
281
401
|
await daemonClient.detachAgent(id);
|
|
282
402
|
},
|
|
283
|
-
subscribe: (handler) =>
|
|
284
|
-
const update = message.payload;
|
|
403
|
+
subscribe: (handler) => listen((update) => {
|
|
285
404
|
if (update.kind === "upsert" && update.agent.id === id) {
|
|
286
405
|
current = update.agent;
|
|
287
406
|
handler(update);
|
|
288
407
|
}
|
|
289
408
|
if (update.kind === "remove" && update.agentId === id) {
|
|
290
|
-
current = null;
|
|
291
409
|
handler(update);
|
|
292
410
|
}
|
|
293
411
|
}),
|
|
@@ -326,77 +444,88 @@ function listProviderUsage(daemonClient, options) {
|
|
|
326
444
|
}
|
|
327
445
|
return daemonClient.listProviderUsage(options);
|
|
328
446
|
}
|
|
329
|
-
function waitForProvidersReady(daemonClient, options = {}) {
|
|
330
|
-
// COMPAT(providersSnapshotCwd): added in v0.3.2, remove gate after 2027-02-10.
|
|
331
|
-
if (daemonClient.getLastServerInfoMessage()?.features?.providersSnapshotCwd !== true) {
|
|
332
|
-
return Promise.reject(new Error("Update the host to wait for provider discovery."));
|
|
333
|
-
}
|
|
447
|
+
async function waitForProvidersReady(daemonClient, observation, signal, options = {}) {
|
|
334
448
|
const { timeoutMs = 60000, ...snapshotOptions } = options;
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
449
|
+
try {
|
|
450
|
+
await observation.ready;
|
|
451
|
+
signal.throwIfAborted();
|
|
452
|
+
return await new Promise((resolve, reject) => {
|
|
453
|
+
let settled = false;
|
|
454
|
+
let requestId = null;
|
|
455
|
+
let snapshotCwd;
|
|
456
|
+
const pendingUpdates = new Map();
|
|
457
|
+
let latestEntries = [];
|
|
458
|
+
const cleanup = () => {
|
|
459
|
+
clearTimeout(timeout);
|
|
460
|
+
unsubscribe();
|
|
461
|
+
signal.removeEventListener("abort", abort);
|
|
462
|
+
};
|
|
463
|
+
const finish = (snapshot) => {
|
|
464
|
+
if (settled)
|
|
465
|
+
return;
|
|
466
|
+
settled = true;
|
|
467
|
+
cleanup();
|
|
468
|
+
resolve(snapshot);
|
|
469
|
+
};
|
|
470
|
+
const fail = (error) => {
|
|
471
|
+
if (settled)
|
|
472
|
+
return;
|
|
473
|
+
settled = true;
|
|
474
|
+
cleanup();
|
|
475
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
476
|
+
};
|
|
477
|
+
const updateMatches = (update) => update.cwd === snapshotCwd;
|
|
478
|
+
const unsubscribe = observation.subscribe({
|
|
479
|
+
snapshot: () => { },
|
|
480
|
+
update: (message) => {
|
|
481
|
+
if (message.type !== "providers_snapshot_update")
|
|
482
|
+
return;
|
|
483
|
+
const update = message.payload;
|
|
484
|
+
if (!requestId) {
|
|
485
|
+
pendingUpdates.set(update.cwd, update);
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
if (!updateMatches(update))
|
|
489
|
+
return;
|
|
490
|
+
latestEntries = update.entries;
|
|
491
|
+
if (update.entries.some((entry) => entry.status === "loading"))
|
|
492
|
+
return;
|
|
493
|
+
finish({ ...update, requestId });
|
|
494
|
+
},
|
|
495
|
+
});
|
|
496
|
+
const abort = () => fail(new Error("Paseo API is disposed"));
|
|
497
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
498
|
+
const timeout = setTimeout(() => {
|
|
499
|
+
const loading = latestEntries
|
|
500
|
+
.filter((entry) => entry.status === "loading")
|
|
501
|
+
.map((entry) => entry.provider)
|
|
502
|
+
.join(", ");
|
|
503
|
+
fail(new Error(loading
|
|
504
|
+
? `Timed out waiting for providers: ${loading}`
|
|
505
|
+
: "Timed out waiting for provider discovery"));
|
|
506
|
+
}, timeoutMs);
|
|
507
|
+
void daemonClient
|
|
508
|
+
.getProvidersSnapshot(snapshotOptions)
|
|
509
|
+
.then((snapshot) => {
|
|
510
|
+
requestId = snapshot.requestId;
|
|
511
|
+
snapshotCwd = snapshot.cwd;
|
|
512
|
+
latestEntries = snapshot.entries;
|
|
513
|
+
if (!snapshot.entries.some((entry) => entry.status === "loading")) {
|
|
514
|
+
finish(snapshot);
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
const pendingUpdate = pendingUpdates.get(snapshotCwd);
|
|
518
|
+
if (pendingUpdate && !pendingUpdate.entries.some((entry) => entry.status === "loading")) {
|
|
519
|
+
finish({ ...pendingUpdate, requestId });
|
|
520
|
+
}
|
|
521
|
+
return undefined;
|
|
522
|
+
})
|
|
523
|
+
.catch(fail);
|
|
372
524
|
});
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
.join(", ");
|
|
378
|
-
fail(new Error(loading
|
|
379
|
-
? `Timed out waiting for providers: ${loading}`
|
|
380
|
-
: "Timed out waiting for provider discovery"));
|
|
381
|
-
}, timeoutMs);
|
|
382
|
-
void daemonClient
|
|
383
|
-
.getProvidersSnapshot(snapshotOptions)
|
|
384
|
-
.then((snapshot) => {
|
|
385
|
-
requestId = snapshot.requestId;
|
|
386
|
-
snapshotCwd = snapshot.cwd;
|
|
387
|
-
latestEntries = snapshot.entries;
|
|
388
|
-
if (!snapshot.entries.some((entry) => entry.status === "loading")) {
|
|
389
|
-
finish(snapshot);
|
|
390
|
-
return;
|
|
391
|
-
}
|
|
392
|
-
const pendingUpdate = pendingUpdates.get(snapshotCwd);
|
|
393
|
-
if (pendingUpdate && !pendingUpdate.entries.some((entry) => entry.status === "loading")) {
|
|
394
|
-
finish({ ...pendingUpdate, requestId });
|
|
395
|
-
}
|
|
396
|
-
return undefined;
|
|
397
|
-
})
|
|
398
|
-
.catch(fail);
|
|
399
|
-
});
|
|
525
|
+
}
|
|
526
|
+
finally {
|
|
527
|
+
await observation.release();
|
|
528
|
+
}
|
|
400
529
|
}
|
|
401
530
|
function createGeneratedClientId() {
|
|
402
531
|
const randomId = typeof globalThis.crypto?.randomUUID === "function"
|
|
@@ -7,15 +7,13 @@ type Snapshot = GetProvidersSnapshotResponseMessage["payload"];
|
|
|
7
7
|
export declare class ProviderSnapshotUpdates {
|
|
8
8
|
private readonly host;
|
|
9
9
|
private pending;
|
|
10
|
-
private paused;
|
|
11
10
|
constructor(host: {
|
|
11
|
+
active(update: Update): boolean;
|
|
12
12
|
fetch(cwd: string | undefined): Promise<Snapshot>;
|
|
13
13
|
emit(update: Update): void;
|
|
14
14
|
failed(error: unknown): void;
|
|
15
15
|
});
|
|
16
16
|
clear(): void;
|
|
17
|
-
pause(): void;
|
|
18
|
-
resume(): void;
|
|
19
17
|
receive(update: Update): void;
|
|
20
18
|
private resolve;
|
|
21
19
|
}
|