@hyperdrive.bot/fleet-client 0.3.218 → 0.3.221
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 +39 -7
- package/dist/compat/normalize-provider-models.d.ts +2 -2
- package/dist/compat/normalize-provider-models.js +13 -4
- package/dist/connection/index.d.ts +50 -0
- package/dist/connection/index.js +119 -0
- 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/creation/index.d.ts +40 -0
- package/dist/creation/index.js +197 -0
- package/dist/daemon-client-relay-e2ee-transport.d.ts +1 -0
- package/dist/daemon-client-relay-e2ee-transport.js +10 -6
- package/dist/daemon-client-transport-types.d.ts +1 -1
- package/dist/daemon-client-transport-utils.d.ts +5 -1
- package/dist/daemon-client-transport-utils.js +8 -6
- package/dist/daemon-client-transport.d.ts +2 -2
- package/dist/daemon-client-transport.js +2 -2
- package/dist/daemon-client-websocket-transport.d.ts +4 -0
- package/dist/daemon-client-websocket-transport.js +23 -3
- package/dist/daemon-client.d.ts +1372 -925
- package/dist/daemon-client.js +4073 -2691
- package/dist/index.d.ts +197 -66
- package/dist/index.js +435 -72
- package/dist/legacy-usage-icons.d.ts +2 -0
- package/dist/legacy-usage-icons.js +16 -0
- package/dist/provider-snapshots/index.d.ts +21 -0
- package/dist/provider-snapshots/index.js +56 -0
- package/dist/terminal-stream-router.d.ts +7 -10
- package/dist/terminal-stream-router.js +41 -87
- package/dist/terminals/index.d.ts +69 -0
- package/dist/terminals/index.js +103 -0
- package/dist/timeline-subscription/index.d.ts +25 -0
- package/dist/timeline-subscription/index.js +56 -0
- package/package.json +10 -6
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { SessionInboundMessageSchema } from "@hyperdrive.bot/fleet-protocol/messages";
|
|
2
|
+
import { LegacyWorkspaces } from "./legacy-workspaces.js";
|
|
3
|
+
import { BrowserAutomationHostCapabilitySchema } from "@hyperdrive.bot/fleet-protocol/browser-automation/capabilities";
|
|
4
|
+
// COMPAT(ownedSubscriptions): added in v0.8.0; remove after 2027-03-11 once daemon floor >= v0.8.0.
|
|
5
|
+
// Legacy directories retain their connection-wide, last-query-wins semantics.
|
|
6
|
+
// IDs below identify local listeners, not independent server subscriptions.
|
|
7
|
+
export class LegacySubscriptions {
|
|
8
|
+
constructor(info, send, browserHost) {
|
|
9
|
+
this.info = info;
|
|
10
|
+
this.send = send;
|
|
11
|
+
this.browserHost = browserHost;
|
|
12
|
+
this.interests = new Map();
|
|
13
|
+
this.workspaces = info.features?.workspaceMultiplicity === true ? null : new LegacyWorkspaces();
|
|
14
|
+
}
|
|
15
|
+
normalize(message) {
|
|
16
|
+
return this.workspaces?.normalize(message) ?? message;
|
|
17
|
+
}
|
|
18
|
+
prepareRequest(message) {
|
|
19
|
+
const identity = {
|
|
20
|
+
message,
|
|
21
|
+
receive: (value) => value,
|
|
22
|
+
finish: async () => { },
|
|
23
|
+
};
|
|
24
|
+
const workspaces = this.workspaces;
|
|
25
|
+
if (workspaces && message.type === "fetch_workspaces_request") {
|
|
26
|
+
return {
|
|
27
|
+
...identity,
|
|
28
|
+
message: SessionInboundMessageSchema.parse({
|
|
29
|
+
type: "fetch_agents_request",
|
|
30
|
+
requestId: message.requestId,
|
|
31
|
+
scope: "active",
|
|
32
|
+
sort: [{ key: "updated_at", direction: "desc" }],
|
|
33
|
+
page: message.page,
|
|
34
|
+
subscribe: message.subscribe,
|
|
35
|
+
}),
|
|
36
|
+
receive: (value) => {
|
|
37
|
+
if (value.type !== "fetch_agents_response")
|
|
38
|
+
return value;
|
|
39
|
+
if (value.payload.requestId !== message.requestId)
|
|
40
|
+
return value;
|
|
41
|
+
return {
|
|
42
|
+
type: "fetch_workspaces_response",
|
|
43
|
+
payload: {
|
|
44
|
+
...value.payload,
|
|
45
|
+
entries: workspaces.read(value.payload.entries, !message.page?.cursor),
|
|
46
|
+
emptyProjects: [],
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if (message.type === "subscribe_terminals_request") {
|
|
53
|
+
return {
|
|
54
|
+
...identity,
|
|
55
|
+
receive: (value) => {
|
|
56
|
+
if (value.type !== "terminals_changed" || value.payload.cwd !== message.cwd)
|
|
57
|
+
return value;
|
|
58
|
+
return { ...value, payload: { ...value.payload, requestId: message.requestId } };
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
if (message.type === "workspace.label.list.request" && !message.subscribe) {
|
|
63
|
+
return {
|
|
64
|
+
...identity,
|
|
65
|
+
message: { ...message, subscribe: { subscriptionId: `legacy:${crypto.randomUUID()}` } },
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
if (message.type === "checkout.diff.get.request") {
|
|
69
|
+
const subscriptionId = `legacy:${crypto.randomUUID()}`;
|
|
70
|
+
return {
|
|
71
|
+
message: SessionInboundMessageSchema.parse({
|
|
72
|
+
...message,
|
|
73
|
+
type: "subscribe_checkout_diff_request",
|
|
74
|
+
subscriptionId,
|
|
75
|
+
}),
|
|
76
|
+
receive: (value) => {
|
|
77
|
+
if (value.type !== "subscribe_checkout_diff_response")
|
|
78
|
+
return value;
|
|
79
|
+
if (value.payload.requestId !== message.requestId)
|
|
80
|
+
return value;
|
|
81
|
+
return { ...value, type: "checkout.diff.get.response" };
|
|
82
|
+
},
|
|
83
|
+
finish: () => this.send({ type: "unsubscribe_checkout_diff_request", subscriptionId }),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return identity;
|
|
87
|
+
}
|
|
88
|
+
start(query) {
|
|
89
|
+
const interest = { id: `legacy:${crypto.randomUUID()}`, query };
|
|
90
|
+
this.interests.set(interest.id, interest);
|
|
91
|
+
return interest;
|
|
92
|
+
}
|
|
93
|
+
request({ id, query }) {
|
|
94
|
+
switch (query.type) {
|
|
95
|
+
case "fetch_agents_request":
|
|
96
|
+
case "fetch_workspaces_request":
|
|
97
|
+
case "workspace.label.list.request":
|
|
98
|
+
return { ...query, subscribe: { subscriptionId: id } };
|
|
99
|
+
case "fs.file.subscribe.request":
|
|
100
|
+
case "subscribe_checkout_diff_request":
|
|
101
|
+
return { ...query, subscriptionId: id };
|
|
102
|
+
case "agent.timeline.set_subscription.request":
|
|
103
|
+
if (!this.info.features?.selectiveAgentTimeline)
|
|
104
|
+
return null;
|
|
105
|
+
return { ...query, agentIds: this.members(query.type, "agentIds") };
|
|
106
|
+
case "session.events.set_subscription.request":
|
|
107
|
+
if (!this.info.features?.explicitEventSubscriptions)
|
|
108
|
+
return null;
|
|
109
|
+
return {
|
|
110
|
+
...query,
|
|
111
|
+
events: this.members(query.type, "events").filter(isLegacyEvent),
|
|
112
|
+
};
|
|
113
|
+
case "browser.host.register.request":
|
|
114
|
+
if (!matchesBrowserRegistration(this.browserHost, query)) {
|
|
115
|
+
throw new Error("This daemon requires browser_host registration in the client's hello capabilities");
|
|
116
|
+
}
|
|
117
|
+
return null; // Older hosts register in hello.
|
|
118
|
+
default:
|
|
119
|
+
return query;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
members(type, field) {
|
|
123
|
+
const members = new Set();
|
|
124
|
+
for (const interest of this.interests.values()) {
|
|
125
|
+
if (interest.query.type !== type)
|
|
126
|
+
continue;
|
|
127
|
+
for (const value of interest.query[field])
|
|
128
|
+
members.add(value);
|
|
129
|
+
}
|
|
130
|
+
return [...members].sort();
|
|
131
|
+
}
|
|
132
|
+
release(id) {
|
|
133
|
+
const interest = this.interests.get(id);
|
|
134
|
+
if (!interest)
|
|
135
|
+
return null;
|
|
136
|
+
this.interests.delete(id);
|
|
137
|
+
const { query } = interest;
|
|
138
|
+
switch (query.type) {
|
|
139
|
+
case "agent.timeline.set_subscription.request":
|
|
140
|
+
case "session.events.set_subscription.request":
|
|
141
|
+
return this.request(interest);
|
|
142
|
+
case "fs.file.subscribe.request":
|
|
143
|
+
return { type: "fs.file.unsubscribe.request", subscriptionId: id };
|
|
144
|
+
case "subscribe_checkout_diff_request":
|
|
145
|
+
return { type: "unsubscribe_checkout_diff_request", subscriptionId: id };
|
|
146
|
+
case "subscribe_terminal_request":
|
|
147
|
+
if ([...this.interests.values()].some((item) => item.query.type === query.type && item.query.terminalId === query.terminalId))
|
|
148
|
+
return null;
|
|
149
|
+
return { type: "unsubscribe_terminal_request", terminalId: query.terminalId };
|
|
150
|
+
case "subscribe_terminals_request":
|
|
151
|
+
if ([...this.interests.values()].some((item) => item.query.type === query.type &&
|
|
152
|
+
item.query.cwd === query.cwd &&
|
|
153
|
+
item.query.workspaceId === query.workspaceId))
|
|
154
|
+
return null;
|
|
155
|
+
return {
|
|
156
|
+
type: "unsubscribe_terminals_request",
|
|
157
|
+
cwd: query.cwd,
|
|
158
|
+
workspaceId: query.workspaceId,
|
|
159
|
+
};
|
|
160
|
+
default:
|
|
161
|
+
// Directory slots have no release RPC. Detach the local listener.
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
owns(message) {
|
|
166
|
+
return [...this.interests.values()].some((interest) => this.matches(interest, message));
|
|
167
|
+
}
|
|
168
|
+
receive(message, deliver) {
|
|
169
|
+
const updates = [
|
|
170
|
+
message,
|
|
171
|
+
...(this.workspaces?.update(message) ?? []),
|
|
172
|
+
];
|
|
173
|
+
if (message.type === "agent_stream" && message.payload.event.type === "attention_required") {
|
|
174
|
+
const { agentId, event } = message.payload;
|
|
175
|
+
updates.push({
|
|
176
|
+
type: "agent_attention_required",
|
|
177
|
+
payload: {
|
|
178
|
+
agentId,
|
|
179
|
+
reason: event.reason,
|
|
180
|
+
timestamp: event.timestamp,
|
|
181
|
+
shouldNotify: event.shouldNotify,
|
|
182
|
+
...(event.notification ? { notification: event.notification } : {}),
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
for (const update of updates)
|
|
187
|
+
for (const interest of this.interests.values()) {
|
|
188
|
+
if (!this.matches(interest, update))
|
|
189
|
+
continue;
|
|
190
|
+
// Normalize at the connection edge; all consumers receive the same handle shape.
|
|
191
|
+
deliver(("payload" in update
|
|
192
|
+
? { ...update, payload: { ...update.payload, subscriptionId: interest.id } }
|
|
193
|
+
: { ...update, subscriptionId: interest.id }));
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
matches({ id, query }, message) {
|
|
197
|
+
switch (query.type) {
|
|
198
|
+
case "fetch_agents_request":
|
|
199
|
+
return message.type === "agent_update";
|
|
200
|
+
case "fetch_workspaces_request":
|
|
201
|
+
return message.type === "workspace_update";
|
|
202
|
+
case "workspace.label.list.request":
|
|
203
|
+
return message.type === "workspace.label.update";
|
|
204
|
+
case "fs.file.subscribe.request":
|
|
205
|
+
return message.type === "fs.file.update" && message.payload.subscriptionId === id;
|
|
206
|
+
case "subscribe_checkout_diff_request":
|
|
207
|
+
return message.type === "checkout_diff_update" && message.payload.subscriptionId === id;
|
|
208
|
+
case "subscribe_terminals_request":
|
|
209
|
+
return message.type === "terminals_changed" && message.payload.cwd === query.cwd;
|
|
210
|
+
case "subscribe_terminal_request":
|
|
211
|
+
return (message.type === "terminal_stream_exit" && message.payload.terminalId === query.terminalId);
|
|
212
|
+
case "agent.timeline.set_subscription.request":
|
|
213
|
+
return ((message.type === "agent_stream" || message.type === "agent.timeline.replacement") &&
|
|
214
|
+
query.agentIds.includes(message.payload.agentId));
|
|
215
|
+
case "session.events.set_subscription.request":
|
|
216
|
+
return query.events.includes(message.type === "status" ? `status.${message.payload.status}` : message.type);
|
|
217
|
+
case "browser.host.register.request":
|
|
218
|
+
return message.type === "browser.automation.execute.request";
|
|
219
|
+
default:
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
function isLegacyEvent(event) {
|
|
225
|
+
return [
|
|
226
|
+
"project.update",
|
|
227
|
+
"providers_snapshot_update",
|
|
228
|
+
"agent_attention_required",
|
|
229
|
+
"agent_permission_request",
|
|
230
|
+
"agent_permission_resolved",
|
|
231
|
+
].includes(event);
|
|
232
|
+
}
|
|
233
|
+
function matchesBrowserRegistration(advertised, request) {
|
|
234
|
+
const host = BrowserAutomationHostCapabilitySchema.safeParse(advertised);
|
|
235
|
+
if (!host.success)
|
|
236
|
+
return false;
|
|
237
|
+
if (host.data.hostKind !== request.hostKind)
|
|
238
|
+
return false;
|
|
239
|
+
for (const command of request.supportedCommands) {
|
|
240
|
+
if (!host.data.supportedCommands.some((supported) => supported === command))
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
//# sourceMappingURL=legacy.js.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { SessionOutboundMessage } from "@hyperdrive.bot/fleet-protocol/messages";
|
|
2
|
+
export type SubscriptionSnapshot<T> = T & {
|
|
3
|
+
subscriptionId: string;
|
|
4
|
+
};
|
|
5
|
+
export interface SubscriptionObserver<T> {
|
|
6
|
+
snapshot(snapshot: T): void;
|
|
7
|
+
update(message: SessionOutboundMessage): void;
|
|
8
|
+
error?(error: unknown): void;
|
|
9
|
+
}
|
|
10
|
+
/** A stable local lifetime. IDs change on reconnect; legacy IDs identify local listeners only. */
|
|
11
|
+
export interface OwnedSubscription<T> {
|
|
12
|
+
readonly subscriptionId: string | null;
|
|
13
|
+
readonly ready: Promise<SubscriptionSnapshot<T>>;
|
|
14
|
+
subscribe(observer: SubscriptionObserver<SubscriptionSnapshot<T>>): () => void;
|
|
15
|
+
release(): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export interface OwnedSubscriptionsHost {
|
|
18
|
+
release(subscriptionId: string): Promise<void>;
|
|
19
|
+
failed(error: unknown): void;
|
|
20
|
+
}
|
|
21
|
+
/** Transport routing and lifetime only; query semantics remain with each domain. */
|
|
22
|
+
export declare class OwnedSubscriptions {
|
|
23
|
+
private readonly host;
|
|
24
|
+
private readonly registrations;
|
|
25
|
+
private readonly routes;
|
|
26
|
+
private generation;
|
|
27
|
+
private connected;
|
|
28
|
+
constructor(host: OwnedSubscriptionsHost);
|
|
29
|
+
observe<T>(request: (accept: (snapshot: T) => void) => Promise<T>, signal?: AbortSignal, requestFailed?: () => void): OwnedSubscription<T>;
|
|
30
|
+
owns(message: SessionOutboundMessage): boolean;
|
|
31
|
+
receive(message: SessionOutboundMessage): void;
|
|
32
|
+
restore(): void;
|
|
33
|
+
disconnected(): void;
|
|
34
|
+
close(): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=owned.d.ts.map
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
const MAX_BOOTSTRAP_UPDATES = 128;
|
|
2
|
+
/** Transport routing and lifetime only; query semantics remain with each domain. */
|
|
3
|
+
export class OwnedSubscriptions {
|
|
4
|
+
constructor(host) {
|
|
5
|
+
this.host = host;
|
|
6
|
+
this.registrations = new Set();
|
|
7
|
+
this.routes = new Map();
|
|
8
|
+
this.generation = 0;
|
|
9
|
+
this.connected = false;
|
|
10
|
+
}
|
|
11
|
+
observe(request, signal, requestFailed) {
|
|
12
|
+
let id = null;
|
|
13
|
+
let snapshot;
|
|
14
|
+
let pending = null;
|
|
15
|
+
let teardown = null;
|
|
16
|
+
let disposed = false;
|
|
17
|
+
let overflow = false;
|
|
18
|
+
const buffered = [];
|
|
19
|
+
const observers = new Set();
|
|
20
|
+
let resolve;
|
|
21
|
+
let reject;
|
|
22
|
+
const ready = new Promise((accept, fail) => {
|
|
23
|
+
resolve = accept;
|
|
24
|
+
reject = fail;
|
|
25
|
+
});
|
|
26
|
+
void ready.catch(() => { });
|
|
27
|
+
const deliver = (observer, message) => {
|
|
28
|
+
try {
|
|
29
|
+
if (message)
|
|
30
|
+
observer.update(message);
|
|
31
|
+
else if (snapshot)
|
|
32
|
+
observer.snapshot(snapshot);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
this.host.failed(error);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const releaseId = async () => {
|
|
39
|
+
const released = id;
|
|
40
|
+
id = null;
|
|
41
|
+
if (!released)
|
|
42
|
+
return;
|
|
43
|
+
this.routes.delete(released);
|
|
44
|
+
await this.host.release(released);
|
|
45
|
+
};
|
|
46
|
+
const registration = {
|
|
47
|
+
start: () => {
|
|
48
|
+
if (disposed || pending || id || !this.connected)
|
|
49
|
+
return;
|
|
50
|
+
const generation = this.generation;
|
|
51
|
+
pending = request((value) => {
|
|
52
|
+
if (generation !== this.generation)
|
|
53
|
+
return;
|
|
54
|
+
assertSubscriptionSnapshot(value);
|
|
55
|
+
id = value.subscriptionId;
|
|
56
|
+
snapshot = value;
|
|
57
|
+
buffered.length = 0;
|
|
58
|
+
overflow = false;
|
|
59
|
+
if (disposed)
|
|
60
|
+
return;
|
|
61
|
+
this.routes.set(id, registration);
|
|
62
|
+
for (const observer of observers)
|
|
63
|
+
deliver(observer);
|
|
64
|
+
})
|
|
65
|
+
.then(() => {
|
|
66
|
+
if (generation === this.generation && !disposed) {
|
|
67
|
+
if (!snapshot)
|
|
68
|
+
throw new Error("The host omitted its subscription snapshot");
|
|
69
|
+
resolve(snapshot);
|
|
70
|
+
}
|
|
71
|
+
return undefined;
|
|
72
|
+
})
|
|
73
|
+
.catch((error) => {
|
|
74
|
+
if (generation !== this.generation)
|
|
75
|
+
return;
|
|
76
|
+
reject(error);
|
|
77
|
+
for (const observer of observers) {
|
|
78
|
+
try {
|
|
79
|
+
observer.error?.(error);
|
|
80
|
+
}
|
|
81
|
+
catch (failure) {
|
|
82
|
+
this.host.failed(failure);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
void handle.release().catch(this.host.failed);
|
|
86
|
+
this.host.failed(error);
|
|
87
|
+
requestFailed?.();
|
|
88
|
+
return undefined;
|
|
89
|
+
})
|
|
90
|
+
.finally(() => {
|
|
91
|
+
pending = null;
|
|
92
|
+
if (generation !== this.generation && !disposed)
|
|
93
|
+
registration.start();
|
|
94
|
+
});
|
|
95
|
+
},
|
|
96
|
+
disconnected: () => {
|
|
97
|
+
id = null;
|
|
98
|
+
snapshot = undefined;
|
|
99
|
+
buffered.length = 0;
|
|
100
|
+
overflow = false;
|
|
101
|
+
},
|
|
102
|
+
receive: (message) => {
|
|
103
|
+
if (disposed || ("payload" in message && Object.is(message.payload, snapshot)))
|
|
104
|
+
return;
|
|
105
|
+
if (buffered.length < MAX_BOOTSTRAP_UPDATES && !overflow)
|
|
106
|
+
buffered.push(message);
|
|
107
|
+
else {
|
|
108
|
+
buffered.length = 0;
|
|
109
|
+
overflow = true;
|
|
110
|
+
}
|
|
111
|
+
for (const observer of observers)
|
|
112
|
+
deliver(observer, message);
|
|
113
|
+
},
|
|
114
|
+
release: () => {
|
|
115
|
+
if (teardown)
|
|
116
|
+
return teardown;
|
|
117
|
+
disposed = true;
|
|
118
|
+
signal?.removeEventListener("abort", abort);
|
|
119
|
+
observers.clear();
|
|
120
|
+
buffered.length = 0;
|
|
121
|
+
if (id)
|
|
122
|
+
this.routes.delete(id);
|
|
123
|
+
reject(new Error("Subscription released"));
|
|
124
|
+
teardown = (async () => {
|
|
125
|
+
await pending;
|
|
126
|
+
await releaseId();
|
|
127
|
+
this.registrations.delete(registration);
|
|
128
|
+
})();
|
|
129
|
+
return teardown;
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
const abort = () => {
|
|
133
|
+
void handle.release().catch(this.host.failed);
|
|
134
|
+
};
|
|
135
|
+
const handle = {
|
|
136
|
+
get subscriptionId() {
|
|
137
|
+
return id;
|
|
138
|
+
},
|
|
139
|
+
ready,
|
|
140
|
+
release: registration.release,
|
|
141
|
+
subscribe: (observer) => {
|
|
142
|
+
if (disposed)
|
|
143
|
+
throw new Error("Subscription released");
|
|
144
|
+
observers.add(observer);
|
|
145
|
+
if (overflow) {
|
|
146
|
+
overflow = false;
|
|
147
|
+
snapshot = undefined;
|
|
148
|
+
void releaseId()
|
|
149
|
+
.then(() => {
|
|
150
|
+
registration.start();
|
|
151
|
+
return undefined;
|
|
152
|
+
})
|
|
153
|
+
.catch(this.host.failed);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
deliver(observer);
|
|
157
|
+
for (const message of buffered)
|
|
158
|
+
deliver(observer, message);
|
|
159
|
+
}
|
|
160
|
+
return () => {
|
|
161
|
+
observers.delete(observer);
|
|
162
|
+
};
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
this.registrations.add(registration);
|
|
166
|
+
if (signal?.aborted)
|
|
167
|
+
abort();
|
|
168
|
+
else {
|
|
169
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
170
|
+
registration.start();
|
|
171
|
+
}
|
|
172
|
+
return handle;
|
|
173
|
+
}
|
|
174
|
+
owns(message) {
|
|
175
|
+
const payload = "payload" in message ? message.payload : message;
|
|
176
|
+
return ("subscriptionId" in payload &&
|
|
177
|
+
typeof payload.subscriptionId === "string" &&
|
|
178
|
+
this.routes.has(payload.subscriptionId));
|
|
179
|
+
}
|
|
180
|
+
receive(message) {
|
|
181
|
+
const payload = "payload" in message ? message.payload : message;
|
|
182
|
+
if ("subscriptionId" in payload && typeof payload.subscriptionId === "string")
|
|
183
|
+
this.routes.get(payload.subscriptionId)?.receive(message);
|
|
184
|
+
}
|
|
185
|
+
restore() {
|
|
186
|
+
this.connected = true;
|
|
187
|
+
for (const registration of this.registrations)
|
|
188
|
+
registration.start();
|
|
189
|
+
}
|
|
190
|
+
disconnected() {
|
|
191
|
+
this.connected = false;
|
|
192
|
+
this.generation++;
|
|
193
|
+
this.routes.clear();
|
|
194
|
+
for (const registration of this.registrations)
|
|
195
|
+
registration.disconnected();
|
|
196
|
+
}
|
|
197
|
+
async close() {
|
|
198
|
+
await Promise.all([...this.registrations].map((registration) => registration.release()));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function assertSubscriptionSnapshot(value) {
|
|
202
|
+
if (value &&
|
|
203
|
+
typeof value === "object" &&
|
|
204
|
+
"subscriptionId" in value &&
|
|
205
|
+
typeof value.subscriptionId === "string" &&
|
|
206
|
+
value.subscriptionId)
|
|
207
|
+
return;
|
|
208
|
+
throw new Error("The host omitted its subscription ID");
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=owned.js.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { AgentSnapshotPayload, CreationSnapshot, WorkspaceCreateResponse } from "@hyperdrive.bot/fleet-protocol/messages";
|
|
2
|
+
import type { CreateAgentRequestOptions, CreateWorkspaceRequestOptions } from "../daemon-client.js";
|
|
3
|
+
type Kind = CreationSnapshot["kind"];
|
|
4
|
+
export interface CreationResult {
|
|
5
|
+
agent?: AgentSnapshotPayload | null;
|
|
6
|
+
workspace?: WorkspaceCreateResponse["payload"]["workspace"];
|
|
7
|
+
creation?: CreationSnapshot;
|
|
8
|
+
error: string | null;
|
|
9
|
+
errorCode?: string;
|
|
10
|
+
setupTerminalId?: string | null;
|
|
11
|
+
setupSkippedReason?: string;
|
|
12
|
+
requestId?: string;
|
|
13
|
+
}
|
|
14
|
+
interface Dependencies {
|
|
15
|
+
supports: (feature: "creationLifecycle" | "agentRequestReceipts") => boolean;
|
|
16
|
+
requestId: () => string;
|
|
17
|
+
request: (kind: Kind, input: Record<string, unknown>) => Promise<CreationResult>;
|
|
18
|
+
observe: (kind: Kind, key: string, next: (snapshot: CreationSnapshot | null) => void, error: (error: unknown) => void) => () => void;
|
|
19
|
+
legacyAgent: (input: CreateAgentRequestOptions) => Promise<AgentSnapshotPayload>;
|
|
20
|
+
legacyWorkspace: (input: CreateWorkspaceRequestOptions) => Promise<WorkspaceCreateResponse["payload"]>;
|
|
21
|
+
}
|
|
22
|
+
/** Client-owned observation and compatibility. UI callbacks never advance creation. */
|
|
23
|
+
export declare class CreationClient {
|
|
24
|
+
private readonly deps;
|
|
25
|
+
private readonly operations;
|
|
26
|
+
constructor(deps: Dependencies);
|
|
27
|
+
createAgent(input: CreateAgentRequestOptions): Promise<CreationResult>;
|
|
28
|
+
createWorkspace(input: CreateWorkspaceRequestOptions): Promise<CreationResult>;
|
|
29
|
+
private legacyAgent;
|
|
30
|
+
private start;
|
|
31
|
+
private submit;
|
|
32
|
+
receive(snapshot: CreationSnapshot): void;
|
|
33
|
+
reconnect(): void;
|
|
34
|
+
close(): void;
|
|
35
|
+
private finish;
|
|
36
|
+
private fail;
|
|
37
|
+
private notify;
|
|
38
|
+
}
|
|
39
|
+
export {};
|
|
40
|
+
//# sourceMappingURL=index.d.ts.map
|