@seed-app-studio/sdk 0.1.1-canary.0 → 0.1.1-canary.2
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/index.cjs +4120 -0
- package/dist/index.d.cts +2672 -0
- package/package.json +4 -4
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,4120 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let _seed_app_studio_protocol = require("@seed-app-studio/protocol");
|
|
3
|
+
//#region src/agentStream.ts
|
|
4
|
+
function matchesSeedAppHostEventSubscription(eventName, event) {
|
|
5
|
+
switch (eventName) {
|
|
6
|
+
case _seed_app_studio_protocol.SEED_APP_HOST_EVENT_NAMES.agentStream: return (0, _seed_app_studio_protocol.isSeedAppAgentStreamEvent)(event);
|
|
7
|
+
case _seed_app_studio_protocol.SEED_APP_HOST_EVENT_NAMES.aguiStream: return (0, _seed_app_studio_protocol.isSeedAguiEvent)(event);
|
|
8
|
+
case _seed_app_studio_protocol.SEED_APP_HOST_EVENT_NAMES.backgroundStream: return (0, _seed_app_studio_protocol.isSeedBackgroundStreamEvent)(event) || event.type === _seed_app_studio_protocol.SEED_AGENT_STREAM_EVENT_NAMES.backgroundOutput;
|
|
9
|
+
default: return event.type === eventName;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/contracts.ts
|
|
14
|
+
const SEED_APP_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
15
|
+
"host_unavailable",
|
|
16
|
+
"app_not_installed",
|
|
17
|
+
"app_disabled",
|
|
18
|
+
"handshake_timeout",
|
|
19
|
+
"unsupported_runtime",
|
|
20
|
+
"unauthenticated",
|
|
21
|
+
"capability_denied",
|
|
22
|
+
"approval_required",
|
|
23
|
+
"approval_denied",
|
|
24
|
+
"tenant_policy_denied",
|
|
25
|
+
"origin_denied",
|
|
26
|
+
"grant_required",
|
|
27
|
+
"nonce_invalid",
|
|
28
|
+
"csrf_required",
|
|
29
|
+
"bridge_policy_denied",
|
|
30
|
+
"provider_unavailable",
|
|
31
|
+
"provider_failed",
|
|
32
|
+
"payload_too_large",
|
|
33
|
+
"rate_limited",
|
|
34
|
+
"invalid_payload_schema",
|
|
35
|
+
"reserved_bridge_key",
|
|
36
|
+
"mcp_unavailable",
|
|
37
|
+
"memory_scope_denied",
|
|
38
|
+
"resource_scope_denied",
|
|
39
|
+
"skill_scope_denied",
|
|
40
|
+
"bundle_integrity_failed",
|
|
41
|
+
"sdk_version_incompatible",
|
|
42
|
+
"invalid_request",
|
|
43
|
+
"invalid_target",
|
|
44
|
+
"not_found",
|
|
45
|
+
"already_exists",
|
|
46
|
+
"not_a_file",
|
|
47
|
+
"not_a_directory",
|
|
48
|
+
"parent_not_found",
|
|
49
|
+
"directory_not_empty",
|
|
50
|
+
"protected_path",
|
|
51
|
+
"permission_denied",
|
|
52
|
+
"mount_not_found",
|
|
53
|
+
"resource_not_found",
|
|
54
|
+
"resource_disabled",
|
|
55
|
+
"resource_unavailable",
|
|
56
|
+
"mode_not_allowed",
|
|
57
|
+
"unsupported_operation",
|
|
58
|
+
"unsupported_option_combination",
|
|
59
|
+
"execute_preflight_unknown",
|
|
60
|
+
"cross_mount_move_unsupported",
|
|
61
|
+
"version_conflict",
|
|
62
|
+
"edit_conflict",
|
|
63
|
+
"idempotency_conflict",
|
|
64
|
+
"stale_writer",
|
|
65
|
+
"write_fence_expired",
|
|
66
|
+
"write_fence_scope_denied",
|
|
67
|
+
"file_too_large",
|
|
68
|
+
"cursor_invalid",
|
|
69
|
+
"timeout",
|
|
70
|
+
"cancelled",
|
|
71
|
+
"provider_error",
|
|
72
|
+
"internal_error"
|
|
73
|
+
]);
|
|
74
|
+
function normalizeSeedAppErrorCode(value) {
|
|
75
|
+
return typeof value === "string" && SEED_APP_ERROR_CODES.has(value) ? value : "provider_failed";
|
|
76
|
+
}
|
|
77
|
+
function createSeedAppError(input) {
|
|
78
|
+
return {
|
|
79
|
+
retryable: false,
|
|
80
|
+
...input
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
const TRACEPARENT_PATTERN = /^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/i;
|
|
84
|
+
const ZERO_TRACE_ID = "00000000000000000000000000000000";
|
|
85
|
+
const ZERO_SPAN_ID = "0000000000000000";
|
|
86
|
+
function isSeedAppTraceEnabled(env = readSeedAppEnv()) {
|
|
87
|
+
return env.SEED_TRACE === "1" || env.SEED_TRACE?.toLowerCase() === "true";
|
|
88
|
+
}
|
|
89
|
+
function createSeedAppRootTraceContext(input = {}) {
|
|
90
|
+
const traceId = isValidTraceId(input.traceId) ? input.traceId.toLowerCase() : createRandomHex(16, ZERO_TRACE_ID);
|
|
91
|
+
const spanId = createRandomHex(8, ZERO_SPAN_ID);
|
|
92
|
+
const sampled = input.sampled ?? true;
|
|
93
|
+
return {
|
|
94
|
+
traceId,
|
|
95
|
+
spanId,
|
|
96
|
+
traceparent: toSeedAppTraceparent({
|
|
97
|
+
traceId,
|
|
98
|
+
spanId,
|
|
99
|
+
sampled
|
|
100
|
+
}),
|
|
101
|
+
sampled
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function extractSeedAppTraceContext(carrier) {
|
|
105
|
+
const record = readSeedAppRecord(carrier);
|
|
106
|
+
if (!record) return void 0;
|
|
107
|
+
const parsed = parseSeedAppTraceparent(firstString(nestedString(record.metadata, "traceparent"), nestedString(record.metadata, "traceParent"), record.traceparent, record.traceParent));
|
|
108
|
+
if (parsed) return parsed;
|
|
109
|
+
const traceId = firstString(nestedString(record.metadata, "traceId"), record.traceId);
|
|
110
|
+
return isValidTraceId(traceId) ? createSeedAppRootTraceContext({ traceId: traceId.toLowerCase() }) : void 0;
|
|
111
|
+
}
|
|
112
|
+
function ensureSeedAppTraceCarrier(value, env = readSeedAppEnv()) {
|
|
113
|
+
const record = readSeedAppRecord(value);
|
|
114
|
+
if (!record) return value;
|
|
115
|
+
const context = extractSeedAppTraceContext(record) ?? (isSeedAppTraceEnabled(env) ? createSeedAppRootTraceContext() : void 0);
|
|
116
|
+
if (!context) return value;
|
|
117
|
+
return injectSeedAppTraceContext(record, context);
|
|
118
|
+
}
|
|
119
|
+
function injectSeedAppTraceContext(record, context) {
|
|
120
|
+
const metadata = readSeedAppRecord(record.metadata) ?? {};
|
|
121
|
+
const traceId = firstString(metadata.traceId, record.traceId) ?? context.traceId;
|
|
122
|
+
const traceparent = firstString(metadata.traceparent, metadata.traceParent, record.traceparent, record.traceParent) ?? context.traceparent;
|
|
123
|
+
return {
|
|
124
|
+
...record,
|
|
125
|
+
traceId,
|
|
126
|
+
traceparent,
|
|
127
|
+
metadata: {
|
|
128
|
+
...metadata,
|
|
129
|
+
traceId,
|
|
130
|
+
traceparent
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function parseSeedAppTraceparent(traceparent) {
|
|
135
|
+
if (typeof traceparent !== "string") return void 0;
|
|
136
|
+
const match = TRACEPARENT_PATTERN.exec(traceparent.trim());
|
|
137
|
+
if (!match) return void 0;
|
|
138
|
+
const [version, traceId, spanId, flags] = match.slice(1);
|
|
139
|
+
if (version.toLowerCase() === "ff") return void 0;
|
|
140
|
+
if (!isValidTraceId(traceId) || !isValidSpanId(spanId)) return void 0;
|
|
141
|
+
return {
|
|
142
|
+
traceId: traceId.toLowerCase(),
|
|
143
|
+
spanId: spanId.toLowerCase(),
|
|
144
|
+
traceparent: `00-${traceId.toLowerCase()}-${spanId.toLowerCase()}-${flags.toLowerCase()}`,
|
|
145
|
+
sampled: (Number.parseInt(flags, 16) & 1) === 1
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
function toSeedAppTraceparent(context) {
|
|
149
|
+
return `00-${context.traceId}-${context.spanId}-${context.sampled ? "01" : "00"}`;
|
|
150
|
+
}
|
|
151
|
+
function isValidTraceId(value) {
|
|
152
|
+
return typeof value === "string" && /^[0-9a-f]{32}$/i.test(value) && value.toLowerCase() !== ZERO_TRACE_ID;
|
|
153
|
+
}
|
|
154
|
+
function isValidSpanId(value) {
|
|
155
|
+
return typeof value === "string" && /^[0-9a-f]{16}$/i.test(value) && value.toLowerCase() !== ZERO_SPAN_ID;
|
|
156
|
+
}
|
|
157
|
+
function createRandomHex(byteLength, zeroValue) {
|
|
158
|
+
for (let attempt = 0; attempt < 4; attempt += 1) {
|
|
159
|
+
const bytes = new Uint8Array(byteLength);
|
|
160
|
+
const cryptoLike = globalThis.crypto;
|
|
161
|
+
const randomBytes = cryptoLike?.getRandomValues ? cryptoLike.getRandomValues(bytes) : fallbackRandomBytes(bytes);
|
|
162
|
+
const value = Array.from(randomBytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
163
|
+
if (value !== zeroValue) return value;
|
|
164
|
+
}
|
|
165
|
+
return `${"1".padStart(byteLength * 2, "0")}`;
|
|
166
|
+
}
|
|
167
|
+
function fallbackRandomBytes(bytes) {
|
|
168
|
+
for (let index = 0; index < bytes.length; index += 1) bytes[index] = Math.floor(Math.random() * 256);
|
|
169
|
+
return bytes;
|
|
170
|
+
}
|
|
171
|
+
function firstString(...values) {
|
|
172
|
+
return values.find((value) => typeof value === "string");
|
|
173
|
+
}
|
|
174
|
+
function nestedString(value, key) {
|
|
175
|
+
const nestedValue = readSeedAppRecord(value)?.[key];
|
|
176
|
+
return typeof nestedValue === "string" ? nestedValue : void 0;
|
|
177
|
+
}
|
|
178
|
+
function readSeedAppRecord(value) {
|
|
179
|
+
return isRecord$4(value) ? value : void 0;
|
|
180
|
+
}
|
|
181
|
+
function readSeedAppEnv() {
|
|
182
|
+
return globalThis.process?.env ?? {};
|
|
183
|
+
}
|
|
184
|
+
const seedBridgeGoldenCaseReportSchemaVersion = "seed.bridge.golden-case.report.v1";
|
|
185
|
+
const seedBridgeGoldenCasePrimaryTransportByRuntime = {
|
|
186
|
+
electron: "electron-ipc",
|
|
187
|
+
"local-webui": "local-websocket",
|
|
188
|
+
"web-saas": "web-saas-bridge-ws",
|
|
189
|
+
"react-native": "react-native-webview",
|
|
190
|
+
"app-host": "app-host-postmessage"
|
|
191
|
+
};
|
|
192
|
+
function createSeedBridgeGoldenCaseReport(input) {
|
|
193
|
+
return {
|
|
194
|
+
schemaVersion: seedBridgeGoldenCaseReportSchemaVersion,
|
|
195
|
+
...input
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function validateSeedBridgeGoldenCaseReport(report) {
|
|
199
|
+
const errors = [];
|
|
200
|
+
const record = report;
|
|
201
|
+
if (!isRecord$4(record)) return {
|
|
202
|
+
ok: false,
|
|
203
|
+
errors: ["report must be an object."]
|
|
204
|
+
};
|
|
205
|
+
if (record.schemaVersion !== "seed.bridge.golden-case.report.v1") errors.push(`schemaVersion must be ${seedBridgeGoldenCaseReportSchemaVersion}.`);
|
|
206
|
+
validateRequiredText(record, "runtime", errors);
|
|
207
|
+
validateRequiredText(record, "transport", errors);
|
|
208
|
+
validateRequiredText(record, "appId", errors);
|
|
209
|
+
validateRequiredText(record, "appSessionId", errors);
|
|
210
|
+
validateRequiredText(record, "generatedAt", errors);
|
|
211
|
+
if (hasText(record.runtime) && !isSeedBridgeGoldenCaseRuntime(record.runtime)) errors.push("runtime must be one of electron, local-webui, web-saas, react-native, app-host.");
|
|
212
|
+
validateRuntimeTransport(record, errors);
|
|
213
|
+
validateGeneratedAt(record.generatedAt, errors);
|
|
214
|
+
validateMethodResults(record.methodResults, errors);
|
|
215
|
+
validateRequiredTextArray(record.eventTypes, "eventTypes", errors);
|
|
216
|
+
validateRequiredTextArray(record.terminalTypes, "terminalTypes", errors);
|
|
217
|
+
validateNegativeCases(record.negativeCases, errors);
|
|
218
|
+
validateArtifacts(record.artifacts, errors);
|
|
219
|
+
return {
|
|
220
|
+
ok: errors.length === 0,
|
|
221
|
+
errors
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
function isSeedBridgeGoldenCaseReport(value) {
|
|
225
|
+
if (!isRecord$4(value)) return false;
|
|
226
|
+
return validateSeedBridgeGoldenCaseReport(value).ok;
|
|
227
|
+
}
|
|
228
|
+
function validateRuntimeTransport(record, errors) {
|
|
229
|
+
if (!isSeedBridgeGoldenCaseRuntime(record.runtime) || typeof record.transport !== "string") return;
|
|
230
|
+
const expectedTransport = seedBridgeGoldenCasePrimaryTransportByRuntime[record.runtime];
|
|
231
|
+
if (record.transport !== expectedTransport) errors.push(`transport must be ${expectedTransport} for runtime ${record.runtime}.`);
|
|
232
|
+
}
|
|
233
|
+
function validateGeneratedAt(value, errors) {
|
|
234
|
+
if (typeof value !== "string" || value.trim().length === 0) return;
|
|
235
|
+
if (Number.isNaN(Date.parse(value))) errors.push("generatedAt must be an ISO-8601 timestamp.");
|
|
236
|
+
}
|
|
237
|
+
function validateMethodResults(value, errors) {
|
|
238
|
+
if (!Array.isArray(value)) {
|
|
239
|
+
errors.push("methodResults must be an array.");
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (value.length === 0) errors.push("methodResults must contain at least one method result.");
|
|
243
|
+
value.forEach((entry, index) => {
|
|
244
|
+
if (!isRecord$4(entry)) {
|
|
245
|
+
errors.push(`methodResults[${index}] must be an object.`);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (!hasText(entry.method)) errors.push(`methodResults[${index}].method must be a non-empty string.`);
|
|
249
|
+
if (typeof entry.ok !== "boolean") errors.push(`methodResults[${index}].ok must be a boolean.`);
|
|
250
|
+
if (entry.durationMs !== void 0 && !isNonNegativeFiniteNumber(entry.durationMs)) errors.push(`methodResults[${index}].durationMs must be a non-negative finite number.`);
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
function validateRequiredTextArray(value, field, errors) {
|
|
254
|
+
if (!Array.isArray(value)) {
|
|
255
|
+
errors.push(`${field} must be an array.`);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
if (value.length === 0) errors.push(`${field} must contain at least one value.`);
|
|
259
|
+
value.forEach((entry, index) => {
|
|
260
|
+
if (!hasText(entry)) errors.push(`${field}[${index}] must be a non-empty string.`);
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
function validateNegativeCases(value, errors) {
|
|
264
|
+
if (!Array.isArray(value)) {
|
|
265
|
+
errors.push("negativeCases must be an array.");
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
if (value.length === 0) errors.push("negativeCases must contain at least one case.");
|
|
269
|
+
value.forEach((entry, index) => {
|
|
270
|
+
if (!isRecord$4(entry)) {
|
|
271
|
+
errors.push(`negativeCases[${index}] must be an object.`);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
if (!hasText(entry.id)) errors.push(`negativeCases[${index}].id must be a non-empty string.`);
|
|
275
|
+
if (entry.status !== "passed" && entry.status !== "failed") errors.push(`negativeCases[${index}].status must be passed or failed.`);
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
function validateArtifacts(value, errors) {
|
|
279
|
+
if (!Array.isArray(value)) {
|
|
280
|
+
errors.push("artifacts must be an array.");
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
if (value.length === 0) errors.push("artifacts must contain at least one artifact.");
|
|
284
|
+
value.forEach((entry, index) => {
|
|
285
|
+
if (!isRecord$4(entry)) {
|
|
286
|
+
errors.push(`artifacts[${index}] must be an object.`);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
if (!hasText(entry.kind)) errors.push(`artifacts[${index}].kind must be a non-empty string.`);
|
|
290
|
+
if (!hasText(entry.path) && !hasText(entry.url)) errors.push(`artifacts[${index}] must include path or url.`);
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
function validateRequiredText(record, field, errors) {
|
|
294
|
+
if (!hasText(readPath(record, field))) errors.push(`${field} must be a non-empty string.`);
|
|
295
|
+
}
|
|
296
|
+
function readPath(record, path) {
|
|
297
|
+
return path.split(".").reduce((current, segment) => {
|
|
298
|
+
if (!isRecord$4(current)) return void 0;
|
|
299
|
+
return current[segment];
|
|
300
|
+
}, record);
|
|
301
|
+
}
|
|
302
|
+
function isSeedBridgeGoldenCaseRuntime(value) {
|
|
303
|
+
return value === "electron" || value === "local-webui" || value === "web-saas" || value === "react-native" || value === "app-host";
|
|
304
|
+
}
|
|
305
|
+
function isNonNegativeFiniteNumber(value) {
|
|
306
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
307
|
+
}
|
|
308
|
+
function hasText(value) {
|
|
309
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
310
|
+
}
|
|
311
|
+
function isRecord$4(value) {
|
|
312
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
313
|
+
}
|
|
314
|
+
//#endregion
|
|
315
|
+
//#region src/workspace.ts
|
|
316
|
+
const WORKSPACE_CHANGED_METHOD = "seed.workspace.changed";
|
|
317
|
+
function createSeedAppWorkspaceClient(request, eventSource = {}) {
|
|
318
|
+
return {
|
|
319
|
+
async list(input, options) {
|
|
320
|
+
assertWorkspaceLocation(input, "seed.workspace.list");
|
|
321
|
+
assertPositiveInteger(input.maxDepth, "maxDepth", "seed.workspace.list");
|
|
322
|
+
assertPositiveInteger(input.maxResults, "maxResults", "seed.workspace.list");
|
|
323
|
+
return request("seed.workspace.list", input, options);
|
|
324
|
+
},
|
|
325
|
+
async read(input, options) {
|
|
326
|
+
assertWorkspaceLocation(input, "seed.workspace.read");
|
|
327
|
+
assertPositiveInteger(input.maxBytes, "maxBytes", "seed.workspace.read");
|
|
328
|
+
return request("seed.workspace.read", input, options);
|
|
329
|
+
},
|
|
330
|
+
async search(input, options) {
|
|
331
|
+
assertOptionalWorkspaceLocation(input, "seed.workspace.search");
|
|
332
|
+
assertSearchNeedle(input);
|
|
333
|
+
assertPositiveInteger(input.maxDepth, "maxDepth", "seed.workspace.search");
|
|
334
|
+
assertPositiveInteger(input.maxResults, "maxResults", "seed.workspace.search");
|
|
335
|
+
return request("seed.workspace.search", input, options);
|
|
336
|
+
},
|
|
337
|
+
async capabilities(input = {}, options) {
|
|
338
|
+
assertOptionalWorkspaceLocation(input, "seed.workspace.capabilities.get");
|
|
339
|
+
return request("seed.workspace.capabilities.get", input, options);
|
|
340
|
+
},
|
|
341
|
+
async write(input, options) {
|
|
342
|
+
return request("seed.workspace.write", toWorkspaceWritePayload(input), options);
|
|
343
|
+
},
|
|
344
|
+
async stageUpload(input, options) {
|
|
345
|
+
return request("seed.workspace.upload.stage", toWorkspaceUploadStagePayload(input), options);
|
|
346
|
+
},
|
|
347
|
+
async delete(input, options) {
|
|
348
|
+
assertWorkspaceLocation(input, "seed.workspace.delete");
|
|
349
|
+
assertNonEmptyString(input.expectedVersion ?? input.expectedEtag ?? "", "expectedVersion", "seed.workspace.delete");
|
|
350
|
+
assertNonEmptyString(input.idempotencyKey, "idempotencyKey", "seed.workspace.delete");
|
|
351
|
+
return request("seed.workspace.delete", input, options);
|
|
352
|
+
},
|
|
353
|
+
async rename(input, options) {
|
|
354
|
+
assertWorkspaceLocation(input, "seed.workspace.rename");
|
|
355
|
+
assertWorkspaceFileName(input.newName, "newName", "seed.workspace.rename");
|
|
356
|
+
assertNonEmptyString(input.expectedVersion ?? input.expectedEtag ?? "", "expectedVersion", "seed.workspace.rename");
|
|
357
|
+
assertNonEmptyString(input.idempotencyKey, "idempotencyKey", "seed.workspace.rename");
|
|
358
|
+
return request("seed.workspace.rename", input, options);
|
|
359
|
+
},
|
|
360
|
+
async copy(input, options) {
|
|
361
|
+
assertWorkspaceLocation(input, "seed.workspace.copy");
|
|
362
|
+
assertOptionalWorkspaceId(input.targetWorkspaceId, "targetWorkspaceId", "seed.workspace.copy");
|
|
363
|
+
assertOptionalWorkspaceRelativePath(input.name, "name", "seed.workspace.copy");
|
|
364
|
+
return request("seed.workspace.copy", input, options);
|
|
365
|
+
},
|
|
366
|
+
async importLocalFiles(input, options) {
|
|
367
|
+
assertWorkspacePath(input.targetDirectory, "seed.workspace.importLocalFiles");
|
|
368
|
+
assertLocalSourcePaths(input.sourcePaths, "seed.workspace.importLocalFiles");
|
|
369
|
+
if (input.sourceRoot !== void 0) assertNonEmptyString(input.sourceRoot, "sourceRoot", "seed.workspace.importLocalFiles");
|
|
370
|
+
return request("seed.workspace.importLocalFiles", input, options);
|
|
371
|
+
},
|
|
372
|
+
subscribeChanged: (input, handler, options) => eventSource.subscribeBridgeEvent?.(WORKSPACE_CHANGED_METHOD, (event) => {
|
|
373
|
+
if (event.type !== WORKSPACE_CHANGED_METHOD || !isWorkspaceChangedPayload(event.payload)) return;
|
|
374
|
+
if (input.workspaceId && event.payload.workspaceId !== input.workspaceId) return;
|
|
375
|
+
handler(event.payload);
|
|
376
|
+
}, options, buildWorkspaceChangedSubscribeParams(input)) ?? Promise.resolve(eventSource.subscribe?.((event) => {
|
|
377
|
+
if (event.type !== WORKSPACE_CHANGED_METHOD || !isWorkspaceChangedPayload(event.payload)) return;
|
|
378
|
+
if (input.workspaceId && event.payload.workspaceId !== input.workspaceId) return;
|
|
379
|
+
handler(event.payload);
|
|
380
|
+
}) ?? (() => void 0)),
|
|
381
|
+
admin: {
|
|
382
|
+
async list(options) {
|
|
383
|
+
return request("seed.workspace.admin.list", void 0, options);
|
|
384
|
+
},
|
|
385
|
+
async create(input, options) {
|
|
386
|
+
assertNonEmptyString(input.name, "name", "seed.workspace.admin.create");
|
|
387
|
+
assertNonEmptyString(input.idempotencyKey, "idempotencyKey", "seed.workspace.admin.create");
|
|
388
|
+
if (input.ownershipScope === "tenant") assertNonEmptyString(input.tenantId ?? "", "tenantId", "seed.workspace.admin.create");
|
|
389
|
+
return request("seed.workspace.admin.create", input, options);
|
|
390
|
+
},
|
|
391
|
+
async update(input, options) {
|
|
392
|
+
assertNonEmptyString(input.workspaceId, "workspaceId", "seed.workspace.admin.update");
|
|
393
|
+
assertNonEmptyString(input.expectedVersion, "expectedVersion", "seed.workspace.admin.update");
|
|
394
|
+
if (input.name !== void 0) assertNonEmptyString(input.name, "name", "seed.workspace.admin.update");
|
|
395
|
+
if (input.name === void 0 && input.selectedDirectory === void 0 && input.status === void 0) throw createSeedAppError({
|
|
396
|
+
code: "invalid_payload_schema",
|
|
397
|
+
message: "seed.workspace.admin.update requires at least one update field",
|
|
398
|
+
requestId: "seed.workspace.admin.update"
|
|
399
|
+
});
|
|
400
|
+
return request("seed.workspace.admin.update", input, options);
|
|
401
|
+
},
|
|
402
|
+
async delete(input, options) {
|
|
403
|
+
assertNonEmptyString(input.workspaceId, "workspaceId", "seed.workspace.admin.delete");
|
|
404
|
+
assertNonEmptyString(input.expectedVersion, "expectedVersion", "seed.workspace.admin.delete");
|
|
405
|
+
assertNonEmptyString(input.idempotencyKey, "idempotencyKey", "seed.workspace.admin.delete");
|
|
406
|
+
await request("seed.workspace.admin.delete", input, options);
|
|
407
|
+
}
|
|
408
|
+
},
|
|
409
|
+
member: {
|
|
410
|
+
async assignableUsers(input, options) {
|
|
411
|
+
assertNonEmptyString(input.workspaceId, "workspaceId", "seed.workspace.member.assignableUsers");
|
|
412
|
+
return request("seed.workspace.member.assignableUsers", input, options);
|
|
413
|
+
},
|
|
414
|
+
async list(input, options) {
|
|
415
|
+
assertNonEmptyString(input.workspaceId, "workspaceId", "seed.workspace.member.list");
|
|
416
|
+
return request("seed.workspace.member.list", input, options);
|
|
417
|
+
},
|
|
418
|
+
async add(input, options) {
|
|
419
|
+
assertNonEmptyString(input.workspaceId, "workspaceId", "seed.workspace.member.add");
|
|
420
|
+
assertNonEmptyString(input.userId, "userId", "seed.workspace.member.add");
|
|
421
|
+
assertNonEmptyString(input.role, "role", "seed.workspace.member.add");
|
|
422
|
+
return request("seed.workspace.member.add", input, options);
|
|
423
|
+
},
|
|
424
|
+
async update(input, options) {
|
|
425
|
+
assertNonEmptyString(input.workspaceId, "workspaceId", "seed.workspace.member.update");
|
|
426
|
+
assertNonEmptyString(input.userId, "userId", "seed.workspace.member.update");
|
|
427
|
+
assertNonEmptyString(input.role, "role", "seed.workspace.member.update");
|
|
428
|
+
return request("seed.workspace.member.update", input, options);
|
|
429
|
+
},
|
|
430
|
+
async remove(input, options) {
|
|
431
|
+
assertNonEmptyString(input.workspaceId, "workspaceId", "seed.workspace.member.remove");
|
|
432
|
+
assertNonEmptyString(input.userId, "userId", "seed.workspace.member.remove");
|
|
433
|
+
await request("seed.workspace.member.remove", input, options);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
function buildWorkspaceChangedSubscribeParams(input) {
|
|
439
|
+
const params = {};
|
|
440
|
+
if (input.workspaceId) params.workspaceId = input.workspaceId;
|
|
441
|
+
if (input.cursor) params.cursor = input.cursor;
|
|
442
|
+
return Object.keys(params).length > 0 ? params : void 0;
|
|
443
|
+
}
|
|
444
|
+
function isWorkspaceChangedPayload(value) {
|
|
445
|
+
if (!value || typeof value !== "object") return false;
|
|
446
|
+
const record = value;
|
|
447
|
+
return typeof record.workspaceId === "string" && typeof record.uri === "string" && (record.operation === "created" || record.operation === "updated" || record.operation === "deleted" || record.operation === "moved" || record.operation === "patched") && (record.paths === void 0 || isStringArray(record.paths)) && (record.parentDirs === void 0 || isStringArray(record.parentDirs)) && (record.affectedUris === void 0 || isStringArray(record.affectedUris)) && (record.updatedAt === void 0 || typeof record.updatedAt === "string") && (record.occurredAt === void 0 || typeof record.occurredAt === "string") && (record.source === void 0 || typeof record.source === "string");
|
|
448
|
+
}
|
|
449
|
+
function isStringArray(value) {
|
|
450
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
451
|
+
}
|
|
452
|
+
function toWorkspaceWritePayload(input) {
|
|
453
|
+
assertWorkspaceLocation(input, "seed.workspace.write");
|
|
454
|
+
assertWorkspaceContent(input.content);
|
|
455
|
+
assertNonEmptyString(input.idempotencyKey, "idempotencyKey", "seed.workspace.write");
|
|
456
|
+
if (typeof input.content === "string") return {
|
|
457
|
+
...input,
|
|
458
|
+
content: input.content,
|
|
459
|
+
encoding: input.encoding ?? "utf8"
|
|
460
|
+
};
|
|
461
|
+
return {
|
|
462
|
+
...input,
|
|
463
|
+
content: encodeBytesBase64(input.content),
|
|
464
|
+
encoding: "base64"
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
function toWorkspaceUploadStagePayload(input) {
|
|
468
|
+
assertWorkspaceFileName(input.fileName, "fileName", "seed.workspace.upload.stage");
|
|
469
|
+
assertWorkspaceBinaryContent(input.content, "seed.workspace.upload.stage");
|
|
470
|
+
if (input.mimeType !== void 0) assertNonEmptyString(input.mimeType, "mimeType", "seed.workspace.upload.stage");
|
|
471
|
+
if (input.conversationId !== void 0) assertNonEmptyString(input.conversationId, "conversationId", "seed.workspace.upload.stage");
|
|
472
|
+
return {
|
|
473
|
+
...input,
|
|
474
|
+
content: encodeBytesBase64(input.content),
|
|
475
|
+
encoding: "base64"
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
function assertWorkspacePath(path, requestId) {
|
|
479
|
+
if (typeof path !== "string" || !path.startsWith("/") || path.includes("://")) throw createSeedAppError({
|
|
480
|
+
code: "invalid_payload_schema",
|
|
481
|
+
message: "Workspace path must be a Seed workspace absolute path.",
|
|
482
|
+
requestId
|
|
483
|
+
});
|
|
484
|
+
if (path.split("/").some((segment) => segment === "." || segment === "..")) throw createSeedAppError({
|
|
485
|
+
code: "invalid_payload_schema",
|
|
486
|
+
message: "Workspace path must not contain . or .. segments.",
|
|
487
|
+
requestId
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
function assertWorkspaceLocation(input, requestId) {
|
|
491
|
+
if ("uri" in input && input.uri !== void 0) {
|
|
492
|
+
assertWorkspaceUri(input.uri, requestId);
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
if ("path" in input && input.path !== void 0) {
|
|
496
|
+
assertWorkspacePath(input.path, requestId);
|
|
497
|
+
assertOptionalWorkspaceId(input.workspaceId, "workspaceId", requestId);
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
throw createSeedAppError({
|
|
501
|
+
code: "invalid_payload_schema",
|
|
502
|
+
message: "Workspace target must include path or workspace-fs URI.",
|
|
503
|
+
requestId
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
function assertOptionalWorkspaceLocation(input, requestId) {
|
|
507
|
+
if ("uri" in input && input.uri !== void 0) {
|
|
508
|
+
assertWorkspaceUri(input.uri, requestId);
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
if ("path" in input && input.path !== void 0) {
|
|
512
|
+
assertWorkspacePath(input.path, requestId);
|
|
513
|
+
assertOptionalWorkspaceId(input.workspaceId, "workspaceId", requestId);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
function assertWorkspaceUri(uri, requestId) {
|
|
517
|
+
if (typeof uri !== "string" || !/^workspace-fs:\/\/[^/?#]+\/[^?#]*$/.test(uri)) throw createSeedAppError({
|
|
518
|
+
code: "invalid_payload_schema",
|
|
519
|
+
message: "Workspace URI must be a workspace-fs URI.",
|
|
520
|
+
requestId
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
function assertWorkspaceContent(content) {
|
|
524
|
+
if (typeof content === "string" || content instanceof Uint8Array) return;
|
|
525
|
+
throw createSeedAppError({
|
|
526
|
+
code: "invalid_payload_schema",
|
|
527
|
+
message: "Workspace write content must be a string or Uint8Array.",
|
|
528
|
+
requestId: "seed.workspace.write"
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
function assertWorkspaceBinaryContent(content, requestId) {
|
|
532
|
+
if (content instanceof Uint8Array) return;
|
|
533
|
+
throw createSeedAppError({
|
|
534
|
+
code: "invalid_payload_schema",
|
|
535
|
+
message: "Workspace upload content must be a Uint8Array.",
|
|
536
|
+
requestId
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
function assertSearchNeedle(input) {
|
|
540
|
+
const hasQuery = typeof input.query === "string" && input.query.trim().length > 0;
|
|
541
|
+
const hasPattern = typeof input.pattern === "string" && input.pattern.trim().length > 0;
|
|
542
|
+
if (hasQuery || hasPattern) return;
|
|
543
|
+
throw createSeedAppError({
|
|
544
|
+
code: "invalid_payload_schema",
|
|
545
|
+
message: "Workspace search requires query or pattern.",
|
|
546
|
+
requestId: "seed.workspace.search"
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
function assertPositiveInteger(value, field, requestId) {
|
|
550
|
+
if (value === void 0) return;
|
|
551
|
+
if (Number.isInteger(value) && value > 0) return;
|
|
552
|
+
throw createSeedAppError({
|
|
553
|
+
code: "invalid_payload_schema",
|
|
554
|
+
message: `${field} must be a positive integer.`,
|
|
555
|
+
requestId
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
function assertNonEmptyString(value, field, requestId) {
|
|
559
|
+
if (typeof value === "string" && value.trim().length > 0) return;
|
|
560
|
+
throw createSeedAppError({
|
|
561
|
+
code: "invalid_payload_schema",
|
|
562
|
+
message: `${field} is required.`,
|
|
563
|
+
requestId
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
function assertWorkspaceFileName(value, field, requestId) {
|
|
567
|
+
assertNonEmptyString(value, field, requestId);
|
|
568
|
+
if (value.includes("/") || value.includes("\\") || value.includes("://") || value === "." || value === "..") throw createSeedAppError({
|
|
569
|
+
code: "invalid_payload_schema",
|
|
570
|
+
message: `${field} must be a single workspace file name.`,
|
|
571
|
+
requestId
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
function assertOptionalWorkspaceRelativePath(value, field, requestId) {
|
|
575
|
+
if (value === void 0) return;
|
|
576
|
+
assertNonEmptyString(value, field, requestId);
|
|
577
|
+
if (value.startsWith("/") || value.includes("\\") || value.includes("://")) throw createSeedAppError({
|
|
578
|
+
code: "invalid_payload_schema",
|
|
579
|
+
message: `${field} must be a relative workspace path.`,
|
|
580
|
+
requestId
|
|
581
|
+
});
|
|
582
|
+
if (value.split("/").some((segment) => segment === "" || segment === "." || segment === "..")) throw createSeedAppError({
|
|
583
|
+
code: "invalid_payload_schema",
|
|
584
|
+
message: `${field} must not contain empty, . or .. path segments.`,
|
|
585
|
+
requestId
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
function assertOptionalWorkspaceId(value, field, requestId) {
|
|
589
|
+
if (value === void 0) return;
|
|
590
|
+
if (typeof value === "string" && value.trim().length > 0 && !value.includes("/") && !value.includes("://")) return;
|
|
591
|
+
throw createSeedAppError({
|
|
592
|
+
code: "invalid_payload_schema",
|
|
593
|
+
message: `${field} must be a workspace id.`,
|
|
594
|
+
requestId
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
function assertLocalSourcePaths(value, requestId) {
|
|
598
|
+
if (!Array.isArray(value) || value.length === 0 || !value.every((path) => typeof path === "string" && path.trim().length > 0 && !path.includes("\0") && !path.toLowerCase().startsWith("workspace-fs://"))) throw createSeedAppError({
|
|
599
|
+
code: "invalid_payload_schema",
|
|
600
|
+
message: "sourcePaths must be non-empty local file paths.",
|
|
601
|
+
requestId
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
function encodeBytesBase64(bytes) {
|
|
605
|
+
const maybeBuffer = globalThis.Buffer;
|
|
606
|
+
if (maybeBuffer) return maybeBuffer.from(bytes).toString("base64");
|
|
607
|
+
const btoaFn = globalThis.btoa;
|
|
608
|
+
if (!btoaFn) throw createSeedAppError({
|
|
609
|
+
code: "invalid_payload_schema",
|
|
610
|
+
message: "Workspace write binary content requires base64 encoding support.",
|
|
611
|
+
requestId: "seed.workspace.write"
|
|
612
|
+
});
|
|
613
|
+
let binary = "";
|
|
614
|
+
bytes.forEach((byte) => {
|
|
615
|
+
binary += String.fromCharCode(byte);
|
|
616
|
+
});
|
|
617
|
+
return btoaFn(binary);
|
|
618
|
+
}
|
|
619
|
+
//#endregion
|
|
620
|
+
//#region src/client.ts
|
|
621
|
+
const SEED_EVENT_SUBSCRIBE_METHOD = "seed.event.subscribe";
|
|
622
|
+
const SEED_EVENT_UNSUBSCRIBE_METHOD = "seed.event.unsubscribe";
|
|
623
|
+
const SEED_AGENT_STREAM_EVENT_TYPE = "seed.agent.stream";
|
|
624
|
+
const AGENT_APPROVAL_CHANGED_METHOD = "seed.agent.approval.changed";
|
|
625
|
+
const CONVERSATION_CHANGED_METHOD = "seed.conversation.changed";
|
|
626
|
+
const REMOTE_PC_PRESENCE_CHANGED_METHOD = "seed.remotePc.presence.changed";
|
|
627
|
+
const EMPLOYEE_CHANGED_METHOD = "seed.employee.changed";
|
|
628
|
+
const EMPLOYEE_MEMORY_EVENTS_CHANGED_METHOD = "seed.employee.memory.events.changed";
|
|
629
|
+
const ROLE_MAKER_SESSION_CHANGED_METHOD = "seed.roleMaker.session.changed";
|
|
630
|
+
const ROLE_TEMPLATE_CHANGED_METHOD = "seed.roleTemplate.changed";
|
|
631
|
+
const TEAM_CHANGED_METHOD = "seed.team.changed";
|
|
632
|
+
const TEAM_AGENT_SPAWNED_METHOD = "seed.team.agent.spawned";
|
|
633
|
+
const TEAM_AGENT_REMOVED_METHOD = "seed.team.agent.removed";
|
|
634
|
+
const TEAM_AGENT_RENAMED_METHOD = "seed.team.agent.renamed";
|
|
635
|
+
const TEAM_AGENT_STATUS_CHANGED_METHOD = "seed.team.agent.statusChanged";
|
|
636
|
+
function markAgentStreamReplayEvent(event) {
|
|
637
|
+
if (event.payload.replay === true) return event;
|
|
638
|
+
return {
|
|
639
|
+
...event,
|
|
640
|
+
payload: {
|
|
641
|
+
...event.payload,
|
|
642
|
+
replay: true
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
function mergeAgentStreamDuplicateMetadata(target, source) {
|
|
647
|
+
const sourceCursor = source.cursor ?? source.payload.cursor;
|
|
648
|
+
if (sourceCursor) {
|
|
649
|
+
target.cursor = sourceCursor;
|
|
650
|
+
target.payload.cursor = sourceCursor;
|
|
651
|
+
}
|
|
652
|
+
if (!target.id && source.id) target.id = source.id;
|
|
653
|
+
if (!target.payload.eventId && source.payload.eventId) target.payload.eventId = source.payload.eventId;
|
|
654
|
+
if (source.payload.replay === true) target.payload.replay = true;
|
|
655
|
+
}
|
|
656
|
+
function createSeedAppClient(options) {
|
|
657
|
+
const defaultTransport = options.transport;
|
|
658
|
+
const routeTransports = options.routeTransports ?? {};
|
|
659
|
+
let cachedRoutes;
|
|
660
|
+
const findCachedRoute = (method) => cachedRoutes?.find((route) => route.method === method);
|
|
661
|
+
const resolveConfiguredRouteTransport = (route) => {
|
|
662
|
+
if (!route) return void 0;
|
|
663
|
+
return routeTransports[route.implementation] ?? routeTransports[route.commandTransport];
|
|
664
|
+
};
|
|
665
|
+
const resolveTransportForMethod = (method) => {
|
|
666
|
+
const cachedRoute = findCachedRoute(method);
|
|
667
|
+
const metadata = (0, _seed_app_studio_protocol.getSeedBridgeRouteMetadataForMethod)(method);
|
|
668
|
+
if (cachedRoutes && !cachedRoute && metadata?.fallback === "none") throw createSeedAppError({
|
|
669
|
+
code: "unsupported_runtime",
|
|
670
|
+
message: `Seed App route ${method} is not available in host diagnostics.`,
|
|
671
|
+
requestId: method,
|
|
672
|
+
capability: metadata.capability
|
|
673
|
+
});
|
|
674
|
+
const route = cachedRoute ?? metadata;
|
|
675
|
+
if (!route) return defaultTransport;
|
|
676
|
+
const transport = resolveConfiguredRouteTransport(route);
|
|
677
|
+
if (transport) return transport;
|
|
678
|
+
return defaultTransport;
|
|
679
|
+
};
|
|
680
|
+
const cacheDiagnosticsRoutes = (diagnostics) => {
|
|
681
|
+
if (diagnostics.routes) cachedRoutes = [...diagnostics.routes];
|
|
682
|
+
};
|
|
683
|
+
const snapshotRoutes = async (requestOptions) => {
|
|
684
|
+
const diagnostics = await defaultTransport.request("seed.bridge.diagnostics", { includeRoutes: true }, requestOptions);
|
|
685
|
+
cacheDiagnosticsRoutes(diagnostics);
|
|
686
|
+
cachedRoutes = diagnostics.routes ? [...diagnostics.routes] : [];
|
|
687
|
+
return [...cachedRoutes];
|
|
688
|
+
};
|
|
689
|
+
const ensureRoutesBeforeFallbackNoneMethod = async (method, requestOptions) => {
|
|
690
|
+
const metadata = (0, _seed_app_studio_protocol.getSeedBridgeRouteMetadataForMethod)(method);
|
|
691
|
+
if (!cachedRoutes && metadata?.fallback === "none") await snapshotRoutes(requestOptions);
|
|
692
|
+
};
|
|
693
|
+
const resolveTransportForMethodAsync = async (method, requestOptions) => {
|
|
694
|
+
await ensureRoutesBeforeFallbackNoneMethod(method, requestOptions);
|
|
695
|
+
return resolveTransportForMethod(method);
|
|
696
|
+
};
|
|
697
|
+
const subscribeCanonicalBridgeEvent = async (event, handler, requestOptions) => {
|
|
698
|
+
const transport = await resolveTransportForMethodAsync(SEED_EVENT_SUBSCRIBE_METHOD, requestOptions);
|
|
699
|
+
if (!transport.subscribeBridgeEvent) return Promise.reject(createSeedAppError({
|
|
700
|
+
code: "unsupported_runtime",
|
|
701
|
+
message: `Seed App runtime does not support canonical bridge event subscriptions for ${event}.`,
|
|
702
|
+
requestId: SEED_EVENT_SUBSCRIBE_METHOD,
|
|
703
|
+
capability: "event.subscribe"
|
|
704
|
+
}));
|
|
705
|
+
return transport.subscribeBridgeEvent(event, handler, requestOptions);
|
|
706
|
+
};
|
|
707
|
+
const subscribeTypedBridgeEvent = (eventName, isPayload, handler, requestOptions, shouldEmit) => subscribeCanonicalBridgeEvent(eventName, (event) => {
|
|
708
|
+
if (event.type !== eventName || !isPayload(event.payload)) return;
|
|
709
|
+
if (shouldEmit && !shouldEmit(event.payload)) return;
|
|
710
|
+
handler(event.payload);
|
|
711
|
+
}, requestOptions);
|
|
712
|
+
const request = async (method, params, requestOptions) => {
|
|
713
|
+
const response = await (method === "seed.bridge.diagnostics" ? defaultTransport : await resolveTransportForMethodAsync(method, requestOptions)).request(method, method === "seed.agent.run" ? ensureSeedAppTraceCarrier(params) : params, requestOptions);
|
|
714
|
+
if (method === "seed.bridge.diagnostics") cacheDiagnosticsRoutes(response);
|
|
715
|
+
return response;
|
|
716
|
+
};
|
|
717
|
+
const requestEventStream = (method, params, requestOptions) => resolveTransportForMethodAsync(method, requestOptions).then((transport) => transport.request(method, method === SEED_EVENT_SUBSCRIBE_METHOD ? ensureSeedAppTraceCarrier(params) : params, requestOptions));
|
|
718
|
+
const getCachedRoutes = () => cachedRoutes ? [...cachedRoutes] : void 0;
|
|
719
|
+
const resolveRoute = async (method, requestOptions) => {
|
|
720
|
+
return (cachedRoutes ?? await snapshotRoutes(requestOptions)).find((route) => route.method === method);
|
|
721
|
+
};
|
|
722
|
+
const subscribeAgentStream = (handler, subscribeOptions) => resolveTransportForMethod(SEED_EVENT_SUBSCRIBE_METHOD).subscribe?.((event) => {
|
|
723
|
+
if (!(0, _seed_app_studio_protocol.isSeedAppAgentStreamEvent)(event)) return;
|
|
724
|
+
if (subscribeOptions?.conversationId && event.payload.conversationId !== subscribeOptions.conversationId) return;
|
|
725
|
+
if (subscribeOptions?.runId && event.payload.runId !== subscribeOptions.runId) return;
|
|
726
|
+
handler(event);
|
|
727
|
+
}) ?? (() => void 0);
|
|
728
|
+
const subscribeScopedAgentStream = async (input, handler, subscribeOptions) => {
|
|
729
|
+
assertAgentStreamSubscribeScope(input);
|
|
730
|
+
const emittedEventsById = /* @__PURE__ */ new Map();
|
|
731
|
+
const liveBuffer = [];
|
|
732
|
+
let snapshotResolved = false;
|
|
733
|
+
let closed = false;
|
|
734
|
+
let subscriptionId;
|
|
735
|
+
const emitOnce = (event) => {
|
|
736
|
+
if (!matchesAgentStreamScope(event, input)) return;
|
|
737
|
+
const eventId = (0, _seed_app_studio_protocol.resolveSeedAppAgentStreamEventIdentity)(event);
|
|
738
|
+
const previousEvent = emittedEventsById.get(eventId);
|
|
739
|
+
if (previousEvent) {
|
|
740
|
+
mergeAgentStreamDuplicateMetadata(previousEvent, event);
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
emittedEventsById.set(eventId, event);
|
|
744
|
+
handler(event);
|
|
745
|
+
};
|
|
746
|
+
await ensureRoutesBeforeFallbackNoneMethod(SEED_EVENT_SUBSCRIBE_METHOD, { timeoutMs: subscribeOptions?.requestTimeoutMs });
|
|
747
|
+
const unsubscribeLive = subscribeAgentStream((event) => {
|
|
748
|
+
if (!matchesAgentStreamScope(event, input)) return;
|
|
749
|
+
if (!snapshotResolved) {
|
|
750
|
+
liveBuffer.push(event);
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
emitOnce(event);
|
|
754
|
+
});
|
|
755
|
+
try {
|
|
756
|
+
const snapshot = await requestEventStream(SEED_EVENT_SUBSCRIBE_METHOD, {
|
|
757
|
+
type: SEED_AGENT_STREAM_EVENT_TYPE,
|
|
758
|
+
...input
|
|
759
|
+
}, { timeoutMs: subscribeOptions?.requestTimeoutMs });
|
|
760
|
+
subscriptionId = snapshot.subscriptionId;
|
|
761
|
+
snapshot.events.map(markAgentStreamReplayEvent).forEach(emitOnce);
|
|
762
|
+
snapshotResolved = true;
|
|
763
|
+
liveBuffer.splice(0).forEach(emitOnce);
|
|
764
|
+
} catch (error) {
|
|
765
|
+
unsubscribeLive();
|
|
766
|
+
if (subscriptionId) await requestEventStream(SEED_EVENT_UNSUBSCRIBE_METHOD, {
|
|
767
|
+
type: SEED_AGENT_STREAM_EVENT_TYPE,
|
|
768
|
+
subscriptionId
|
|
769
|
+
}).catch(() => void 0);
|
|
770
|
+
throw error;
|
|
771
|
+
}
|
|
772
|
+
return async () => {
|
|
773
|
+
if (closed) return;
|
|
774
|
+
closed = true;
|
|
775
|
+
unsubscribeLive();
|
|
776
|
+
if (!subscriptionId) return;
|
|
777
|
+
await requestEventStream(SEED_EVENT_UNSUBSCRIBE_METHOD, {
|
|
778
|
+
type: SEED_AGENT_STREAM_EVENT_TYPE,
|
|
779
|
+
subscriptionId
|
|
780
|
+
}, { timeoutMs: subscribeOptions?.requestTimeoutMs });
|
|
781
|
+
};
|
|
782
|
+
};
|
|
783
|
+
const subscribeAguiStream = (handler, subscribeOptions) => resolveTransportForMethod(SEED_EVENT_SUBSCRIBE_METHOD).subscribe?.((event) => {
|
|
784
|
+
if (!(0, _seed_app_studio_protocol.isSeedAguiEvent)(event)) return;
|
|
785
|
+
if (subscribeOptions?.conversationId && event.seed.conversationId !== subscribeOptions.conversationId) return;
|
|
786
|
+
if (subscribeOptions?.runId && event.runId !== subscribeOptions.runId) return;
|
|
787
|
+
handler(event);
|
|
788
|
+
}) ?? (() => void 0);
|
|
789
|
+
const subscribeScopedAguiStream = async (input, handler, subscribeOptions) => {
|
|
790
|
+
assertAguiStreamSubscribeScope(input);
|
|
791
|
+
const seenEventIds = /* @__PURE__ */ new Set();
|
|
792
|
+
const liveBuffer = [];
|
|
793
|
+
let snapshotResolved = false;
|
|
794
|
+
let closed = false;
|
|
795
|
+
let subscriptionId;
|
|
796
|
+
const emitOnce = (event) => {
|
|
797
|
+
if (!(0, _seed_app_studio_protocol.matchesSeedAguiStreamScope)(event, input)) return;
|
|
798
|
+
const eventId = (0, _seed_app_studio_protocol.resolveSeedAguiEventIdentity)(event);
|
|
799
|
+
if (seenEventIds.has(eventId)) return;
|
|
800
|
+
seenEventIds.add(eventId);
|
|
801
|
+
handler(event);
|
|
802
|
+
};
|
|
803
|
+
await ensureRoutesBeforeFallbackNoneMethod(SEED_EVENT_SUBSCRIBE_METHOD, { timeoutMs: subscribeOptions?.requestTimeoutMs });
|
|
804
|
+
const unsubscribeLive = subscribeAguiStream((event) => {
|
|
805
|
+
if (!(0, _seed_app_studio_protocol.matchesSeedAguiStreamScope)(event, input)) return;
|
|
806
|
+
if (!snapshotResolved) {
|
|
807
|
+
liveBuffer.push(event);
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
emitOnce(event);
|
|
811
|
+
});
|
|
812
|
+
try {
|
|
813
|
+
const snapshot = await requestEventStream(SEED_EVENT_SUBSCRIBE_METHOD, {
|
|
814
|
+
type: _seed_app_studio_protocol.SEED_AGUI_STREAM_EVENT_TYPE,
|
|
815
|
+
...input
|
|
816
|
+
}, { timeoutMs: subscribeOptions?.requestTimeoutMs });
|
|
817
|
+
subscriptionId = snapshot.subscriptionId;
|
|
818
|
+
snapshot.events.forEach(emitOnce);
|
|
819
|
+
snapshotResolved = true;
|
|
820
|
+
liveBuffer.splice(0).forEach(emitOnce);
|
|
821
|
+
} catch (error) {
|
|
822
|
+
unsubscribeLive();
|
|
823
|
+
if (subscriptionId) await requestEventStream(SEED_EVENT_UNSUBSCRIBE_METHOD, {
|
|
824
|
+
type: _seed_app_studio_protocol.SEED_AGUI_STREAM_EVENT_TYPE,
|
|
825
|
+
subscriptionId
|
|
826
|
+
}).catch(() => void 0);
|
|
827
|
+
throw error;
|
|
828
|
+
}
|
|
829
|
+
return async () => {
|
|
830
|
+
if (closed) return;
|
|
831
|
+
closed = true;
|
|
832
|
+
unsubscribeLive();
|
|
833
|
+
if (!subscriptionId) return;
|
|
834
|
+
await requestEventStream(SEED_EVENT_UNSUBSCRIBE_METHOD, {
|
|
835
|
+
type: _seed_app_studio_protocol.SEED_AGUI_STREAM_EVENT_TYPE,
|
|
836
|
+
subscriptionId
|
|
837
|
+
}, { timeoutMs: subscribeOptions?.requestTimeoutMs });
|
|
838
|
+
};
|
|
839
|
+
};
|
|
840
|
+
const runAgent = (input, requestOptions) => request("seed.agent.run", input, requestOptions);
|
|
841
|
+
const runAndWatchAgent = async (input, handlers, watchOptions) => {
|
|
842
|
+
const tracedInput = ensureSeedAppTraceCarrier(input);
|
|
843
|
+
const traceContext = extractSeedAppTraceContext(tracedInput);
|
|
844
|
+
let activeRunId;
|
|
845
|
+
let activeRun;
|
|
846
|
+
const pendingEvents = [];
|
|
847
|
+
let finish;
|
|
848
|
+
let fail;
|
|
849
|
+
let streamTimeout;
|
|
850
|
+
let terminalDispatched = false;
|
|
851
|
+
let terminalResult;
|
|
852
|
+
let terminalError;
|
|
853
|
+
let acceptanceHandlerCompleted = false;
|
|
854
|
+
const completeRun = (result) => {
|
|
855
|
+
if (finish) {
|
|
856
|
+
finish(result);
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
terminalResult = result;
|
|
860
|
+
};
|
|
861
|
+
const rejectRun = (error) => {
|
|
862
|
+
if (fail) {
|
|
863
|
+
fail(error);
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
terminalError = error;
|
|
867
|
+
};
|
|
868
|
+
const handleEvent = (event) => {
|
|
869
|
+
if (!activeRunId || !acceptanceHandlerCompleted) {
|
|
870
|
+
pendingEvents.push(event);
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
if (event.payload.runId !== activeRunId) return;
|
|
874
|
+
const isTerminalEvent = event.type === "agent.run.completed" || event.type === "agent.turn.completed" || event.type === "agent.run.failed" || event.type === "agent.run.cancelled";
|
|
875
|
+
const isDuplicateTerminal = isTerminalEvent && terminalDispatched;
|
|
876
|
+
dispatchAgentStreamEvent(event, handlers, { suppressTerminalHandler: isDuplicateTerminal });
|
|
877
|
+
if (isDuplicateTerminal) return;
|
|
878
|
+
if (isTerminalEvent) terminalDispatched = true;
|
|
879
|
+
if (event.type === "agent.run.completed" || event.type === "agent.turn.completed") completeRun({
|
|
880
|
+
...activeRun,
|
|
881
|
+
runId: activeRunId,
|
|
882
|
+
status: "completed",
|
|
883
|
+
output: event.payload
|
|
884
|
+
});
|
|
885
|
+
if (event.type === "agent.run.failed") rejectRun(createSeedAppError({
|
|
886
|
+
code: "provider_failed",
|
|
887
|
+
message: event.payload.message ?? "Agent run failed.",
|
|
888
|
+
requestId: activeRunId
|
|
889
|
+
}));
|
|
890
|
+
if (event.type === "agent.run.cancelled") rejectRun(createSeedAppError({
|
|
891
|
+
code: "provider_failed",
|
|
892
|
+
message: event.payload.reason ?? "Agent run cancelled.",
|
|
893
|
+
requestId: activeRunId
|
|
894
|
+
}));
|
|
895
|
+
};
|
|
896
|
+
let unsubscribe = () => void 0;
|
|
897
|
+
try {
|
|
898
|
+
if (input.conversationId) unsubscribe = await subscribeScopedAgentStream(traceContext ? injectSeedAppTraceContext({ conversationId: input.conversationId }, traceContext) : { conversationId: input.conversationId }, handleEvent, { requestTimeoutMs: watchOptions?.requestTimeoutMs });
|
|
899
|
+
const run = await runAgent(tracedInput, { timeoutMs: watchOptions?.requestTimeoutMs });
|
|
900
|
+
activeRun = run;
|
|
901
|
+
activeRunId = run.runId;
|
|
902
|
+
await handlers?.onAccepted?.(run);
|
|
903
|
+
acceptanceHandlerCompleted = true;
|
|
904
|
+
if (!input.conversationId) unsubscribe = await subscribeScopedAgentStream(traceContext ? injectSeedAppTraceContext({ runId: run.runId }, traceContext) : { runId: run.runId }, handleEvent, { requestTimeoutMs: watchOptions?.requestTimeoutMs });
|
|
905
|
+
if (run.status === "completed") {
|
|
906
|
+
pendingEvents.splice(0).forEach(handleEvent);
|
|
907
|
+
if (terminalError) throw terminalError;
|
|
908
|
+
return terminalResult ?? run;
|
|
909
|
+
}
|
|
910
|
+
return await new Promise((resolve, reject) => {
|
|
911
|
+
finish = resolve;
|
|
912
|
+
fail = reject;
|
|
913
|
+
pendingEvents.splice(0).forEach(handleEvent);
|
|
914
|
+
if (terminalError) {
|
|
915
|
+
reject(terminalError);
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
if (terminalResult) {
|
|
919
|
+
resolve(terminalResult);
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
if (watchOptions?.streamTimeoutMs !== void 0) streamTimeout = setTimeout(() => {
|
|
923
|
+
reject(createSeedAppError({
|
|
924
|
+
code: "handshake_timeout",
|
|
925
|
+
message: "Agent stream timed out before a terminal event.",
|
|
926
|
+
requestId: run.runId,
|
|
927
|
+
retryable: true
|
|
928
|
+
}));
|
|
929
|
+
}, watchOptions.streamTimeoutMs);
|
|
930
|
+
});
|
|
931
|
+
} finally {
|
|
932
|
+
if (streamTimeout) clearTimeout(streamTimeout);
|
|
933
|
+
await unsubscribe();
|
|
934
|
+
}
|
|
935
|
+
};
|
|
936
|
+
return {
|
|
937
|
+
handshake: (input, requestOptions) => request("seed.handshake", input, requestOptions),
|
|
938
|
+
capabilities: {
|
|
939
|
+
query: (requestOptions) => request("seed.capabilities.query", void 0, requestOptions),
|
|
940
|
+
request: (input, requestOptions) => request("seed.capabilities.request", input, requestOptions)
|
|
941
|
+
},
|
|
942
|
+
auth: {
|
|
943
|
+
getContext: (requestOptions) => request("seed.auth.getContext", void 0, requestOptions),
|
|
944
|
+
getAccessToken: (input, requestOptions) => request("seed.auth.getAccessToken", input, requestOptions)
|
|
945
|
+
},
|
|
946
|
+
network: { fetch: (input, requestOptions) => request("seed.network.fetch", input, requestOptions) },
|
|
947
|
+
agent: {
|
|
948
|
+
list: (input, requestOptions) => request("seed.agent.list", input, requestOptions),
|
|
949
|
+
preset: { update: (input, requestOptions) => request("seed.agent.preset.update", input, requestOptions) },
|
|
950
|
+
config: {
|
|
951
|
+
upsert: (input, requestOptions) => request("seed.agent.config.upsert", input, requestOptions),
|
|
952
|
+
delete: (input, requestOptions) => request("seed.agent.config.delete", input, requestOptions)
|
|
953
|
+
},
|
|
954
|
+
runtime: {
|
|
955
|
+
configOptions: { get: (input, requestOptions) => request("seed.agent.runtime.configOptions.get", input, requestOptions) },
|
|
956
|
+
configOption: { set: (input, requestOptions) => request("seed.agent.runtime.configOption.set", input, requestOptions) },
|
|
957
|
+
model: {
|
|
958
|
+
get: (input, requestOptions) => request("seed.agent.runtime.model.get", input, requestOptions),
|
|
959
|
+
set: (input, requestOptions) => request("seed.agent.runtime.model.set", input, requestOptions)
|
|
960
|
+
},
|
|
961
|
+
mode: {
|
|
962
|
+
get: (input, requestOptions) => request("seed.agent.runtime.mode.get", input, requestOptions),
|
|
963
|
+
set: (input, requestOptions) => request("seed.agent.runtime.mode.set", input, requestOptions)
|
|
964
|
+
},
|
|
965
|
+
bundled: {
|
|
966
|
+
get: (input, requestOptions) => request("seed.agent.runtime.bundled.get", input, requestOptions),
|
|
967
|
+
update: (input, requestOptions) => request("seed.agent.runtime.bundled.update", input, requestOptions)
|
|
968
|
+
},
|
|
969
|
+
local: {
|
|
970
|
+
status: { get: (input, requestOptions) => request("seed.agent.runtime.local.status.get", input, requestOptions) },
|
|
971
|
+
restart: (input, requestOptions) => request("seed.agent.runtime.local.restart", input, requestOptions),
|
|
972
|
+
stale: { mark: (input, requestOptions) => request("seed.agent.runtime.local.stale.mark", input, requestOptions) }
|
|
973
|
+
},
|
|
974
|
+
capabilities: { prefetch: (input, requestOptions) => request("seed.agent.runtime.capabilities.prefetch", input, requestOptions) },
|
|
975
|
+
custom: { test: (input, requestOptions) => request("seed.agent.runtime.custom.test", input, requestOptions) },
|
|
976
|
+
slashCommands: { list: (input, requestOptions) => request("seed.agent.runtime.slashCommands.list", input, requestOptions) },
|
|
977
|
+
slashCommand: { execute: (input, requestOptions) => request("seed.agent.runtime.slashCommand.execute", input, requestOptions) },
|
|
978
|
+
session: { ensure: (input, requestOptions) => request("seed.agent.runtime.session.ensure", input, requestOptions) },
|
|
979
|
+
conversation: { warmup: (input, requestOptions) => request("seed.agent.runtime.conversation.warmup", input, requestOptions) },
|
|
980
|
+
openclaw: { get: (input, requestOptions) => request("seed.agent.runtime.openclaw.get", input, requestOptions) },
|
|
981
|
+
process: {
|
|
982
|
+
snapshot: { get: (input, requestOptions) => request("seed.agent.runtime.process.snapshot.get", input, requestOptions) },
|
|
983
|
+
release: (input, requestOptions) => request("seed.agent.runtime.process.release", input, requestOptions)
|
|
984
|
+
}
|
|
985
|
+
},
|
|
986
|
+
subagent: { session: {
|
|
987
|
+
snapshot: { get: (input, requestOptions) => request("seed.agent.subagent.session.snapshot.get", input, requestOptions) },
|
|
988
|
+
load: (input, requestOptions) => request("seed.agent.subagent.session.load", input, requestOptions)
|
|
989
|
+
} },
|
|
990
|
+
run: runAgent,
|
|
991
|
+
input: { submit: (input, requestOptions) => request("seed.agent.input.submit", input, requestOptions) },
|
|
992
|
+
stop: (input, requestOptions) => request("seed.agent.stop", input, requestOptions),
|
|
993
|
+
sideQuestion: { ask: (input, requestOptions) => request("seed.agent.sideQuestion.ask", input, requestOptions) },
|
|
994
|
+
approval: {
|
|
995
|
+
check: (input, requestOptions) => request("seed.agent.approval.check", input, requestOptions),
|
|
996
|
+
list: (input, requestOptions) => request("seed.agent.approval.list", input, requestOptions),
|
|
997
|
+
respond: (input, requestOptions) => request("seed.agent.approval.respond", input, requestOptions),
|
|
998
|
+
subscribeChanged: (handler, requestOptions) => subscribeCanonicalBridgeEvent(AGENT_APPROVAL_CHANGED_METHOD, (event) => {
|
|
999
|
+
if (event.type !== AGENT_APPROVAL_CHANGED_METHOD || !isAgentApprovalChangedPayload(event.payload)) return;
|
|
1000
|
+
handler(event.payload);
|
|
1001
|
+
}, requestOptions)
|
|
1002
|
+
},
|
|
1003
|
+
elicitation: { respond: (input, requestOptions) => request("seed.agent.elicitation.respond", input, requestOptions) },
|
|
1004
|
+
subscribe: subscribeAgentStream,
|
|
1005
|
+
subscribeConversation: (input, handler, subscribeOptions) => subscribeScopedAgentStream(input, handler, subscribeOptions),
|
|
1006
|
+
subscribeRun: (input, handler, subscribeOptions) => subscribeScopedAgentStream(input, handler, subscribeOptions),
|
|
1007
|
+
runAndWatch: runAndWatchAgent
|
|
1008
|
+
},
|
|
1009
|
+
agui: {
|
|
1010
|
+
subscribe: subscribeAguiStream,
|
|
1011
|
+
subscribeConversation: (input, handler, subscribeOptions) => subscribeScopedAguiStream(input, handler, subscribeOptions),
|
|
1012
|
+
subscribeRun: (input, handler, subscribeOptions) => subscribeScopedAguiStream(input, handler, subscribeOptions)
|
|
1013
|
+
},
|
|
1014
|
+
team: {
|
|
1015
|
+
list: (input, requestOptions) => request("seed.team.list", input ?? {}, requestOptions),
|
|
1016
|
+
create: (input, requestOptions) => request("seed.team.create", input, requestOptions),
|
|
1017
|
+
subscribeChanged: (handler, requestOptions) => subscribeTypedBridgeEvent(TEAM_CHANGED_METHOD, isTeamChangedPayload, handler, requestOptions),
|
|
1018
|
+
get: (input, requestOptions) => request("seed.team.get", input, requestOptions),
|
|
1019
|
+
rename: (input, requestOptions) => request("seed.team.rename", input, requestOptions),
|
|
1020
|
+
sessionMode: { set: (input, requestOptions) => request("seed.team.sessionMode.set", input, requestOptions) },
|
|
1021
|
+
workspace: { update: (input, requestOptions) => request("seed.team.workspace.update", input, requestOptions) },
|
|
1022
|
+
delete: (input, requestOptions) => request("seed.team.delete", input, requestOptions),
|
|
1023
|
+
agent: {
|
|
1024
|
+
add: (input, requestOptions) => request("seed.team.agent.add", input, requestOptions),
|
|
1025
|
+
subscribeSpawned: (handler, requestOptions) => subscribeTypedBridgeEvent(TEAM_AGENT_SPAWNED_METHOD, isTeamAgentSpawnedPayload, handler, requestOptions),
|
|
1026
|
+
subscribeRemoved: (handler, requestOptions) => subscribeTypedBridgeEvent(TEAM_AGENT_REMOVED_METHOD, isTeamAgentRemovedPayload, handler, requestOptions),
|
|
1027
|
+
subscribeRenamed: (handler, requestOptions) => subscribeTypedBridgeEvent(TEAM_AGENT_RENAMED_METHOD, isTeamAgentRenamedPayload, handler, requestOptions),
|
|
1028
|
+
subscribeStatusChanged: (handler, requestOptions) => subscribeTypedBridgeEvent(TEAM_AGENT_STATUS_CHANGED_METHOD, isTeamAgentStatusChangedPayload, handler, requestOptions),
|
|
1029
|
+
rename: (input, requestOptions) => request("seed.team.agent.rename", input, requestOptions),
|
|
1030
|
+
remove: (input, requestOptions) => request("seed.team.agent.remove", input, requestOptions)
|
|
1031
|
+
},
|
|
1032
|
+
message: {
|
|
1033
|
+
send: (input, requestOptions) => request("seed.team.message.send", input, requestOptions),
|
|
1034
|
+
sendToAgent: (input, requestOptions) => request("seed.team.message.sendToAgent", input, requestOptions)
|
|
1035
|
+
},
|
|
1036
|
+
stop: (input, requestOptions) => request("seed.team.stop", input, requestOptions),
|
|
1037
|
+
session: {
|
|
1038
|
+
ensure: (input, requestOptions) => request("seed.team.session.ensure", input, requestOptions),
|
|
1039
|
+
reset: (input, requestOptions) => request("seed.team.session.reset", input, requestOptions)
|
|
1040
|
+
}
|
|
1041
|
+
},
|
|
1042
|
+
speech: { transcribe: (input, requestOptions) => request("seed.speech.transcribe", input, requestOptions) },
|
|
1043
|
+
conversation: {
|
|
1044
|
+
list: (input, requestOptions) => request("seed.conversation.list", input, requestOptions),
|
|
1045
|
+
listByCronJob: (input, requestOptions) => request("seed.conversation.listByCronJob", input, requestOptions),
|
|
1046
|
+
subscribeChanged: (handler, requestOptions) => subscribeTypedBridgeEvent(CONVERSATION_CHANGED_METHOD, isConversationChangedPayload, handler, requestOptions),
|
|
1047
|
+
create: (input, requestOptions) => request("seed.conversation.create", input, requestOptions),
|
|
1048
|
+
get: (input, requestOptions) => request("seed.conversation.get", input, requestOptions),
|
|
1049
|
+
messages: (input, requestOptions) => request("seed.conversation.messages", input, requestOptions),
|
|
1050
|
+
searchMessages: (input, requestOptions) => request("seed.conversation.searchMessages", input, requestOptions),
|
|
1051
|
+
rename: (input, requestOptions) => request("seed.conversation.rename", input, requestOptions),
|
|
1052
|
+
updateMetadata: (input, requestOptions) => request("seed.conversation.metadata.update", input, requestOptions),
|
|
1053
|
+
updateModel: (input, requestOptions) => request("seed.conversation.model.update", input, requestOptions),
|
|
1054
|
+
delete: (input, requestOptions) => request("seed.conversation.delete", input, requestOptions),
|
|
1055
|
+
importArchive: (input, requestOptions) => request("seed.conversation.archive.import", input, requestOptions),
|
|
1056
|
+
artifact: {
|
|
1057
|
+
list: (input, requestOptions) => request("seed.conversation.artifact.list", input, requestOptions),
|
|
1058
|
+
listEvidence: (input, requestOptions) => request("seed.conversation.artifact.evidence.list", input, requestOptions),
|
|
1059
|
+
listAccess: (input, requestOptions) => request("seed.conversation.artifact.access.list", input, requestOptions),
|
|
1060
|
+
listShareTargets: (requestOptions) => request("seed.conversation.artifact.shareTargets.list", void 0, requestOptions),
|
|
1061
|
+
share: (input, requestOptions) => request("seed.conversation.artifact.share", input, requestOptions),
|
|
1062
|
+
revokeAccess: (input, requestOptions) => request("seed.conversation.artifact.access.revoke", input, requestOptions),
|
|
1063
|
+
materialize: (input, requestOptions) => request("seed.conversation.artifact.materialize", input, requestOptions),
|
|
1064
|
+
updatePolicy: (input, requestOptions) => request("seed.conversation.artifact.policy.update", input, requestOptions),
|
|
1065
|
+
dryRunScrub: (input, requestOptions) => request("seed.conversation.artifact.scrub.dryRun", input, requestOptions),
|
|
1066
|
+
applyScrub: (input, requestOptions) => request("seed.conversation.artifact.scrub.apply", input, requestOptions)
|
|
1067
|
+
}
|
|
1068
|
+
},
|
|
1069
|
+
remotePc: {
|
|
1070
|
+
presence: {
|
|
1071
|
+
list: (requestOptions) => request("seed.remotePc.presence.list", void 0, requestOptions),
|
|
1072
|
+
subscribeChanged: (handler, requestOptions) => subscribeTypedBridgeEvent(REMOTE_PC_PRESENCE_CHANGED_METHOD, isRemotePcPresenceChangedPayload, handler, requestOptions)
|
|
1073
|
+
},
|
|
1074
|
+
vibe: {
|
|
1075
|
+
list: (input, requestOptions) => request("seed.remotePc.vibe.list", input, requestOptions),
|
|
1076
|
+
get: (input, requestOptions) => request("seed.remotePc.vibe.get", input, requestOptions),
|
|
1077
|
+
messages: (input, requestOptions) => request("seed.remotePc.vibe.messages", input, requestOptions),
|
|
1078
|
+
subscribe: async (input, handler, subscribeOptions) => {
|
|
1079
|
+
let stopped = false;
|
|
1080
|
+
let cursor = subscribeOptions?.initialCursor;
|
|
1081
|
+
const poll = async () => {
|
|
1082
|
+
while (!stopped) {
|
|
1083
|
+
const result = await request("seed.remotePc.vibe.events", {
|
|
1084
|
+
...input,
|
|
1085
|
+
...cursor ? { cursor } : {},
|
|
1086
|
+
waitMs: subscribeOptions?.waitMs ?? 15e3
|
|
1087
|
+
}, { timeoutMs: subscribeOptions?.requestTimeoutMs ?? 25e3 });
|
|
1088
|
+
if (stopped) return;
|
|
1089
|
+
if (result.cursor && result.cursor !== cursor) {
|
|
1090
|
+
cursor = result.cursor;
|
|
1091
|
+
await subscribeOptions?.onCursor?.(cursor);
|
|
1092
|
+
}
|
|
1093
|
+
if (result.messages.length > 0) handler(result);
|
|
1094
|
+
}
|
|
1095
|
+
};
|
|
1096
|
+
poll().catch((error) => {
|
|
1097
|
+
stopped = true;
|
|
1098
|
+
subscribeOptions?.onError?.(error);
|
|
1099
|
+
});
|
|
1100
|
+
return async () => {
|
|
1101
|
+
stopped = true;
|
|
1102
|
+
};
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
},
|
|
1106
|
+
memory: { search: (input, requestOptions) => request("seed.memory.search", input, requestOptions) },
|
|
1107
|
+
resource: {
|
|
1108
|
+
read: (input, requestOptions) => request("seed.resource.read", input, requestOptions),
|
|
1109
|
+
stat: (input, requestOptions) => request("seed.resource.stat", input, requestOptions),
|
|
1110
|
+
list: (input, requestOptions) => request("seed.resource.list", input, requestOptions),
|
|
1111
|
+
open: (input, requestOptions) => request("seed.resource.open", input, requestOptions),
|
|
1112
|
+
openableSchemes: (input, requestOptions) => request("seed.resource.openableSchemes", input, requestOptions)
|
|
1113
|
+
},
|
|
1114
|
+
skill: {
|
|
1115
|
+
list: (requestOptions) => request("seed.skill.list", void 0, requestOptions),
|
|
1116
|
+
listAutoInjected: (requestOptions) => request("seed.skill.autoInjected.list", void 0, requestOptions),
|
|
1117
|
+
detectExternal: (requestOptions) => request("seed.skill.external.detect", void 0, requestOptions),
|
|
1118
|
+
getPaths: (requestOptions) => request("seed.skill.paths.get", void 0, requestOptions),
|
|
1119
|
+
addExternalPath: (input, requestOptions) => request("seed.skill.externalPath.add", input, requestOptions),
|
|
1120
|
+
importLocal: (input, requestOptions) => request("seed.skill.importLocal", input, requestOptions),
|
|
1121
|
+
delete: (input, requestOptions) => request("seed.skill.delete", input, requestOptions),
|
|
1122
|
+
exportLocal: (input, requestOptions) => request("seed.skill.exportLocal", input, requestOptions),
|
|
1123
|
+
addAutoInjected: (input, requestOptions) => request("seed.skill.autoInjected.add", input, requestOptions),
|
|
1124
|
+
removeAutoInjected: (input, requestOptions) => request("seed.skill.autoInjected.remove", input, requestOptions),
|
|
1125
|
+
setMarketEnabled: (input, requestOptions) => request("seed.skill.market.setEnabled", input, requestOptions)
|
|
1126
|
+
},
|
|
1127
|
+
assistant: { resource: {
|
|
1128
|
+
read: (input, requestOptions) => request("seed.assistant.resource.read", input, requestOptions),
|
|
1129
|
+
write: (input, requestOptions) => request("seed.assistant.resource.write", input, requestOptions),
|
|
1130
|
+
delete: (input, requestOptions) => request("seed.assistant.resource.delete", input, requestOptions)
|
|
1131
|
+
} },
|
|
1132
|
+
builtin: { resource: { read: (input, requestOptions) => request("seed.builtin.resource.read", input, requestOptions) } },
|
|
1133
|
+
workspace: createSeedAppWorkspaceClient(request, {
|
|
1134
|
+
subscribe: options.transport.subscribe,
|
|
1135
|
+
subscribeBridgeEvent: options.transport.subscribeBridgeEvent
|
|
1136
|
+
}),
|
|
1137
|
+
brainBundle: { previewSummaries: (input, requestOptions) => request("seed.brainBundle.previewSummaries", input, requestOptions) },
|
|
1138
|
+
employee: {
|
|
1139
|
+
list: (input, requestOptions) => request("seed.employee.list", input, requestOptions),
|
|
1140
|
+
subscribeChanged: (handler, requestOptions) => subscribeTypedBridgeEvent(EMPLOYEE_CHANGED_METHOD, isEmployeeChangedPayload, handler, requestOptions),
|
|
1141
|
+
get: (input, requestOptions) => request("seed.employee.get", input, requestOptions),
|
|
1142
|
+
openConversation: (input, requestOptions) => request("seed.employee.openConversation", input, requestOptions),
|
|
1143
|
+
createFromRoleTemplate: (input, requestOptions) => request("seed.employee.createFromRoleTemplate", input, requestOptions),
|
|
1144
|
+
createFromAssistant: (input, requestOptions) => request("seed.employee.createFromAssistant", input, requestOptions),
|
|
1145
|
+
update: (input, requestOptions) => request("seed.employee.update", input, requestOptions),
|
|
1146
|
+
remove: (input, requestOptions) => request("seed.employee.remove", input, requestOptions),
|
|
1147
|
+
memory: {
|
|
1148
|
+
list: (input, requestOptions) => request("seed.employee.memory.list", input, requestOptions),
|
|
1149
|
+
delete: (input, requestOptions) => request("seed.employee.memory.delete", input, requestOptions),
|
|
1150
|
+
events: {
|
|
1151
|
+
list: (input, requestOptions) => request("seed.employee.memory.events.list", input, requestOptions),
|
|
1152
|
+
subscribeChanged: (input, handler, requestOptions) => subscribeTypedBridgeEvent(EMPLOYEE_MEMORY_EVENTS_CHANGED_METHOD, isEmployeeMemoryChangedPayload, handler, requestOptions, (event) => !input.employeeId || event.employeeId === input.employeeId)
|
|
1153
|
+
}
|
|
1154
|
+
},
|
|
1155
|
+
activity: { list: (input, requestOptions) => request("seed.employee.activity.list", input, requestOptions) }
|
|
1156
|
+
},
|
|
1157
|
+
roleTemplate: {
|
|
1158
|
+
list: (input, requestOptions) => request("seed.roleTemplate.list", input, requestOptions),
|
|
1159
|
+
get: (input, requestOptions) => request("seed.roleTemplate.get", input, requestOptions),
|
|
1160
|
+
create: (input, requestOptions) => request("seed.roleTemplate.create", input, requestOptions),
|
|
1161
|
+
update: (input, requestOptions) => request("seed.roleTemplate.update", input, requestOptions),
|
|
1162
|
+
delete: (input, requestOptions) => request("seed.roleTemplate.delete", input, requestOptions),
|
|
1163
|
+
subscribeChanged: (handler, requestOptions) => subscribeTypedBridgeEvent(ROLE_TEMPLATE_CHANGED_METHOD, isRoleTemplateChangedPayload, handler, requestOptions)
|
|
1164
|
+
},
|
|
1165
|
+
roleMaker: { session: {
|
|
1166
|
+
start: (input, requestOptions) => request("seed.roleMaker.session.start", input, requestOptions),
|
|
1167
|
+
get: (input, requestOptions) => request("seed.roleMaker.session.get", input, requestOptions),
|
|
1168
|
+
generate: (input, requestOptions) => request("seed.roleMaker.session.generate", input, requestOptions),
|
|
1169
|
+
apply: (input, requestOptions) => request("seed.roleMaker.session.apply", input, requestOptions),
|
|
1170
|
+
approve: (input, requestOptions) => request("seed.roleMaker.session.approve", input, requestOptions),
|
|
1171
|
+
discard: (input, requestOptions) => request("seed.roleMaker.session.discard", input, requestOptions),
|
|
1172
|
+
subscribeChanged: (handler, requestOptions) => subscribeTypedBridgeEvent(ROLE_MAKER_SESSION_CHANGED_METHOD, isRoleMakerSessionChangedPayload, handler, requestOptions)
|
|
1173
|
+
} },
|
|
1174
|
+
mcp: {
|
|
1175
|
+
listTools: (input, requestOptions) => request("seed.mcp.listTools", input, requestOptions),
|
|
1176
|
+
callTool: (input, requestOptions) => request("seed.mcp.callTool", input, requestOptions)
|
|
1177
|
+
},
|
|
1178
|
+
diagnostics: { snapshot: (input, requestOptions) => request("seed.bridge.diagnostics", input, requestOptions) },
|
|
1179
|
+
routes: {
|
|
1180
|
+
snapshot: snapshotRoutes,
|
|
1181
|
+
resolve: resolveRoute,
|
|
1182
|
+
getCached: getCachedRoutes
|
|
1183
|
+
},
|
|
1184
|
+
events: {
|
|
1185
|
+
subscribe: (handler) => options.transport.subscribe?.(handler) ?? (() => void 0),
|
|
1186
|
+
subscribeRealtimeChannel: async (channel, cursor) => await options.transport.subscribeRealtimeChannel?.(channel, cursor) ?? (() => void 0),
|
|
1187
|
+
subscribeBridgeEvent: (event, handler, requestOptions) => subscribeCanonicalBridgeEvent(event, handler, requestOptions)
|
|
1188
|
+
},
|
|
1189
|
+
request,
|
|
1190
|
+
close: () => options.transport.close?.()
|
|
1191
|
+
};
|
|
1192
|
+
}
|
|
1193
|
+
function isRoleTemplateChangedPayload(value) {
|
|
1194
|
+
if (!value || typeof value !== "object") return false;
|
|
1195
|
+
const record = value;
|
|
1196
|
+
return typeof record.roleTemplateId === "string" && (record.action === "created" || record.action === "updated" || record.action === "deleted");
|
|
1197
|
+
}
|
|
1198
|
+
function isRoleMakerSessionChangedPayload(value) {
|
|
1199
|
+
if (!value || typeof value !== "object") return false;
|
|
1200
|
+
const record = value;
|
|
1201
|
+
return typeof record.sessionId === "string" && typeof record.conversationId === "string" && typeof record.status === "string" && (record.updatedAt === void 0 || typeof record.updatedAt === "string" || typeof record.updatedAt === "number") && typeof record.source === "string";
|
|
1202
|
+
}
|
|
1203
|
+
function isAgentApprovalChangedPayload(value) {
|
|
1204
|
+
if (!value || typeof value !== "object") return false;
|
|
1205
|
+
const record = value;
|
|
1206
|
+
return typeof record.conversationId === "string" && typeof record.approvalId === "string" && (record.action === "added" || record.action === "updated" || record.action === "removed") && (record.approval === void 0 || record.approval !== null && typeof record.approval === "object");
|
|
1207
|
+
}
|
|
1208
|
+
function isConversationChangedPayload(value) {
|
|
1209
|
+
if (!value || typeof value !== "object") return false;
|
|
1210
|
+
const record = value;
|
|
1211
|
+
return typeof record.conversationId === "string" && (record.action === "created" || record.action === "updated" || record.action === "deleted") && (record.source === void 0 || typeof record.source === "string");
|
|
1212
|
+
}
|
|
1213
|
+
function isRemotePcPresenceChangedPayload(value) {
|
|
1214
|
+
if (!value || typeof value !== "object") return false;
|
|
1215
|
+
const record = value;
|
|
1216
|
+
const presence = record.presence;
|
|
1217
|
+
if (!presence || typeof presence !== "object") return false;
|
|
1218
|
+
const presenceRecord = presence;
|
|
1219
|
+
const contentLease = presenceRecord.contentLease;
|
|
1220
|
+
return (record.action === "connected" || record.action === "updated" || record.action === "lease-expired") && typeof presenceRecord.deviceId === "string" && typeof presenceRecord.displayName === "string" && (presenceRecord.status === "online" || presenceRecord.status === "offline") && typeof presenceRecord.lastSeenAt === "number" && Boolean(contentLease && typeof contentLease === "object");
|
|
1221
|
+
}
|
|
1222
|
+
function isEmployeeChangedPayload(value) {
|
|
1223
|
+
if (!value || typeof value !== "object") return false;
|
|
1224
|
+
const record = value;
|
|
1225
|
+
return typeof record.employeeId === "string" && (record.action === "created" || record.action === "updated" || record.action === "deleted") && (record.employee === void 0 || record.employee !== null && typeof record.employee === "object");
|
|
1226
|
+
}
|
|
1227
|
+
function isTeamChangedPayload(value) {
|
|
1228
|
+
if (!value || typeof value !== "object") return false;
|
|
1229
|
+
const record = value;
|
|
1230
|
+
return typeof record.teamId === "string" && (record.action === "created" || record.action === "updated" || record.action === "removed" || record.action === "agent_added" || record.action === "agent_removed" || record.action === "agent_renamed");
|
|
1231
|
+
}
|
|
1232
|
+
function isTeamAgentStatusChangedPayload(value) {
|
|
1233
|
+
if (!value || typeof value !== "object") return false;
|
|
1234
|
+
const record = value;
|
|
1235
|
+
return typeof record.teamId === "string" && typeof record.slotId === "string" && typeof record.status === "string" && (record.lastMessage === void 0 || typeof record.lastMessage === "string");
|
|
1236
|
+
}
|
|
1237
|
+
function isTeamAgentSpawnedPayload(value) {
|
|
1238
|
+
if (!value || typeof value !== "object") return false;
|
|
1239
|
+
const record = value;
|
|
1240
|
+
return typeof record.teamId === "string" && isTeamAgentPayload(record.agent);
|
|
1241
|
+
}
|
|
1242
|
+
function isTeamAgentRemovedPayload(value) {
|
|
1243
|
+
if (!value || typeof value !== "object") return false;
|
|
1244
|
+
const record = value;
|
|
1245
|
+
return typeof record.teamId === "string" && typeof record.slotId === "string";
|
|
1246
|
+
}
|
|
1247
|
+
function isTeamAgentRenamedPayload(value) {
|
|
1248
|
+
if (!value || typeof value !== "object") return false;
|
|
1249
|
+
const record = value;
|
|
1250
|
+
return typeof record.teamId === "string" && typeof record.slotId === "string" && typeof record.oldName === "string" && typeof record.newName === "string";
|
|
1251
|
+
}
|
|
1252
|
+
function isTeamAgentPayload(value) {
|
|
1253
|
+
if (!value || typeof value !== "object") return false;
|
|
1254
|
+
const record = value;
|
|
1255
|
+
return typeof record.slotId === "string" && typeof record.conversationId === "string" && typeof record.role === "string" && typeof record.agentType === "string" && typeof record.agentName === "string" && typeof record.conversationType === "string" && typeof record.status === "string";
|
|
1256
|
+
}
|
|
1257
|
+
function isEmployeeMemoryChangedPayload(value) {
|
|
1258
|
+
if (!value || typeof value !== "object") return false;
|
|
1259
|
+
const record = value;
|
|
1260
|
+
return (record.type === "memory.created" || record.type === "memory.deleted") && typeof record.employeeId === "string" && typeof record.memoryId === "string" && record.data !== null && typeof record.data === "object" && (typeof record.timestamp === "string" || typeof record.timestamp === "number");
|
|
1261
|
+
}
|
|
1262
|
+
function assertAgentStreamSubscribeScope(input) {
|
|
1263
|
+
if (("conversationId" in input && typeof input.conversationId === "string" && input.conversationId.length > 0) !== ("runId" in input && typeof input.runId === "string" && input.runId.length > 0)) return;
|
|
1264
|
+
throw createSeedAppError({
|
|
1265
|
+
code: "invalid_payload_schema",
|
|
1266
|
+
message: "Provide exactly one of conversationId or runId when subscribing to agent stream replay.",
|
|
1267
|
+
requestId: SEED_EVENT_SUBSCRIBE_METHOD
|
|
1268
|
+
});
|
|
1269
|
+
}
|
|
1270
|
+
function assertAguiStreamSubscribeScope(input) {
|
|
1271
|
+
if (("conversationId" in input && typeof input.conversationId === "string" && input.conversationId.length > 0) !== ("runId" in input && typeof input.runId === "string" && input.runId.length > 0)) return;
|
|
1272
|
+
throw createSeedAppError({
|
|
1273
|
+
code: "invalid_payload_schema",
|
|
1274
|
+
message: "Provide exactly one of conversationId or runId when subscribing to AGUI stream replay.",
|
|
1275
|
+
requestId: SEED_EVENT_SUBSCRIBE_METHOD
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1278
|
+
function matchesAgentStreamScope(event, input) {
|
|
1279
|
+
if ("conversationId" in input) return event.payload.conversationId === input.conversationId;
|
|
1280
|
+
return event.payload.runId === input.runId;
|
|
1281
|
+
}
|
|
1282
|
+
function dispatchAgentStreamEvent(event, handlers, options) {
|
|
1283
|
+
handlers?.onEvent?.(event);
|
|
1284
|
+
switch (event.type) {
|
|
1285
|
+
case "agent.message.delta":
|
|
1286
|
+
handlers?.onMessageDelta?.(event);
|
|
1287
|
+
break;
|
|
1288
|
+
case "agent.thought.delta":
|
|
1289
|
+
handlers?.onThoughtDelta?.(event);
|
|
1290
|
+
break;
|
|
1291
|
+
case "agent.tool.started":
|
|
1292
|
+
case "agent.tool.updated":
|
|
1293
|
+
handlers?.onTool?.(event);
|
|
1294
|
+
break;
|
|
1295
|
+
case "agent.permission.required":
|
|
1296
|
+
case "agent.elicitation.required":
|
|
1297
|
+
handlers?.onBlocked?.(event);
|
|
1298
|
+
break;
|
|
1299
|
+
case "agent.run.completed":
|
|
1300
|
+
case "agent.run.failed":
|
|
1301
|
+
case "agent.run.cancelled":
|
|
1302
|
+
case "agent.turn.completed":
|
|
1303
|
+
if (options?.suppressTerminalHandler) break;
|
|
1304
|
+
handlers?.onTerminal?.(event);
|
|
1305
|
+
break;
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
//#endregion
|
|
1309
|
+
//#region src/bridgeTarget.ts
|
|
1310
|
+
const SEED_APP_BRIDGE_TARGET_DEFAULT_TIMEOUT_MS = 3e4;
|
|
1311
|
+
const seedAppBridgeTargetModes = {
|
|
1312
|
+
appHost: "app-host",
|
|
1313
|
+
webEndpoint: "web-endpoint"
|
|
1314
|
+
};
|
|
1315
|
+
const seedAppBridgeTargetSearchParams = {
|
|
1316
|
+
mode: "seedBridgeTarget",
|
|
1317
|
+
endpoint: "seedBridgeEndpoint",
|
|
1318
|
+
timeoutMs: "seedBridgeTimeoutMs"
|
|
1319
|
+
};
|
|
1320
|
+
const seedAppAppearanceSearchParams = {
|
|
1321
|
+
theme: "seedTheme",
|
|
1322
|
+
colorScheme: "seedColorScheme",
|
|
1323
|
+
fontScale: "seedFontScale"
|
|
1324
|
+
};
|
|
1325
|
+
function appendSeedAppBridgeTargetSearchParams(entryUrl, target) {
|
|
1326
|
+
if (target.mode !== seedAppBridgeTargetModes.webEndpoint) return entryUrl;
|
|
1327
|
+
try {
|
|
1328
|
+
const url = new URL(entryUrl);
|
|
1329
|
+
url.searchParams.set(seedAppBridgeTargetSearchParams.mode, target.mode);
|
|
1330
|
+
url.searchParams.set(seedAppBridgeTargetSearchParams.endpoint, target.endpoint);
|
|
1331
|
+
url.searchParams.set(seedAppBridgeTargetSearchParams.timeoutMs, String(target.timeoutMs));
|
|
1332
|
+
return url.toString();
|
|
1333
|
+
} catch {
|
|
1334
|
+
return entryUrl;
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
function appendSeedAppAppearanceSearchParams(entryUrl, appearance) {
|
|
1338
|
+
if (!appearance) return entryUrl;
|
|
1339
|
+
try {
|
|
1340
|
+
const url = new URL(entryUrl);
|
|
1341
|
+
url.searchParams.set(seedAppAppearanceSearchParams.theme, appearance.theme);
|
|
1342
|
+
if (appearance.colorScheme) url.searchParams.set(seedAppAppearanceSearchParams.colorScheme, appearance.colorScheme);
|
|
1343
|
+
else url.searchParams.delete(seedAppAppearanceSearchParams.colorScheme);
|
|
1344
|
+
if (typeof appearance.fontScale === "number" && Number.isFinite(appearance.fontScale)) url.searchParams.set(seedAppAppearanceSearchParams.fontScale, String(appearance.fontScale));
|
|
1345
|
+
else url.searchParams.delete(seedAppAppearanceSearchParams.fontScale);
|
|
1346
|
+
return url.toString();
|
|
1347
|
+
} catch {
|
|
1348
|
+
return entryUrl;
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
function resolveSeedAppBridgeTargetFromSearch(search) {
|
|
1352
|
+
const params = typeof search === "string" ? new URLSearchParams(search.startsWith("?") ? search.slice(1) : search) : search;
|
|
1353
|
+
if (params.get(seedAppBridgeTargetSearchParams.mode) !== seedAppBridgeTargetModes.webEndpoint) return { mode: seedAppBridgeTargetModes.appHost };
|
|
1354
|
+
const endpoint = params.get(seedAppBridgeTargetSearchParams.endpoint)?.trim();
|
|
1355
|
+
if (!endpoint) return { mode: seedAppBridgeTargetModes.appHost };
|
|
1356
|
+
return {
|
|
1357
|
+
mode: seedAppBridgeTargetModes.webEndpoint,
|
|
1358
|
+
endpoint,
|
|
1359
|
+
timeoutMs: readTimeoutMs(params.get(seedAppBridgeTargetSearchParams.timeoutMs))
|
|
1360
|
+
};
|
|
1361
|
+
}
|
|
1362
|
+
function resolveSeedAppAppearanceFromSearch(search) {
|
|
1363
|
+
const params = typeof search === "string" ? new URLSearchParams(search.startsWith("?") ? search.slice(1) : search) : search;
|
|
1364
|
+
const theme = readThemeMode(params.get(seedAppAppearanceSearchParams.theme));
|
|
1365
|
+
if (!theme) return void 0;
|
|
1366
|
+
const colorScheme = params.get(seedAppAppearanceSearchParams.colorScheme)?.trim() || void 0;
|
|
1367
|
+
const fontScale = readFontScale(params.get(seedAppAppearanceSearchParams.fontScale));
|
|
1368
|
+
return {
|
|
1369
|
+
theme,
|
|
1370
|
+
...colorScheme ? { colorScheme } : {},
|
|
1371
|
+
...fontScale ? { fontScale } : {},
|
|
1372
|
+
source: "query"
|
|
1373
|
+
};
|
|
1374
|
+
}
|
|
1375
|
+
function readTimeoutMs(value) {
|
|
1376
|
+
if (!value) return SEED_APP_BRIDGE_TARGET_DEFAULT_TIMEOUT_MS;
|
|
1377
|
+
const parsed = Number(value);
|
|
1378
|
+
if (!Number.isFinite(parsed) || parsed < 1e3) return SEED_APP_BRIDGE_TARGET_DEFAULT_TIMEOUT_MS;
|
|
1379
|
+
return Math.round(parsed);
|
|
1380
|
+
}
|
|
1381
|
+
function readThemeMode(value) {
|
|
1382
|
+
if (value === "light" || value === "dark") return value;
|
|
1383
|
+
}
|
|
1384
|
+
function readFontScale(value) {
|
|
1385
|
+
if (!value) return void 0;
|
|
1386
|
+
const parsed = Number(value);
|
|
1387
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return void 0;
|
|
1388
|
+
return parsed;
|
|
1389
|
+
}
|
|
1390
|
+
//#endregion
|
|
1391
|
+
//#region src/consoleBridge.ts
|
|
1392
|
+
const DEFAULT_LEVELS = [
|
|
1393
|
+
"log",
|
|
1394
|
+
"info",
|
|
1395
|
+
"warn",
|
|
1396
|
+
"error",
|
|
1397
|
+
"debug"
|
|
1398
|
+
];
|
|
1399
|
+
const DEFAULT_MAX_ARG_LENGTH = 2e3;
|
|
1400
|
+
function installSeedAppConsoleBridge(options = {}) {
|
|
1401
|
+
const currentWindow = options.currentWindow ?? globalThis.window;
|
|
1402
|
+
if (!currentWindow) return { uninstall: () => void 0 };
|
|
1403
|
+
const targetWindow = options.targetWindow ?? currentWindow.parent;
|
|
1404
|
+
const targetOrigin = options.targetOrigin ?? "*";
|
|
1405
|
+
const includeOriginal = options.includeOriginal ?? true;
|
|
1406
|
+
const maxArgLength = options.maxArgLength ?? DEFAULT_MAX_ARG_LENGTH;
|
|
1407
|
+
const levels = options.levels ?? DEFAULT_LEVELS;
|
|
1408
|
+
const originals = /* @__PURE__ */ new Map();
|
|
1409
|
+
levels.forEach((level) => {
|
|
1410
|
+
const original = currentWindow.console[level];
|
|
1411
|
+
originals.set(level, original);
|
|
1412
|
+
currentWindow.console[level] = ((...args) => {
|
|
1413
|
+
if (includeOriginal) original.apply(currentWindow.console, args);
|
|
1414
|
+
targetWindow.postMessage(createConsoleEnvelope(level, args, maxArgLength), targetOrigin);
|
|
1415
|
+
});
|
|
1416
|
+
});
|
|
1417
|
+
return { uninstall() {
|
|
1418
|
+
originals.forEach((original, level) => {
|
|
1419
|
+
currentWindow.console[level] = original;
|
|
1420
|
+
});
|
|
1421
|
+
originals.clear();
|
|
1422
|
+
} };
|
|
1423
|
+
}
|
|
1424
|
+
function createConsoleEnvelope(level, args, maxArgLength) {
|
|
1425
|
+
const serializedArgs = args.map((arg) => serializeConsoleArg(arg, maxArgLength));
|
|
1426
|
+
return {
|
|
1427
|
+
source: "seed-app-console",
|
|
1428
|
+
channel: "seed-app-console",
|
|
1429
|
+
level,
|
|
1430
|
+
message: serializedArgs.join(" "),
|
|
1431
|
+
args: serializedArgs,
|
|
1432
|
+
timestamp: Date.now()
|
|
1433
|
+
};
|
|
1434
|
+
}
|
|
1435
|
+
function serializeConsoleArg(value, maxLength) {
|
|
1436
|
+
const serialized = serializeValue(value);
|
|
1437
|
+
return serialized.length > maxLength ? `${serialized.slice(0, maxLength)}...` : serialized;
|
|
1438
|
+
}
|
|
1439
|
+
function serializeValue(value) {
|
|
1440
|
+
if (typeof value === "string") return value;
|
|
1441
|
+
if (value instanceof Error) return value.stack ?? value.message;
|
|
1442
|
+
try {
|
|
1443
|
+
return JSON.stringify(value);
|
|
1444
|
+
} catch {
|
|
1445
|
+
return String(value);
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
//#endregion
|
|
1449
|
+
//#region src/endpointTransport.ts
|
|
1450
|
+
const WEBSOCKET_OPEN_STATE = 1;
|
|
1451
|
+
const REALTIME_RECOVERY_INTERVAL_MS = 3e4;
|
|
1452
|
+
const ENDPOINT_TRACE_SCHEMA = "seed-app.endpoint-transport.trace.v1";
|
|
1453
|
+
const ENDPOINT_TRACE_BRIDGE_CHANNEL = "seed-app-devtools";
|
|
1454
|
+
function createSeedAppEndpointTraceBridge(options = {}) {
|
|
1455
|
+
const currentWindow = options.currentWindow ?? (typeof window === "undefined" ? void 0 : window);
|
|
1456
|
+
const targetWindow = options.targetWindow ?? currentWindow?.parent;
|
|
1457
|
+
if (!targetWindow || currentWindow && targetWindow === currentWindow) return () => void 0;
|
|
1458
|
+
const targetOrigin = options.targetOrigin ?? "*";
|
|
1459
|
+
return (trace) => {
|
|
1460
|
+
targetWindow.postMessage({
|
|
1461
|
+
source: ENDPOINT_TRACE_BRIDGE_CHANNEL,
|
|
1462
|
+
channel: ENDPOINT_TRACE_BRIDGE_CHANNEL,
|
|
1463
|
+
trace,
|
|
1464
|
+
timestamp: Date.now()
|
|
1465
|
+
}, targetOrigin);
|
|
1466
|
+
};
|
|
1467
|
+
}
|
|
1468
|
+
function createSeedAppEndpointTransport(options) {
|
|
1469
|
+
const timeoutMs = options.timeoutMs ?? 3e4;
|
|
1470
|
+
const endpoint = String(options.endpoint);
|
|
1471
|
+
const endpointTraceTarget = summarizeEndpointForTrace(endpoint);
|
|
1472
|
+
const webSocketFactory = options.webSocketFactory ?? createDefaultWebSocket;
|
|
1473
|
+
const autoAckRealtime = options.autoAckRealtime ?? true;
|
|
1474
|
+
const autoReconnect = options.autoReconnect ?? true;
|
|
1475
|
+
const maxReconnectAttempts = options.maxReconnectAttempts ?? Number.POSITIVE_INFINITY;
|
|
1476
|
+
const pending = /* @__PURE__ */ new Map();
|
|
1477
|
+
const eventHandlers = /* @__PURE__ */ new Set();
|
|
1478
|
+
const bridgeEventSubscriptions = /* @__PURE__ */ new Map();
|
|
1479
|
+
const sendQueue = [];
|
|
1480
|
+
const channelCursors = /* @__PURE__ */ new Map();
|
|
1481
|
+
const realtimeChannels = new Set(options.realtimeChannels ?? []);
|
|
1482
|
+
let appSessionId = options.appSessionId;
|
|
1483
|
+
let socket = webSocketFactory(endpoint);
|
|
1484
|
+
let nextId = 0;
|
|
1485
|
+
let nextBridgeEventSubscriptionId = 0;
|
|
1486
|
+
let closed = false;
|
|
1487
|
+
let reconnectAttempts = 0;
|
|
1488
|
+
let reconnectTimer;
|
|
1489
|
+
let realtimeRecoveryTimer;
|
|
1490
|
+
const emitTrace = (event) => {
|
|
1491
|
+
options.onTrace?.(stripUndefined({
|
|
1492
|
+
schema: ENDPOINT_TRACE_SCHEMA,
|
|
1493
|
+
timestamp: Date.now(),
|
|
1494
|
+
endpointOrigin: endpointTraceTarget.origin,
|
|
1495
|
+
endpointPath: endpointTraceTarget.pathname,
|
|
1496
|
+
...event
|
|
1497
|
+
}));
|
|
1498
|
+
};
|
|
1499
|
+
const sendFrame = (frame) => {
|
|
1500
|
+
const serialized = isJsonRpcSerializableFrame(frame) ? (0, _seed_app_studio_protocol.serializeJsonRpcFrame)(frame) : JSON.stringify(frame);
|
|
1501
|
+
if (socket.readyState === WEBSOCKET_OPEN_STATE) {
|
|
1502
|
+
socket.send(serialized);
|
|
1503
|
+
emitTrace(createFrameTraceEvent("frame.send", "outbound", frame, serialized.length, appSessionId, options));
|
|
1504
|
+
return;
|
|
1505
|
+
}
|
|
1506
|
+
sendQueue.push({
|
|
1507
|
+
frame,
|
|
1508
|
+
serialized
|
|
1509
|
+
});
|
|
1510
|
+
emitTrace(createFrameTraceEvent("frame.queue", "outbound", frame, serialized.length, appSessionId, options));
|
|
1511
|
+
};
|
|
1512
|
+
const requestJsonRpc = (method, params, requestOptions) => {
|
|
1513
|
+
if (closed) return Promise.reject(createSeedAppError({
|
|
1514
|
+
code: "host_unavailable",
|
|
1515
|
+
message: "Seed App endpoint transport is closed.",
|
|
1516
|
+
requestId: method
|
|
1517
|
+
}));
|
|
1518
|
+
const id = `${options.idPrefix ?? "seed-app-endpoint"}-${Date.now()}-${nextId}`;
|
|
1519
|
+
nextId += 1;
|
|
1520
|
+
const requestTimeoutMs = requestOptions?.timeoutMs ?? timeoutMs;
|
|
1521
|
+
return new Promise((resolve, reject) => {
|
|
1522
|
+
const timeout = setTimeout(() => {
|
|
1523
|
+
pending.delete(id);
|
|
1524
|
+
emitTrace({
|
|
1525
|
+
direction: "internal",
|
|
1526
|
+
id,
|
|
1527
|
+
method,
|
|
1528
|
+
phase: "request.timeout",
|
|
1529
|
+
traceId: id
|
|
1530
|
+
});
|
|
1531
|
+
reject(createSeedAppError({
|
|
1532
|
+
code: "handshake_timeout",
|
|
1533
|
+
message: "Seed App Host request timed out.",
|
|
1534
|
+
requestId: id,
|
|
1535
|
+
retryable: true
|
|
1536
|
+
}));
|
|
1537
|
+
}, requestTimeoutMs);
|
|
1538
|
+
pending.set(id, {
|
|
1539
|
+
method,
|
|
1540
|
+
traceId: id,
|
|
1541
|
+
resolve,
|
|
1542
|
+
reject,
|
|
1543
|
+
timeout
|
|
1544
|
+
});
|
|
1545
|
+
sendFrame((0, _seed_app_studio_protocol.createJsonRpcRequest)(method, id, toJsonRpcParams$2(params)));
|
|
1546
|
+
});
|
|
1547
|
+
};
|
|
1548
|
+
const flushQueue = () => {
|
|
1549
|
+
if (closed || socket.readyState !== WEBSOCKET_OPEN_STATE) return;
|
|
1550
|
+
while (sendQueue.length > 0) {
|
|
1551
|
+
const queued = sendQueue.shift();
|
|
1552
|
+
if (queued) {
|
|
1553
|
+
socket.send(queued.serialized);
|
|
1554
|
+
emitTrace(createFrameTraceEvent("frame.send", "outbound", queued.frame, queued.serialized.length, appSessionId, options));
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
};
|
|
1558
|
+
const handleOpen = () => {
|
|
1559
|
+
emitTrace({
|
|
1560
|
+
direction: "internal",
|
|
1561
|
+
phase: "socket.open",
|
|
1562
|
+
appSessionId
|
|
1563
|
+
});
|
|
1564
|
+
reconnectAttempts = 0;
|
|
1565
|
+
subscribeConfiguredRealtimeChannels();
|
|
1566
|
+
resubscribeBridgeEvents();
|
|
1567
|
+
flushQueue();
|
|
1568
|
+
};
|
|
1569
|
+
const handleMessage = (event) => {
|
|
1570
|
+
const frame = parseFrame(event?.data);
|
|
1571
|
+
if (!frame) return;
|
|
1572
|
+
emitTrace(createFrameTraceEvent("frame.receive", "inbound", frame, readFrameSize(event?.data), appSessionId, options, pending));
|
|
1573
|
+
if (frame.type === "response") {
|
|
1574
|
+
const id = String(frame.id);
|
|
1575
|
+
syncHandshakeAppSession(id, frame.result, pending, (nextAppSessionId) => {
|
|
1576
|
+
appSessionId = nextAppSessionId;
|
|
1577
|
+
subscribeAppRealtime(nextAppSessionId);
|
|
1578
|
+
});
|
|
1579
|
+
settleSuccess(id, frame.result, pending);
|
|
1580
|
+
return;
|
|
1581
|
+
}
|
|
1582
|
+
if (frame.type === "error") {
|
|
1583
|
+
settleJsonRpcFailure(frame.id, frame.error, pending);
|
|
1584
|
+
return;
|
|
1585
|
+
}
|
|
1586
|
+
if (frame.type === "notification") {
|
|
1587
|
+
if (frame.method === "seed.event") {
|
|
1588
|
+
const params = frame.params ?? {};
|
|
1589
|
+
updateSeedEventRealtimeCursor(params);
|
|
1590
|
+
emitSeedEventNotification(params, eventHandlers);
|
|
1591
|
+
acknowledgeSeedEventRealtimeFrame(params);
|
|
1592
|
+
return;
|
|
1593
|
+
}
|
|
1594
|
+
if (frame.method === "realtime.snapshot") {
|
|
1595
|
+
const params = frame.params ?? {};
|
|
1596
|
+
updateRealtimeSnapshotCursor(params);
|
|
1597
|
+
emitRealtimeSnapshotTrace(params);
|
|
1598
|
+
emitRealtimeSnapshot(params, eventHandlers);
|
|
1599
|
+
acknowledgeRealtimeFrame(params);
|
|
1600
|
+
return;
|
|
1601
|
+
}
|
|
1602
|
+
if (frame.method === "auth.expired") {
|
|
1603
|
+
closed = true;
|
|
1604
|
+
clearReconnectTimer();
|
|
1605
|
+
emitTrace({
|
|
1606
|
+
direction: "internal",
|
|
1607
|
+
phase: "auth.expired",
|
|
1608
|
+
appSessionId
|
|
1609
|
+
});
|
|
1610
|
+
rejectAll(pending, "host_unavailable", "Seed App endpoint authentication expired.");
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
};
|
|
1614
|
+
const handleClose = () => {
|
|
1615
|
+
emitTrace({
|
|
1616
|
+
direction: "internal",
|
|
1617
|
+
phase: "socket.close",
|
|
1618
|
+
appSessionId
|
|
1619
|
+
});
|
|
1620
|
+
rejectAll(pending, "host_unavailable", "Seed App endpoint transport closed.");
|
|
1621
|
+
for (const subscription of bridgeEventSubscriptions.values()) subscription.serverSubscriptionId = void 0;
|
|
1622
|
+
scheduleReconnect();
|
|
1623
|
+
};
|
|
1624
|
+
const handleError = () => {
|
|
1625
|
+
emitTrace({
|
|
1626
|
+
direction: "internal",
|
|
1627
|
+
phase: "socket.error",
|
|
1628
|
+
appSessionId
|
|
1629
|
+
});
|
|
1630
|
+
rejectAll(pending, "host_unavailable", "Seed App endpoint transport failed.");
|
|
1631
|
+
scheduleReconnect();
|
|
1632
|
+
};
|
|
1633
|
+
const attachSocket = (nextSocket) => {
|
|
1634
|
+
nextSocket.addEventListener("open", handleOpen);
|
|
1635
|
+
nextSocket.addEventListener("message", handleMessage);
|
|
1636
|
+
nextSocket.addEventListener("close", handleClose);
|
|
1637
|
+
nextSocket.addEventListener("error", handleError);
|
|
1638
|
+
};
|
|
1639
|
+
const detachSocket = (currentSocket) => {
|
|
1640
|
+
currentSocket.removeEventListener("open", handleOpen);
|
|
1641
|
+
currentSocket.removeEventListener("message", handleMessage);
|
|
1642
|
+
currentSocket.removeEventListener("close", handleClose);
|
|
1643
|
+
currentSocket.removeEventListener("error", handleError);
|
|
1644
|
+
};
|
|
1645
|
+
const reconnect = () => {
|
|
1646
|
+
if (closed) return;
|
|
1647
|
+
detachSocket(socket);
|
|
1648
|
+
socket = webSocketFactory(endpoint);
|
|
1649
|
+
attachSocket(socket);
|
|
1650
|
+
};
|
|
1651
|
+
const scheduleReconnect = () => {
|
|
1652
|
+
if (closed || reconnectTimer) return;
|
|
1653
|
+
if (!autoReconnect) {
|
|
1654
|
+
closed = true;
|
|
1655
|
+
return;
|
|
1656
|
+
}
|
|
1657
|
+
if (reconnectAttempts >= maxReconnectAttempts) {
|
|
1658
|
+
closed = true;
|
|
1659
|
+
emitTrace({
|
|
1660
|
+
direction: "internal",
|
|
1661
|
+
phase: "socket.reconnect.failed",
|
|
1662
|
+
appSessionId
|
|
1663
|
+
});
|
|
1664
|
+
return;
|
|
1665
|
+
}
|
|
1666
|
+
reconnectAttempts += 1;
|
|
1667
|
+
emitTrace({
|
|
1668
|
+
direction: "internal",
|
|
1669
|
+
phase: "socket.reconnecting",
|
|
1670
|
+
appSessionId
|
|
1671
|
+
});
|
|
1672
|
+
const delayMs = resolveReconnectDelayMs(options.reconnectDelayMs, reconnectAttempts);
|
|
1673
|
+
if (delayMs <= 0) {
|
|
1674
|
+
reconnect();
|
|
1675
|
+
return;
|
|
1676
|
+
}
|
|
1677
|
+
reconnectTimer = setTimeout(() => {
|
|
1678
|
+
reconnectTimer = void 0;
|
|
1679
|
+
reconnect();
|
|
1680
|
+
}, delayMs);
|
|
1681
|
+
};
|
|
1682
|
+
const clearReconnectTimer = () => {
|
|
1683
|
+
if (!reconnectTimer) return;
|
|
1684
|
+
clearTimeout(reconnectTimer);
|
|
1685
|
+
reconnectTimer = void 0;
|
|
1686
|
+
};
|
|
1687
|
+
const clearRealtimeRecoveryTimer = () => {
|
|
1688
|
+
if (!realtimeRecoveryTimer) return;
|
|
1689
|
+
clearInterval(realtimeRecoveryTimer);
|
|
1690
|
+
realtimeRecoveryTimer = void 0;
|
|
1691
|
+
};
|
|
1692
|
+
const subscribeAppRealtime = (nextAppSessionId) => {
|
|
1693
|
+
realtimeChannels.add(`app:${nextAppSessionId}`);
|
|
1694
|
+
sendRealtimeSubscribeFrame(`app:${nextAppSessionId}`, nextAppSessionId);
|
|
1695
|
+
};
|
|
1696
|
+
const subscribeConfiguredRealtimeChannels = () => {
|
|
1697
|
+
if (appSessionId) realtimeChannels.add(`app:${appSessionId}`);
|
|
1698
|
+
for (const channel of realtimeChannels) sendRealtimeSubscribeFrame(channel, appSessionId);
|
|
1699
|
+
};
|
|
1700
|
+
const sendRealtimeSubscribeFrame = (channel, currentAppSessionId) => {
|
|
1701
|
+
const cursor = channelCursors.get(channel);
|
|
1702
|
+
requestJsonRpc("realtime.subscribe", stripUndefined({
|
|
1703
|
+
channel,
|
|
1704
|
+
cursor
|
|
1705
|
+
})).then((snapshot) => {
|
|
1706
|
+
updateRealtimeSnapshotCursor(snapshot);
|
|
1707
|
+
emitRealtimeSnapshotTrace(snapshot);
|
|
1708
|
+
emitRealtimeSnapshot(snapshot, eventHandlers);
|
|
1709
|
+
}).catch(() => void 0);
|
|
1710
|
+
emitTrace({
|
|
1711
|
+
direction: "outbound",
|
|
1712
|
+
phase: "realtime.resubscribe",
|
|
1713
|
+
appSessionId: currentAppSessionId,
|
|
1714
|
+
channel
|
|
1715
|
+
});
|
|
1716
|
+
};
|
|
1717
|
+
const updateSeedEventRealtimeCursor = (frame) => {
|
|
1718
|
+
const meta = isRecord$3(frame.meta) ? frame.meta : void 0;
|
|
1719
|
+
const channel = readString$1(meta?.channel);
|
|
1720
|
+
const cursor = readString$1(meta?.cursor) ?? readString$1(meta?.eventId);
|
|
1721
|
+
if (channel && cursor) channelCursors.set(channel, cursor);
|
|
1722
|
+
};
|
|
1723
|
+
const updateRealtimeSnapshotCursor = (frame) => {
|
|
1724
|
+
const channel = readString$1(frame.channel);
|
|
1725
|
+
const cursor = readString$1(frame.cursor);
|
|
1726
|
+
if (channel && cursor) channelCursors.set(channel, cursor);
|
|
1727
|
+
};
|
|
1728
|
+
const emitRealtimeSnapshotTrace = (frame) => {
|
|
1729
|
+
const channel = readString$1(frame.channel);
|
|
1730
|
+
const replayRequired = frame.replayRequired === true;
|
|
1731
|
+
emitTrace({
|
|
1732
|
+
direction: "inbound",
|
|
1733
|
+
phase: replayRequired ? "realtime.snapshot.gap" : "realtime.snapshot.replayed",
|
|
1734
|
+
appSessionId,
|
|
1735
|
+
channel
|
|
1736
|
+
});
|
|
1737
|
+
};
|
|
1738
|
+
const acknowledgeRealtimeFrame = (frame) => {
|
|
1739
|
+
if (!autoAckRealtime) return;
|
|
1740
|
+
const channel = readString$1(frame.channel);
|
|
1741
|
+
const cursor = channel ? channelCursors.get(channel) : void 0;
|
|
1742
|
+
if (!channel || !cursor) return;
|
|
1743
|
+
requestJsonRpc("realtime.ack", {
|
|
1744
|
+
channel,
|
|
1745
|
+
cursor
|
|
1746
|
+
}).catch(() => void 0);
|
|
1747
|
+
emitTrace({
|
|
1748
|
+
direction: "outbound",
|
|
1749
|
+
phase: "realtime.ack",
|
|
1750
|
+
appSessionId,
|
|
1751
|
+
channel
|
|
1752
|
+
});
|
|
1753
|
+
};
|
|
1754
|
+
const acknowledgeSeedEventRealtimeFrame = (frame) => {
|
|
1755
|
+
if (!autoAckRealtime) return;
|
|
1756
|
+
const channel = readString$1((isRecord$3(frame.meta) ? frame.meta : void 0)?.channel);
|
|
1757
|
+
const cursor = channel ? channelCursors.get(channel) : void 0;
|
|
1758
|
+
if (!channel || !cursor) return;
|
|
1759
|
+
requestJsonRpc("realtime.ack", {
|
|
1760
|
+
channel,
|
|
1761
|
+
cursor
|
|
1762
|
+
}).catch(() => void 0);
|
|
1763
|
+
emitTrace({
|
|
1764
|
+
direction: "outbound",
|
|
1765
|
+
phase: "realtime.ack",
|
|
1766
|
+
appSessionId,
|
|
1767
|
+
channel
|
|
1768
|
+
});
|
|
1769
|
+
};
|
|
1770
|
+
attachSocket(socket);
|
|
1771
|
+
realtimeRecoveryTimer = setInterval(() => {
|
|
1772
|
+
if (closed || socket.readyState !== WEBSOCKET_OPEN_STATE) return;
|
|
1773
|
+
subscribeConfiguredRealtimeChannels();
|
|
1774
|
+
}, REALTIME_RECOVERY_INTERVAL_MS);
|
|
1775
|
+
const applyBridgeSubscriptionResult = (subscription, result) => {
|
|
1776
|
+
subscription.serverSubscriptionId = readBridgeSubscriptionId$4(result);
|
|
1777
|
+
if (!isRecord$3(result)) return;
|
|
1778
|
+
const channel = readString$1(result.channel);
|
|
1779
|
+
if (!channel) return;
|
|
1780
|
+
subscription.channel = channel;
|
|
1781
|
+
realtimeChannels.add(channel);
|
|
1782
|
+
updateRealtimeSnapshotCursor(result);
|
|
1783
|
+
if (Array.isArray(result.events) && result.events.length > 0) emitRealtimeSnapshot(result, eventHandlers);
|
|
1784
|
+
};
|
|
1785
|
+
const resubscribeBridgeEvents = () => {
|
|
1786
|
+
for (const [localSubscriptionId, subscription] of bridgeEventSubscriptions) requestJsonRpc("seed.event.subscribe", buildBridgeSubscribeParams$4(subscription.event, subscription.params), subscription.requestOptions).then((result) => {
|
|
1787
|
+
const active = bridgeEventSubscriptions.get(localSubscriptionId);
|
|
1788
|
+
if (active === subscription) applyBridgeSubscriptionResult(active, result);
|
|
1789
|
+
}).catch(() => void 0);
|
|
1790
|
+
};
|
|
1791
|
+
return {
|
|
1792
|
+
request(method, params, requestOptions) {
|
|
1793
|
+
return requestJsonRpc(method, withAppSession(params, appSessionId), requestOptions);
|
|
1794
|
+
},
|
|
1795
|
+
subscribe(handler) {
|
|
1796
|
+
eventHandlers.add(handler);
|
|
1797
|
+
return () => eventHandlers.delete(handler);
|
|
1798
|
+
},
|
|
1799
|
+
subscribeRealtimeChannel(channel, cursor) {
|
|
1800
|
+
if (cursor) channelCursors.set(channel, cursor);
|
|
1801
|
+
realtimeChannels.add(channel);
|
|
1802
|
+
sendRealtimeSubscribeFrame(channel, appSessionId);
|
|
1803
|
+
return () => {
|
|
1804
|
+
realtimeChannels.delete(channel);
|
|
1805
|
+
requestJsonRpc("realtime.unsubscribe", { channel }).catch(() => void 0);
|
|
1806
|
+
};
|
|
1807
|
+
},
|
|
1808
|
+
async subscribeBridgeEvent(event, handler, requestOptions, params) {
|
|
1809
|
+
const scopedHandler = (nextEvent) => {
|
|
1810
|
+
if (matchesSeedAppHostEventSubscription(event, nextEvent)) handler(nextEvent);
|
|
1811
|
+
};
|
|
1812
|
+
eventHandlers.add(scopedHandler);
|
|
1813
|
+
const localSubscriptionId = nextBridgeEventSubscriptionId;
|
|
1814
|
+
nextBridgeEventSubscriptionId += 1;
|
|
1815
|
+
try {
|
|
1816
|
+
const result = await requestJsonRpc("seed.event.subscribe", buildBridgeSubscribeParams$4(event, params), requestOptions);
|
|
1817
|
+
const subscription = {
|
|
1818
|
+
event,
|
|
1819
|
+
...params ? { params } : {},
|
|
1820
|
+
...requestOptions ? { requestOptions } : {}
|
|
1821
|
+
};
|
|
1822
|
+
applyBridgeSubscriptionResult(subscription, result);
|
|
1823
|
+
bridgeEventSubscriptions.set(localSubscriptionId, subscription);
|
|
1824
|
+
return () => {
|
|
1825
|
+
eventHandlers.delete(scopedHandler);
|
|
1826
|
+
bridgeEventSubscriptions.delete(localSubscriptionId);
|
|
1827
|
+
const serverSubscriptionId = subscription.serverSubscriptionId;
|
|
1828
|
+
if (!serverSubscriptionId) return;
|
|
1829
|
+
requestJsonRpc("seed.event.unsubscribe", buildBridgeUnsubscribeParams$4(event, serverSubscriptionId), requestOptions).catch(() => void 0);
|
|
1830
|
+
};
|
|
1831
|
+
} catch (error) {
|
|
1832
|
+
eventHandlers.delete(scopedHandler);
|
|
1833
|
+
throw error;
|
|
1834
|
+
}
|
|
1835
|
+
},
|
|
1836
|
+
close() {
|
|
1837
|
+
if (closed) return;
|
|
1838
|
+
closed = true;
|
|
1839
|
+
clearReconnectTimer();
|
|
1840
|
+
clearRealtimeRecoveryTimer();
|
|
1841
|
+
detachSocket(socket);
|
|
1842
|
+
socket.close();
|
|
1843
|
+
rejectAll(pending, "host_unavailable", "Seed App endpoint transport closed.");
|
|
1844
|
+
eventHandlers.clear();
|
|
1845
|
+
bridgeEventSubscriptions.clear();
|
|
1846
|
+
}
|
|
1847
|
+
};
|
|
1848
|
+
}
|
|
1849
|
+
function buildBridgeSubscribeParams$4(event, params) {
|
|
1850
|
+
return {
|
|
1851
|
+
...params,
|
|
1852
|
+
type: event
|
|
1853
|
+
};
|
|
1854
|
+
}
|
|
1855
|
+
function buildBridgeUnsubscribeParams$4(event, subscriptionId) {
|
|
1856
|
+
return subscriptionId ? {
|
|
1857
|
+
type: event,
|
|
1858
|
+
subscriptionId
|
|
1859
|
+
} : { type: event };
|
|
1860
|
+
}
|
|
1861
|
+
function readBridgeSubscriptionId$4(value) {
|
|
1862
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
1863
|
+
const subscriptionId = value.subscriptionId;
|
|
1864
|
+
return typeof subscriptionId === "string" && subscriptionId.trim() ? subscriptionId.trim() : void 0;
|
|
1865
|
+
}
|
|
1866
|
+
function resolveReconnectDelayMs(reconnectDelayMs, attempt) {
|
|
1867
|
+
if (typeof reconnectDelayMs === "function") return Math.max(0, reconnectDelayMs(attempt));
|
|
1868
|
+
if (typeof reconnectDelayMs === "number") return Math.max(0, reconnectDelayMs);
|
|
1869
|
+
return Math.min(3e4, 500 * 2 ** Math.max(0, attempt - 1));
|
|
1870
|
+
}
|
|
1871
|
+
function syncHandshakeAppSession(id, payload, pending, onAppSession) {
|
|
1872
|
+
if (pending.get(id)?.method !== "seed.handshake" || !isRecord$3(payload) || typeof payload.appSessionId !== "string") return;
|
|
1873
|
+
onAppSession(payload.appSessionId);
|
|
1874
|
+
}
|
|
1875
|
+
function createDefaultWebSocket(url) {
|
|
1876
|
+
const ctor = globalThis.WebSocket;
|
|
1877
|
+
if (!ctor) throw createSeedAppError({
|
|
1878
|
+
code: "host_unavailable",
|
|
1879
|
+
message: "WebSocket is not available in this runtime.",
|
|
1880
|
+
requestId: "endpoint-unavailable"
|
|
1881
|
+
});
|
|
1882
|
+
return new ctor(url);
|
|
1883
|
+
}
|
|
1884
|
+
function withAppSession(params, appSessionId) {
|
|
1885
|
+
if (!appSessionId) return params;
|
|
1886
|
+
if (isRecord$3(params)) return params.appSessionId === void 0 ? {
|
|
1887
|
+
...params,
|
|
1888
|
+
appSessionId
|
|
1889
|
+
} : params;
|
|
1890
|
+
if (params === void 0) return { appSessionId };
|
|
1891
|
+
return params;
|
|
1892
|
+
}
|
|
1893
|
+
function createFrameTraceEvent(phase, direction, frame, sizeBytes, appSessionId, options, pending) {
|
|
1894
|
+
const record = isRecord$3(frame) ? frame : void 0;
|
|
1895
|
+
const params = isRecord$3(record?.params) ? record.params : void 0;
|
|
1896
|
+
const error = isRecord$3(record?.error) ? record.error : void 0;
|
|
1897
|
+
const errorData = isRecord$3(error?.data) ? error.data : void 0;
|
|
1898
|
+
const frameType = readJsonRpcFrameType(record) ?? readString$1(record?.type);
|
|
1899
|
+
const id = readString$1(record?.id) ?? (typeof record?.id === "number" ? String(record.id) : void 0);
|
|
1900
|
+
const pendingRequest = id ? pending?.get(id) : void 0;
|
|
1901
|
+
const method = readString$1(record?.method) ?? pendingRequest?.method;
|
|
1902
|
+
return stripUndefined({
|
|
1903
|
+
direction,
|
|
1904
|
+
phase,
|
|
1905
|
+
appSessionId,
|
|
1906
|
+
capability: method ? resolveMethodCapability(method) : void 0,
|
|
1907
|
+
channel: readString$1(params?.channel),
|
|
1908
|
+
errorCode: frameType === "jsonrpc.error" ? readString$1(errorData?.code) : void 0,
|
|
1909
|
+
frameType,
|
|
1910
|
+
id,
|
|
1911
|
+
method,
|
|
1912
|
+
payloadPreview: options.includeTracePayloads ? frame : void 0,
|
|
1913
|
+
sizeBytes,
|
|
1914
|
+
status: readFrameTraceStatus(phase, frameType),
|
|
1915
|
+
traceId: pendingRequest?.traceId ?? id
|
|
1916
|
+
});
|
|
1917
|
+
}
|
|
1918
|
+
function resolveMethodCapability(method) {
|
|
1919
|
+
return (0, _seed_app_studio_protocol.getSeedBridgeCapabilityForMethod)(method);
|
|
1920
|
+
}
|
|
1921
|
+
function readJsonRpcFrameType(record) {
|
|
1922
|
+
if (record?.jsonrpc === "2.0") {
|
|
1923
|
+
if (typeof record.method === "string") return record.id === void 0 ? "jsonrpc.notification" : "jsonrpc.request";
|
|
1924
|
+
if (isRecord$3(record.error)) return "jsonrpc.error";
|
|
1925
|
+
if (Object.prototype.hasOwnProperty.call(record, "result")) return "jsonrpc.response";
|
|
1926
|
+
}
|
|
1927
|
+
if (record?.type === "request") return "jsonrpc.request";
|
|
1928
|
+
if (record?.type === "response") return "jsonrpc.response";
|
|
1929
|
+
if (record?.type === "error") return "jsonrpc.error";
|
|
1930
|
+
if (record?.type === "notification") return "jsonrpc.notification";
|
|
1931
|
+
}
|
|
1932
|
+
function readFrameTraceStatus(phase, frameType) {
|
|
1933
|
+
if (frameType === "jsonrpc.error") return "failed";
|
|
1934
|
+
if (phase === "frame.receive") return "completed";
|
|
1935
|
+
if (phase === "frame.queue") return "received";
|
|
1936
|
+
}
|
|
1937
|
+
function summarizeEndpointForTrace(endpoint) {
|
|
1938
|
+
try {
|
|
1939
|
+
const url = new URL(endpoint);
|
|
1940
|
+
return {
|
|
1941
|
+
origin: url.origin,
|
|
1942
|
+
pathname: url.pathname
|
|
1943
|
+
};
|
|
1944
|
+
} catch {
|
|
1945
|
+
return {};
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
function readFrameSize(data) {
|
|
1949
|
+
return typeof data === "string" ? data.length : 0;
|
|
1950
|
+
}
|
|
1951
|
+
function toJsonRpcParams$2(params) {
|
|
1952
|
+
if (params === void 0) return void 0;
|
|
1953
|
+
if (isRecord$3(params)) return params;
|
|
1954
|
+
return { value: params };
|
|
1955
|
+
}
|
|
1956
|
+
function isJsonRpcSerializableFrame(frame) {
|
|
1957
|
+
return isRecord$3(frame) && frame.jsonrpc === "2.0";
|
|
1958
|
+
}
|
|
1959
|
+
function stripUndefined(input) {
|
|
1960
|
+
return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== void 0));
|
|
1961
|
+
}
|
|
1962
|
+
function readString$1(value) {
|
|
1963
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
1964
|
+
}
|
|
1965
|
+
function readPayloadStatus(value) {
|
|
1966
|
+
return isRecord$3(value) ? readString$1(value.status) : void 0;
|
|
1967
|
+
}
|
|
1968
|
+
function settleSuccess(id, payload, pending) {
|
|
1969
|
+
const request = pending.get(id);
|
|
1970
|
+
if (!request) return;
|
|
1971
|
+
pending.delete(id);
|
|
1972
|
+
clearTimeout(request.timeout);
|
|
1973
|
+
request.resolve(payload);
|
|
1974
|
+
}
|
|
1975
|
+
function settleJsonRpcFailure(id, error, pending) {
|
|
1976
|
+
const requestId = id === null ? void 0 : String(id);
|
|
1977
|
+
const request = requestId ? pending.get(requestId) : void 0;
|
|
1978
|
+
if (!requestId || !request) return;
|
|
1979
|
+
pending.delete(requestId);
|
|
1980
|
+
clearTimeout(request.timeout);
|
|
1981
|
+
const data = isRecord$3(error.data) ? error.data : void 0;
|
|
1982
|
+
request.reject(createSeedAppError({
|
|
1983
|
+
code: toSeedAppErrorCode(readString$1(data?.code)),
|
|
1984
|
+
message: error.message,
|
|
1985
|
+
requestId: readString$1(data?.requestId) ?? requestId,
|
|
1986
|
+
retryable: typeof data?.retryable === "boolean" ? data.retryable : error.code === -32603,
|
|
1987
|
+
operationId: readString$1(data?.operationId),
|
|
1988
|
+
details: data?.details
|
|
1989
|
+
}));
|
|
1990
|
+
}
|
|
1991
|
+
function emitSeedEventNotification(params, handlers) {
|
|
1992
|
+
const projected = (0, _seed_app_studio_protocol.projectSeedEventParamsToClientEvent)(params);
|
|
1993
|
+
if (projected) emitEvent(projected, handlers);
|
|
1994
|
+
}
|
|
1995
|
+
function emitRealtimeEvent(frame, handlers) {
|
|
1996
|
+
const event = isRecord$3(frame.event) ? frame.event : void 0;
|
|
1997
|
+
if (!event) return;
|
|
1998
|
+
const typed = toSeedAppEvent$1(isRecord$3(event.payload) ? event.payload : void 0);
|
|
1999
|
+
if (typed) {
|
|
2000
|
+
emitEvent(typed, handlers);
|
|
2001
|
+
return;
|
|
2002
|
+
}
|
|
2003
|
+
if (typeof event.type === "string") emitEvent(stripUndefined({
|
|
2004
|
+
type: event.type,
|
|
2005
|
+
payload: event.payload,
|
|
2006
|
+
status: readString$1(event.status) ?? readPayloadStatus(event.payload),
|
|
2007
|
+
timestamp: Date.now()
|
|
2008
|
+
}), handlers);
|
|
2009
|
+
}
|
|
2010
|
+
function emitRealtimeSnapshot(frame, handlers) {
|
|
2011
|
+
if (!Array.isArray(frame.events)) return;
|
|
2012
|
+
for (const item of frame.events) emitRealtimeEvent({ event: item }, handlers);
|
|
2013
|
+
}
|
|
2014
|
+
function toSeedAppEvent$1(value) {
|
|
2015
|
+
if (!value || typeof value.type !== "string") return void 0;
|
|
2016
|
+
return stripUndefined({
|
|
2017
|
+
type: value.type,
|
|
2018
|
+
payload: value.payload,
|
|
2019
|
+
status: readString$1(value.status) ?? readPayloadStatus(value.payload),
|
|
2020
|
+
timestamp: typeof value.timestamp === "number" ? value.timestamp : Date.now()
|
|
2021
|
+
});
|
|
2022
|
+
}
|
|
2023
|
+
function emitEvent(event, handlers) {
|
|
2024
|
+
handlers.forEach((handler) => handler(event));
|
|
2025
|
+
}
|
|
2026
|
+
function rejectAll(pending, code, message, retryable = false) {
|
|
2027
|
+
pending.forEach((request, id) => {
|
|
2028
|
+
clearTimeout(request.timeout);
|
|
2029
|
+
request.reject(createSeedAppError({
|
|
2030
|
+
code,
|
|
2031
|
+
message,
|
|
2032
|
+
requestId: id,
|
|
2033
|
+
retryable
|
|
2034
|
+
}));
|
|
2035
|
+
});
|
|
2036
|
+
pending.clear();
|
|
2037
|
+
}
|
|
2038
|
+
function parseFrame(data) {
|
|
2039
|
+
if (typeof data !== "string") return void 0;
|
|
2040
|
+
return (0, _seed_app_studio_protocol.deserializeJsonRpcFrame)(data);
|
|
2041
|
+
}
|
|
2042
|
+
function toSeedAppErrorCode(value) {
|
|
2043
|
+
return normalizeSeedAppErrorCode(value);
|
|
2044
|
+
}
|
|
2045
|
+
function isRecord$3(value) {
|
|
2046
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2047
|
+
}
|
|
2048
|
+
//#endregion
|
|
2049
|
+
//#region src/electronTransport.ts
|
|
2050
|
+
function createSeedAppElectronTransport(options = {}) {
|
|
2051
|
+
const electronAPI = options.electronAPI ?? readGlobalElectronApi();
|
|
2052
|
+
if (!electronAPI) throw createSeedAppError({
|
|
2053
|
+
code: "host_unavailable",
|
|
2054
|
+
message: "Electron transport requires window.electronAPI.",
|
|
2055
|
+
requestId: "electron-unavailable"
|
|
2056
|
+
});
|
|
2057
|
+
const eventHandlers = /* @__PURE__ */ new Set();
|
|
2058
|
+
let nextId = 0;
|
|
2059
|
+
let closed = false;
|
|
2060
|
+
const maybeUnsubscribe = electronAPI.on?.((event) => {
|
|
2061
|
+
const frame = (0, _seed_app_studio_protocol.deserializeJsonRpcFrame)(event.value);
|
|
2062
|
+
if (!frame || frame.type !== "notification") return;
|
|
2063
|
+
if (frame.method !== "seed.event") return;
|
|
2064
|
+
const projectedEvent = (0, _seed_app_studio_protocol.projectSeedEventParamsToClientEvent)(frame.params);
|
|
2065
|
+
if (projectedEvent) eventHandlers.forEach((handler) => handler(projectedEvent));
|
|
2066
|
+
});
|
|
2067
|
+
const unsubscribe = typeof maybeUnsubscribe === "function" ? maybeUnsubscribe : void 0;
|
|
2068
|
+
const requestBridge = (method, params, requestOptions) => {
|
|
2069
|
+
if (closed) return Promise.reject(createSeedAppError({
|
|
2070
|
+
code: "host_unavailable",
|
|
2071
|
+
message: "Electron transport is closed.",
|
|
2072
|
+
requestId: method
|
|
2073
|
+
}));
|
|
2074
|
+
const id = `${options.idPrefix ?? "seed-electron"}-${Date.now()}-${nextId}`;
|
|
2075
|
+
nextId += 1;
|
|
2076
|
+
const serialized = (0, _seed_app_studio_protocol.serializeJsonRpcFrame)((0, _seed_app_studio_protocol.createJsonRpcRequest)(method, id, shouldPropagateTraceForMethod(method) ? ensureSeedAppTraceCarrier(params) : params));
|
|
2077
|
+
return withTimeout$1(Promise.resolve(electronAPI.emit(serialized)).then((response) => {
|
|
2078
|
+
const resultFrame = (0, _seed_app_studio_protocol.deserializeJsonRpcFrame)(typeof response === "string" ? response : JSON.stringify(response));
|
|
2079
|
+
if (!resultFrame) throw createSeedAppError({
|
|
2080
|
+
code: "invalid_payload_schema",
|
|
2081
|
+
message: "Electron transport expected a JSON-RPC response frame.",
|
|
2082
|
+
requestId: id
|
|
2083
|
+
});
|
|
2084
|
+
if (resultFrame.type === "response") {
|
|
2085
|
+
if (resultFrame.id !== id) throw createSeedAppError({
|
|
2086
|
+
code: "invalid_payload_schema",
|
|
2087
|
+
message: "Electron transport response id did not match the request.",
|
|
2088
|
+
requestId: id
|
|
2089
|
+
});
|
|
2090
|
+
return resultFrame.result;
|
|
2091
|
+
}
|
|
2092
|
+
if (resultFrame.type === "error") {
|
|
2093
|
+
if (resultFrame.id !== id) throw createSeedAppError({
|
|
2094
|
+
code: "invalid_payload_schema",
|
|
2095
|
+
message: "Electron transport error id did not match the request.",
|
|
2096
|
+
requestId: id
|
|
2097
|
+
});
|
|
2098
|
+
throw jsonRpcErrorToSeedAppError(resultFrame.error, id);
|
|
2099
|
+
}
|
|
2100
|
+
throw createSeedAppError({
|
|
2101
|
+
code: "invalid_payload_schema",
|
|
2102
|
+
message: "Electron transport received unexpected frame type.",
|
|
2103
|
+
requestId: id
|
|
2104
|
+
});
|
|
2105
|
+
}), requestOptions?.timeoutMs ?? options.timeoutMs, id);
|
|
2106
|
+
};
|
|
2107
|
+
return {
|
|
2108
|
+
request: requestBridge,
|
|
2109
|
+
subscribe(handler) {
|
|
2110
|
+
eventHandlers.add(handler);
|
|
2111
|
+
return () => eventHandlers.delete(handler);
|
|
2112
|
+
},
|
|
2113
|
+
async subscribeBridgeEvent(event, handler, requestOptions, params) {
|
|
2114
|
+
const scopedHandler = (nextEvent) => {
|
|
2115
|
+
if (matchesSeedAppHostEventSubscription(event, nextEvent)) handler(nextEvent);
|
|
2116
|
+
};
|
|
2117
|
+
eventHandlers.add(scopedHandler);
|
|
2118
|
+
let subscriptionId;
|
|
2119
|
+
try {
|
|
2120
|
+
subscriptionId = readBridgeSubscriptionId$3(await requestBridge("seed.event.subscribe", buildBridgeSubscribeParams$3(event, params), requestOptions));
|
|
2121
|
+
} catch (error) {
|
|
2122
|
+
eventHandlers.delete(scopedHandler);
|
|
2123
|
+
throw error;
|
|
2124
|
+
}
|
|
2125
|
+
return () => {
|
|
2126
|
+
eventHandlers.delete(scopedHandler);
|
|
2127
|
+
requestBridge("seed.event.unsubscribe", buildBridgeUnsubscribeParams$3(event, subscriptionId), requestOptions).catch(() => void 0);
|
|
2128
|
+
};
|
|
2129
|
+
},
|
|
2130
|
+
close() {
|
|
2131
|
+
closed = true;
|
|
2132
|
+
eventHandlers.clear();
|
|
2133
|
+
unsubscribe?.();
|
|
2134
|
+
}
|
|
2135
|
+
};
|
|
2136
|
+
}
|
|
2137
|
+
function shouldPropagateTraceForMethod(method) {
|
|
2138
|
+
return method === "seed.agent.run" || method === "seed.event.subscribe";
|
|
2139
|
+
}
|
|
2140
|
+
function buildBridgeSubscribeParams$3(event, params) {
|
|
2141
|
+
return {
|
|
2142
|
+
...params,
|
|
2143
|
+
type: event
|
|
2144
|
+
};
|
|
2145
|
+
}
|
|
2146
|
+
function buildBridgeUnsubscribeParams$3(event, subscriptionId) {
|
|
2147
|
+
return subscriptionId ? {
|
|
2148
|
+
type: event,
|
|
2149
|
+
subscriptionId
|
|
2150
|
+
} : { type: event };
|
|
2151
|
+
}
|
|
2152
|
+
function readBridgeSubscriptionId$3(value) {
|
|
2153
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
2154
|
+
const subscriptionId = value.subscriptionId;
|
|
2155
|
+
return typeof subscriptionId === "string" && subscriptionId.trim() ? subscriptionId.trim() : void 0;
|
|
2156
|
+
}
|
|
2157
|
+
function readGlobalElectronApi() {
|
|
2158
|
+
return globalThis.window?.electronAPI;
|
|
2159
|
+
}
|
|
2160
|
+
function jsonRpcErrorToSeedAppError(error, requestId) {
|
|
2161
|
+
const data = error.data;
|
|
2162
|
+
return createSeedAppError({
|
|
2163
|
+
code: normalizeSeedErrorCode$1(typeof data?.code === "string" ? data.code : void 0),
|
|
2164
|
+
message: error.message,
|
|
2165
|
+
requestId: typeof data?.requestId === "string" ? data.requestId : requestId,
|
|
2166
|
+
retryable: typeof data?.retryable === "boolean" ? data.retryable : false,
|
|
2167
|
+
capability: typeof data?.capability === "string" ? data.capability : void 0,
|
|
2168
|
+
action: typeof data?.action === "string" ? data.action : void 0,
|
|
2169
|
+
operationId: typeof data?.operationId === "string" ? data.operationId : void 0,
|
|
2170
|
+
details: data?.details
|
|
2171
|
+
});
|
|
2172
|
+
}
|
|
2173
|
+
function normalizeSeedErrorCode$1(code) {
|
|
2174
|
+
return normalizeSeedAppErrorCode(code);
|
|
2175
|
+
}
|
|
2176
|
+
function withTimeout$1(promise, timeoutMs, requestId) {
|
|
2177
|
+
if (timeoutMs === void 0) return promise;
|
|
2178
|
+
return new Promise((resolve, reject) => {
|
|
2179
|
+
const timeout = setTimeout(() => {
|
|
2180
|
+
reject(createSeedAppError({
|
|
2181
|
+
code: "handshake_timeout",
|
|
2182
|
+
message: "Electron transport request timed out.",
|
|
2183
|
+
requestId,
|
|
2184
|
+
retryable: true
|
|
2185
|
+
}));
|
|
2186
|
+
}, timeoutMs);
|
|
2187
|
+
promise.then((value) => {
|
|
2188
|
+
clearTimeout(timeout);
|
|
2189
|
+
resolve(value);
|
|
2190
|
+
}, (error) => {
|
|
2191
|
+
clearTimeout(timeout);
|
|
2192
|
+
reject(error);
|
|
2193
|
+
});
|
|
2194
|
+
});
|
|
2195
|
+
}
|
|
2196
|
+
//#endregion
|
|
2197
|
+
//#region src/reactNativeTransport.ts
|
|
2198
|
+
function createSeedAppReactNativeTransport(options = {}) {
|
|
2199
|
+
const port = options.port ?? readGlobalReactNativeWebViewPort();
|
|
2200
|
+
if (!port) throw createSeedAppError({
|
|
2201
|
+
code: "host_unavailable",
|
|
2202
|
+
message: "React Native transport requires a WebView postMessage port.",
|
|
2203
|
+
requestId: "react-native-unavailable"
|
|
2204
|
+
});
|
|
2205
|
+
const messageTarget = options.messageTarget ?? readGlobalReactNativeMessageTarget();
|
|
2206
|
+
const adapter = (0, _seed_app_studio_protocol.createJsonRpcReactNativeWebViewAdapter)({
|
|
2207
|
+
port,
|
|
2208
|
+
onInvalidFrame: options.onInvalidFrame
|
|
2209
|
+
});
|
|
2210
|
+
const eventHandlers = /* @__PURE__ */ new Set();
|
|
2211
|
+
const pending = /* @__PURE__ */ new Map();
|
|
2212
|
+
let nextId = 0;
|
|
2213
|
+
let closed = false;
|
|
2214
|
+
const requestBridge = (method, params, requestOptions) => {
|
|
2215
|
+
if (closed) return Promise.reject(createSeedAppError({
|
|
2216
|
+
code: "host_unavailable",
|
|
2217
|
+
message: "React Native transport is closed.",
|
|
2218
|
+
requestId: method
|
|
2219
|
+
}));
|
|
2220
|
+
const id = `${options.idPrefix ?? "seed-react-native"}-${Date.now()}-${nextId}`;
|
|
2221
|
+
nextId += 1;
|
|
2222
|
+
const timeoutMs = requestOptions?.timeoutMs ?? options.timeoutMs ?? 3e4;
|
|
2223
|
+
return new Promise((resolve, reject) => {
|
|
2224
|
+
const timeout = setTimeout(() => {
|
|
2225
|
+
pending.delete(id);
|
|
2226
|
+
reject(createSeedAppError({
|
|
2227
|
+
code: "handshake_timeout",
|
|
2228
|
+
message: "React Native transport request timed out.",
|
|
2229
|
+
requestId: id,
|
|
2230
|
+
retryable: true
|
|
2231
|
+
}));
|
|
2232
|
+
}, timeoutMs);
|
|
2233
|
+
pending.set(id, {
|
|
2234
|
+
resolve,
|
|
2235
|
+
reject,
|
|
2236
|
+
timeout
|
|
2237
|
+
});
|
|
2238
|
+
try {
|
|
2239
|
+
adapter.postRequest((0, _seed_app_studio_protocol.createJsonRpcRequest)(method, id, toJsonRpcParams$1(params)));
|
|
2240
|
+
} catch (error) {
|
|
2241
|
+
pending.delete(id);
|
|
2242
|
+
clearTimeout(timeout);
|
|
2243
|
+
reject(createSeedAppError({
|
|
2244
|
+
code: "host_unavailable",
|
|
2245
|
+
message: `React Native transport postMessage failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
2246
|
+
requestId: id,
|
|
2247
|
+
retryable: true
|
|
2248
|
+
}));
|
|
2249
|
+
}
|
|
2250
|
+
});
|
|
2251
|
+
};
|
|
2252
|
+
const handleMessage = (event) => {
|
|
2253
|
+
const rawMessage = readReactNativeMessageData(event);
|
|
2254
|
+
const frame = adapter.parseMessage(rawMessage);
|
|
2255
|
+
if (!frame) return;
|
|
2256
|
+
handleFrame(frame);
|
|
2257
|
+
};
|
|
2258
|
+
messageTarget?.addEventListener("message", handleMessage);
|
|
2259
|
+
return {
|
|
2260
|
+
request: requestBridge,
|
|
2261
|
+
subscribe(handler) {
|
|
2262
|
+
eventHandlers.add(handler);
|
|
2263
|
+
return () => eventHandlers.delete(handler);
|
|
2264
|
+
},
|
|
2265
|
+
async subscribeBridgeEvent(event, handler, requestOptions, params) {
|
|
2266
|
+
const scopedHandler = (nextEvent) => {
|
|
2267
|
+
if (matchesSeedAppHostEventSubscription(event, nextEvent)) handler(nextEvent);
|
|
2268
|
+
};
|
|
2269
|
+
eventHandlers.add(scopedHandler);
|
|
2270
|
+
let subscriptionId;
|
|
2271
|
+
try {
|
|
2272
|
+
subscriptionId = readBridgeSubscriptionId$2(await requestBridge("seed.event.subscribe", buildBridgeSubscribeParams$2(event, params), requestOptions));
|
|
2273
|
+
} catch (error) {
|
|
2274
|
+
eventHandlers.delete(scopedHandler);
|
|
2275
|
+
throw error;
|
|
2276
|
+
}
|
|
2277
|
+
return () => {
|
|
2278
|
+
eventHandlers.delete(scopedHandler);
|
|
2279
|
+
requestBridge("seed.event.unsubscribe", buildBridgeUnsubscribeParams$2(event, subscriptionId), requestOptions).catch(() => void 0);
|
|
2280
|
+
};
|
|
2281
|
+
},
|
|
2282
|
+
close() {
|
|
2283
|
+
closed = true;
|
|
2284
|
+
messageTarget?.removeEventListener("message", handleMessage);
|
|
2285
|
+
pending.forEach((request, id) => {
|
|
2286
|
+
clearTimeout(request.timeout);
|
|
2287
|
+
request.reject(createSeedAppError({
|
|
2288
|
+
code: "host_unavailable",
|
|
2289
|
+
message: "React Native transport closed.",
|
|
2290
|
+
requestId: id
|
|
2291
|
+
}));
|
|
2292
|
+
});
|
|
2293
|
+
pending.clear();
|
|
2294
|
+
eventHandlers.clear();
|
|
2295
|
+
}
|
|
2296
|
+
};
|
|
2297
|
+
function handleFrame(frame) {
|
|
2298
|
+
switch (frame.type) {
|
|
2299
|
+
case "response":
|
|
2300
|
+
settleSuccess(frame.id, frame.result);
|
|
2301
|
+
return;
|
|
2302
|
+
case "error": {
|
|
2303
|
+
const data = frame.error.data;
|
|
2304
|
+
settleFailure(frame.id, createSeedAppError({
|
|
2305
|
+
code: normalizeSeedErrorCode(typeof data?.code === "string" ? data.code : void 0),
|
|
2306
|
+
message: frame.error.message,
|
|
2307
|
+
requestId: typeof data?.requestId === "string" ? data.requestId : frame.id,
|
|
2308
|
+
retryable: typeof data?.retryable === "boolean" ? data.retryable : false,
|
|
2309
|
+
capability: typeof data?.capability === "string" ? data.capability : void 0,
|
|
2310
|
+
action: typeof data?.action === "string" ? data.action : void 0,
|
|
2311
|
+
operationId: typeof data?.operationId === "string" ? data.operationId : void 0,
|
|
2312
|
+
details: data?.details
|
|
2313
|
+
}));
|
|
2314
|
+
return;
|
|
2315
|
+
}
|
|
2316
|
+
case "notification": {
|
|
2317
|
+
if (frame.method !== "seed.event") return;
|
|
2318
|
+
const projected = (0, _seed_app_studio_protocol.projectSeedEventParamsToClientEvent)(frame.params);
|
|
2319
|
+
if (projected) eventHandlers.forEach((handler) => handler(projected));
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
function settleSuccess(id, result) {
|
|
2324
|
+
const request = pending.get(id);
|
|
2325
|
+
if (!request) return;
|
|
2326
|
+
pending.delete(id);
|
|
2327
|
+
clearTimeout(request.timeout);
|
|
2328
|
+
request.resolve(result);
|
|
2329
|
+
}
|
|
2330
|
+
function settleFailure(id, error) {
|
|
2331
|
+
const request = pending.get(id);
|
|
2332
|
+
if (!request) return;
|
|
2333
|
+
pending.delete(id);
|
|
2334
|
+
clearTimeout(request.timeout);
|
|
2335
|
+
request.reject(error);
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
function buildBridgeSubscribeParams$2(event, params) {
|
|
2339
|
+
return {
|
|
2340
|
+
...params,
|
|
2341
|
+
type: event
|
|
2342
|
+
};
|
|
2343
|
+
}
|
|
2344
|
+
function buildBridgeUnsubscribeParams$2(event, subscriptionId) {
|
|
2345
|
+
return subscriptionId ? {
|
|
2346
|
+
type: event,
|
|
2347
|
+
subscriptionId
|
|
2348
|
+
} : { type: event };
|
|
2349
|
+
}
|
|
2350
|
+
function readBridgeSubscriptionId$2(value) {
|
|
2351
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
2352
|
+
const subscriptionId = value.subscriptionId;
|
|
2353
|
+
return typeof subscriptionId === "string" && subscriptionId.trim() ? subscriptionId.trim() : void 0;
|
|
2354
|
+
}
|
|
2355
|
+
function readGlobalReactNativeWebViewPort() {
|
|
2356
|
+
return globalThis.window?.ReactNativeWebView;
|
|
2357
|
+
}
|
|
2358
|
+
function readGlobalReactNativeMessageTarget() {
|
|
2359
|
+
return globalThis.window;
|
|
2360
|
+
}
|
|
2361
|
+
function readReactNativeMessageData(event) {
|
|
2362
|
+
if (typeof event === "string") return event;
|
|
2363
|
+
return event.nativeEvent?.data ?? event.data;
|
|
2364
|
+
}
|
|
2365
|
+
function toJsonRpcParams$1(params) {
|
|
2366
|
+
if (params === void 0) return void 0;
|
|
2367
|
+
if (typeof params === "object" && params !== null && !Array.isArray(params)) return params;
|
|
2368
|
+
return { value: params };
|
|
2369
|
+
}
|
|
2370
|
+
function normalizeSeedErrorCode(code) {
|
|
2371
|
+
return normalizeSeedAppErrorCode(code);
|
|
2372
|
+
}
|
|
2373
|
+
//#endregion
|
|
2374
|
+
//#region src/webSocketTransport.ts
|
|
2375
|
+
function createSeedAppWebSocketTransport(options) {
|
|
2376
|
+
const getWebSocketImpl = () => {
|
|
2377
|
+
return options.WebSocket ?? globalThis.WebSocket;
|
|
2378
|
+
};
|
|
2379
|
+
const eventHandlers = /* @__PURE__ */ new Set();
|
|
2380
|
+
const pending = /* @__PURE__ */ new Map();
|
|
2381
|
+
const subscribedRealtimeChannels = /* @__PURE__ */ new Map();
|
|
2382
|
+
const sendQueue = [];
|
|
2383
|
+
let nextId = 0;
|
|
2384
|
+
let closed = false;
|
|
2385
|
+
let ws = null;
|
|
2386
|
+
let wsReady = false;
|
|
2387
|
+
const defaultTimeoutMs = options.timeoutMs ?? 3e4;
|
|
2388
|
+
const idPrefix = options.idPrefix ?? "seed-ws";
|
|
2389
|
+
function generateId() {
|
|
2390
|
+
nextId += 1;
|
|
2391
|
+
return `${idPrefix}-${Date.now()}-${nextId}`;
|
|
2392
|
+
}
|
|
2393
|
+
async function resolveToken() {
|
|
2394
|
+
if (typeof options.token === "function") return await options.token();
|
|
2395
|
+
return options.token;
|
|
2396
|
+
}
|
|
2397
|
+
async function buildUrl() {
|
|
2398
|
+
const token = await resolveToken();
|
|
2399
|
+
let resolvedUrl = options.url;
|
|
2400
|
+
if (typeof globalThis.location !== "undefined" && !/^https?:\/\//i.test(resolvedUrl) && !/^wss?:\/\//i.test(resolvedUrl)) {
|
|
2401
|
+
const origin = globalThis.location.origin;
|
|
2402
|
+
resolvedUrl = resolvedUrl.startsWith("/") ? `${origin.replace(/^http/, "ws")}${resolvedUrl}` : `${origin.replace(/^http/, "ws")}/${resolvedUrl}`;
|
|
2403
|
+
}
|
|
2404
|
+
const url = new URL(resolvedUrl);
|
|
2405
|
+
if (token) url.searchParams.set("token", token);
|
|
2406
|
+
return url.toString();
|
|
2407
|
+
}
|
|
2408
|
+
function connect() {
|
|
2409
|
+
if (closed) return;
|
|
2410
|
+
if (!getWebSocketImpl()) return;
|
|
2411
|
+
buildUrl().then((wsUrl) => {
|
|
2412
|
+
if (closed) return;
|
|
2413
|
+
const WsCtor = getWebSocketImpl();
|
|
2414
|
+
if (!WsCtor) return;
|
|
2415
|
+
try {
|
|
2416
|
+
ws = new WsCtor(wsUrl);
|
|
2417
|
+
} catch (error) {
|
|
2418
|
+
const seedError = createSeedAppError({
|
|
2419
|
+
code: "host_unavailable",
|
|
2420
|
+
message: `WebSocket connection failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
2421
|
+
requestId: "ws-connect",
|
|
2422
|
+
retryable: true
|
|
2423
|
+
});
|
|
2424
|
+
rejectAllPending(seedError);
|
|
2425
|
+
rejectAllRealtimeSubscriptions(seedError);
|
|
2426
|
+
return;
|
|
2427
|
+
}
|
|
2428
|
+
attachWsHandlers();
|
|
2429
|
+
}).catch((error) => {
|
|
2430
|
+
const seedError = createSeedAppError({
|
|
2431
|
+
code: "host_unavailable",
|
|
2432
|
+
message: `WebSocket URL build failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
2433
|
+
requestId: "ws-url-build",
|
|
2434
|
+
retryable: true
|
|
2435
|
+
});
|
|
2436
|
+
rejectAllPending(seedError);
|
|
2437
|
+
rejectAllRealtimeSubscriptions(seedError);
|
|
2438
|
+
});
|
|
2439
|
+
}
|
|
2440
|
+
function attachWsHandlers() {
|
|
2441
|
+
if (!ws) return;
|
|
2442
|
+
ws.onmessage = (event) => {
|
|
2443
|
+
const frame = (0, _seed_app_studio_protocol.deserializeJsonRpcFrame)(event.data);
|
|
2444
|
+
if (!frame) return;
|
|
2445
|
+
handleServerFrame(frame);
|
|
2446
|
+
};
|
|
2447
|
+
ws.onclose = () => {
|
|
2448
|
+
wsReady = false;
|
|
2449
|
+
if (!closed) {
|
|
2450
|
+
const seedError = createSeedAppError({
|
|
2451
|
+
code: "host_unavailable",
|
|
2452
|
+
message: "WebSocket connection closed unexpectedly.",
|
|
2453
|
+
requestId: "ws-close",
|
|
2454
|
+
retryable: true
|
|
2455
|
+
});
|
|
2456
|
+
rejectAllPending(seedError);
|
|
2457
|
+
rejectAllRealtimeSubscriptions(seedError);
|
|
2458
|
+
}
|
|
2459
|
+
};
|
|
2460
|
+
ws.onerror = () => {
|
|
2461
|
+
const seedError = createSeedAppError({
|
|
2462
|
+
code: "host_unavailable",
|
|
2463
|
+
message: "WebSocket connection error.",
|
|
2464
|
+
requestId: "ws-error",
|
|
2465
|
+
retryable: true
|
|
2466
|
+
});
|
|
2467
|
+
rejectAllPending(seedError);
|
|
2468
|
+
rejectAllRealtimeSubscriptions(seedError);
|
|
2469
|
+
};
|
|
2470
|
+
ws.onopen = () => {
|
|
2471
|
+
wsReady = true;
|
|
2472
|
+
for (const [channel, subscription] of subscribedRealtimeChannels) sendRealtimeSubscribeFrame(channel, subscription);
|
|
2473
|
+
flushSendQueue();
|
|
2474
|
+
};
|
|
2475
|
+
}
|
|
2476
|
+
function flushSendQueue() {
|
|
2477
|
+
while (sendQueue.length > 0) {
|
|
2478
|
+
const frame = sendQueue.shift();
|
|
2479
|
+
try {
|
|
2480
|
+
ws?.send(frame);
|
|
2481
|
+
} catch {
|
|
2482
|
+
sendQueue.unshift(frame);
|
|
2483
|
+
break;
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
function sendSerialized(serialized) {
|
|
2488
|
+
if (!wsReady || !ws) {
|
|
2489
|
+
sendQueue.push(serialized);
|
|
2490
|
+
return;
|
|
2491
|
+
}
|
|
2492
|
+
ws.send(serialized);
|
|
2493
|
+
}
|
|
2494
|
+
function requestJsonRpc(method, params, requestOptions) {
|
|
2495
|
+
if (closed) return Promise.reject(createSeedAppError({
|
|
2496
|
+
code: "host_unavailable",
|
|
2497
|
+
message: "WebSocket transport is closed.",
|
|
2498
|
+
requestId: method
|
|
2499
|
+
}));
|
|
2500
|
+
ensureConnected();
|
|
2501
|
+
const id = generateId();
|
|
2502
|
+
const timeoutMs = requestOptions?.timeoutMs ?? defaultTimeoutMs;
|
|
2503
|
+
return new Promise((resolve, reject) => {
|
|
2504
|
+
const timeout = setTimeout(() => {
|
|
2505
|
+
pending.delete(id);
|
|
2506
|
+
reject(createSeedAppError({
|
|
2507
|
+
code: "handshake_timeout",
|
|
2508
|
+
message: "WebSocket request timed out.",
|
|
2509
|
+
requestId: id,
|
|
2510
|
+
retryable: true
|
|
2511
|
+
}));
|
|
2512
|
+
}, timeoutMs);
|
|
2513
|
+
pending.set(id, {
|
|
2514
|
+
resolve,
|
|
2515
|
+
reject,
|
|
2516
|
+
timeout
|
|
2517
|
+
});
|
|
2518
|
+
try {
|
|
2519
|
+
sendSerialized((0, _seed_app_studio_protocol.serializeJsonRpcFrame)((0, _seed_app_studio_protocol.createJsonRpcRequest)(method, id, toJsonRpcParams(params))));
|
|
2520
|
+
} catch (error) {
|
|
2521
|
+
pending.delete(id);
|
|
2522
|
+
clearTimeout(timeout);
|
|
2523
|
+
reject(error);
|
|
2524
|
+
}
|
|
2525
|
+
});
|
|
2526
|
+
}
|
|
2527
|
+
function handleServerFrame(frame) {
|
|
2528
|
+
switch (frame.type) {
|
|
2529
|
+
case "response": {
|
|
2530
|
+
const request = pending.get(frame.id);
|
|
2531
|
+
if (!request) return;
|
|
2532
|
+
pending.delete(frame.id);
|
|
2533
|
+
clearTimeout(request.timeout);
|
|
2534
|
+
request.resolve(frame.result);
|
|
2535
|
+
return;
|
|
2536
|
+
}
|
|
2537
|
+
case "error": {
|
|
2538
|
+
const request = pending.get(frame.id);
|
|
2539
|
+
if (!request) return;
|
|
2540
|
+
pending.delete(frame.id);
|
|
2541
|
+
clearTimeout(request.timeout);
|
|
2542
|
+
const data = frame.error.data;
|
|
2543
|
+
request.reject(createSeedAppError({
|
|
2544
|
+
code: normalizeErrorCode(typeof data?.code === "string" ? data.code : void 0),
|
|
2545
|
+
message: frame.error.message,
|
|
2546
|
+
requestId: typeof data?.requestId === "string" ? data.requestId : frame.id,
|
|
2547
|
+
retryable: typeof data?.retryable === "boolean" ? data.retryable : isRetryableJsonRpcCode(frame.error.code),
|
|
2548
|
+
operationId: typeof data?.operationId === "string" ? data.operationId : void 0,
|
|
2549
|
+
details: data?.details
|
|
2550
|
+
}));
|
|
2551
|
+
return;
|
|
2552
|
+
}
|
|
2553
|
+
case "notification":
|
|
2554
|
+
if (frame.method === "seed.event") {
|
|
2555
|
+
const projected = (0, _seed_app_studio_protocol.projectSeedEventParamsToClientEvent)(frame.params);
|
|
2556
|
+
if (projected) eventHandlers.forEach((handler) => handler(projected));
|
|
2557
|
+
return;
|
|
2558
|
+
}
|
|
2559
|
+
if (frame.method === "realtime.snapshot") {
|
|
2560
|
+
const params = frame.params;
|
|
2561
|
+
const channel = typeof params?.channel === "string" ? params.channel : void 0;
|
|
2562
|
+
handleRealtimeSnapshot(params);
|
|
2563
|
+
resolveRealtimeSubscription(channel);
|
|
2564
|
+
return;
|
|
2565
|
+
}
|
|
2566
|
+
if (frame.method === "realtime.ack.ok") {
|
|
2567
|
+
resolveRealtimeSubscription(typeof frame.params?.channel === "string" ? frame.params.channel : void 0);
|
|
2568
|
+
return;
|
|
2569
|
+
}
|
|
2570
|
+
if (frame.method === "realtime.error") {
|
|
2571
|
+
const params = frame.params;
|
|
2572
|
+
const channel = typeof params?.channel === "string" ? params.channel : void 0;
|
|
2573
|
+
rejectRealtimeSubscription(channel, createSeedAppError({
|
|
2574
|
+
code: normalizeErrorCode(typeof params?.code === "string" ? params.code : "provider_failed"),
|
|
2575
|
+
message: typeof params?.message === "string" ? params.message : "Realtime subscription failed.",
|
|
2576
|
+
requestId: channel ?? "realtime.subscribe",
|
|
2577
|
+
retryable: true
|
|
2578
|
+
}));
|
|
2579
|
+
return;
|
|
2580
|
+
}
|
|
2581
|
+
if (frame.method === "pong") return;
|
|
2582
|
+
return;
|
|
2583
|
+
}
|
|
2584
|
+
}
|
|
2585
|
+
function rejectAllPending(error) {
|
|
2586
|
+
const entries = Array.from(pending.entries());
|
|
2587
|
+
pending.clear();
|
|
2588
|
+
for (const [, request] of entries) {
|
|
2589
|
+
clearTimeout(request.timeout);
|
|
2590
|
+
request.reject(error);
|
|
2591
|
+
}
|
|
2592
|
+
}
|
|
2593
|
+
function createRealtimeSubscription(channel, cursor) {
|
|
2594
|
+
let resolveReady = () => void 0;
|
|
2595
|
+
let rejectReady = (_error) => void 0;
|
|
2596
|
+
const subscription = {
|
|
2597
|
+
acknowledged: false,
|
|
2598
|
+
cursor,
|
|
2599
|
+
ready: new Promise((resolve, reject) => {
|
|
2600
|
+
resolveReady = resolve;
|
|
2601
|
+
rejectReady = reject;
|
|
2602
|
+
}),
|
|
2603
|
+
refCount: 1,
|
|
2604
|
+
rejectReady,
|
|
2605
|
+
resolveReady
|
|
2606
|
+
};
|
|
2607
|
+
subscription.ackTimeout = setTimeout(() => {
|
|
2608
|
+
const current = subscribedRealtimeChannels.get(channel);
|
|
2609
|
+
if (current !== subscription || current.acknowledged) return;
|
|
2610
|
+
subscribedRealtimeChannels.delete(channel);
|
|
2611
|
+
current.rejectReady(createSeedAppError({
|
|
2612
|
+
code: "handshake_timeout",
|
|
2613
|
+
message: "Realtime subscription timed out before acknowledgement.",
|
|
2614
|
+
requestId: channel,
|
|
2615
|
+
retryable: true
|
|
2616
|
+
}));
|
|
2617
|
+
}, defaultTimeoutMs);
|
|
2618
|
+
return subscription;
|
|
2619
|
+
}
|
|
2620
|
+
function sendRealtimeSubscribeFrame(channel, subscription) {
|
|
2621
|
+
const params = { channel };
|
|
2622
|
+
if (subscription.cursor) params.cursor = subscription.cursor;
|
|
2623
|
+
requestJsonRpc("realtime.subscribe", params).then((snapshot) => {
|
|
2624
|
+
handleRealtimeSnapshot(snapshot);
|
|
2625
|
+
resolveRealtimeSubscription(channel);
|
|
2626
|
+
}).catch((error) => {
|
|
2627
|
+
rejectRealtimeSubscription(channel, normalizeSeedAppError(error, channel));
|
|
2628
|
+
});
|
|
2629
|
+
}
|
|
2630
|
+
function handleRealtimeSnapshot(params) {
|
|
2631
|
+
(Array.isArray(params?.events) ? params.events : []).map(projectRealtimeEvent).filter(isSeedAppEvent).forEach((event) => {
|
|
2632
|
+
eventHandlers.forEach((handler) => handler(event));
|
|
2633
|
+
});
|
|
2634
|
+
}
|
|
2635
|
+
function resolveRealtimeSubscription(channel) {
|
|
2636
|
+
if (!channel) return;
|
|
2637
|
+
const subscription = subscribedRealtimeChannels.get(channel);
|
|
2638
|
+
if (!subscription || subscription.acknowledged) return;
|
|
2639
|
+
subscription.acknowledged = true;
|
|
2640
|
+
if (subscription.ackTimeout) {
|
|
2641
|
+
clearTimeout(subscription.ackTimeout);
|
|
2642
|
+
subscription.ackTimeout = void 0;
|
|
2643
|
+
}
|
|
2644
|
+
subscription.resolveReady();
|
|
2645
|
+
}
|
|
2646
|
+
function rejectRealtimeSubscription(channel, error) {
|
|
2647
|
+
if (!channel) return;
|
|
2648
|
+
const subscription = subscribedRealtimeChannels.get(channel);
|
|
2649
|
+
if (!subscription || subscription.acknowledged) return;
|
|
2650
|
+
if (subscription.ackTimeout) {
|
|
2651
|
+
clearTimeout(subscription.ackTimeout);
|
|
2652
|
+
subscription.ackTimeout = void 0;
|
|
2653
|
+
}
|
|
2654
|
+
subscribedRealtimeChannels.delete(channel);
|
|
2655
|
+
subscription.rejectReady(error);
|
|
2656
|
+
}
|
|
2657
|
+
function rejectAllRealtimeSubscriptions(error) {
|
|
2658
|
+
const entries = Array.from(subscribedRealtimeChannels.entries());
|
|
2659
|
+
subscribedRealtimeChannels.clear();
|
|
2660
|
+
for (const [, subscription] of entries) {
|
|
2661
|
+
if (subscription.ackTimeout) {
|
|
2662
|
+
clearTimeout(subscription.ackTimeout);
|
|
2663
|
+
subscription.ackTimeout = void 0;
|
|
2664
|
+
}
|
|
2665
|
+
if (!subscription.acknowledged) subscription.rejectReady(error);
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
let connectionInitiated = false;
|
|
2669
|
+
function ensureConnected() {
|
|
2670
|
+
if (!connectionInitiated) {
|
|
2671
|
+
connectionInitiated = true;
|
|
2672
|
+
connect();
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
return {
|
|
2676
|
+
request(method, params, requestOptions) {
|
|
2677
|
+
return requestJsonRpc(method, params, requestOptions);
|
|
2678
|
+
},
|
|
2679
|
+
subscribe(handler) {
|
|
2680
|
+
eventHandlers.add(handler);
|
|
2681
|
+
return () => {
|
|
2682
|
+
eventHandlers.delete(handler);
|
|
2683
|
+
};
|
|
2684
|
+
},
|
|
2685
|
+
async subscribeBridgeEvent(event, handler, requestOptions, params) {
|
|
2686
|
+
const scopedHandler = (nextEvent) => {
|
|
2687
|
+
if (matchesSeedAppHostEventSubscription(event, nextEvent)) handler(nextEvent);
|
|
2688
|
+
};
|
|
2689
|
+
eventHandlers.add(scopedHandler);
|
|
2690
|
+
let subscriptionId;
|
|
2691
|
+
try {
|
|
2692
|
+
subscriptionId = readBridgeSubscriptionId$1(await requestJsonRpc("seed.event.subscribe", buildBridgeSubscribeParams$1(event, params), requestOptions));
|
|
2693
|
+
} catch (error) {
|
|
2694
|
+
eventHandlers.delete(scopedHandler);
|
|
2695
|
+
throw error;
|
|
2696
|
+
}
|
|
2697
|
+
return () => {
|
|
2698
|
+
eventHandlers.delete(scopedHandler);
|
|
2699
|
+
requestJsonRpc("seed.event.unsubscribe", buildBridgeUnsubscribeParams$1(event, subscriptionId), requestOptions).catch(() => void 0);
|
|
2700
|
+
};
|
|
2701
|
+
},
|
|
2702
|
+
async subscribeRealtimeChannel(channel, cursor) {
|
|
2703
|
+
let subscription = subscribedRealtimeChannels.get(channel);
|
|
2704
|
+
if (subscription) {
|
|
2705
|
+
subscription.refCount += 1;
|
|
2706
|
+
if (cursor) subscription.cursor = cursor;
|
|
2707
|
+
} else {
|
|
2708
|
+
subscription = createRealtimeSubscription(channel, cursor);
|
|
2709
|
+
subscribedRealtimeChannels.set(channel, subscription);
|
|
2710
|
+
ensureConnected();
|
|
2711
|
+
if (wsReady && ws) sendRealtimeSubscribeFrame(channel, subscription);
|
|
2712
|
+
}
|
|
2713
|
+
await subscription.ready;
|
|
2714
|
+
return () => {
|
|
2715
|
+
const current = subscribedRealtimeChannels.get(channel);
|
|
2716
|
+
if (!current) return;
|
|
2717
|
+
current.refCount -= 1;
|
|
2718
|
+
if (current.refCount > 0) return;
|
|
2719
|
+
subscribedRealtimeChannels.delete(channel);
|
|
2720
|
+
if (wsReady && ws && ws.readyState === (getWebSocketImpl()?.OPEN ?? 1)) requestJsonRpc("realtime.unsubscribe", { channel }).catch(() => void 0);
|
|
2721
|
+
};
|
|
2722
|
+
},
|
|
2723
|
+
close() {
|
|
2724
|
+
closed = true;
|
|
2725
|
+
wsReady = false;
|
|
2726
|
+
sendQueue.length = 0;
|
|
2727
|
+
const seedError = createSeedAppError({
|
|
2728
|
+
code: "host_unavailable",
|
|
2729
|
+
message: "WebSocket transport closed.",
|
|
2730
|
+
requestId: "ws-close"
|
|
2731
|
+
});
|
|
2732
|
+
rejectAllPending(seedError);
|
|
2733
|
+
rejectAllRealtimeSubscriptions(seedError);
|
|
2734
|
+
eventHandlers.clear();
|
|
2735
|
+
subscribedRealtimeChannels.clear();
|
|
2736
|
+
if (ws) {
|
|
2737
|
+
ws.onmessage = null;
|
|
2738
|
+
ws.onclose = null;
|
|
2739
|
+
ws.onerror = null;
|
|
2740
|
+
ws.onopen = null;
|
|
2741
|
+
try {
|
|
2742
|
+
ws.close(1e3, "Transport closed");
|
|
2743
|
+
} catch {}
|
|
2744
|
+
ws = null;
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
};
|
|
2748
|
+
}
|
|
2749
|
+
function buildBridgeSubscribeParams$1(event, params) {
|
|
2750
|
+
return {
|
|
2751
|
+
...params,
|
|
2752
|
+
type: event
|
|
2753
|
+
};
|
|
2754
|
+
}
|
|
2755
|
+
function buildBridgeUnsubscribeParams$1(event, subscriptionId) {
|
|
2756
|
+
return subscriptionId ? {
|
|
2757
|
+
type: event,
|
|
2758
|
+
subscriptionId
|
|
2759
|
+
} : { type: event };
|
|
2760
|
+
}
|
|
2761
|
+
function readBridgeSubscriptionId$1(value) {
|
|
2762
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
2763
|
+
const subscriptionId = value.subscriptionId;
|
|
2764
|
+
return typeof subscriptionId === "string" && subscriptionId.trim() ? subscriptionId.trim() : void 0;
|
|
2765
|
+
}
|
|
2766
|
+
function projectRealtimeEvent(params) {
|
|
2767
|
+
const envelope = isRecord$2(params?.event) ? params.event : params;
|
|
2768
|
+
if (!envelope || typeof envelope.type !== "string") return void 0;
|
|
2769
|
+
const payload = envelope.payload;
|
|
2770
|
+
const typedPayload = isRecord$2(payload) ? toSeedAppEvent(payload) : void 0;
|
|
2771
|
+
if (typedPayload) return typedPayload;
|
|
2772
|
+
return {
|
|
2773
|
+
type: envelope.type,
|
|
2774
|
+
payload,
|
|
2775
|
+
status: typeof envelope.status === "string" ? envelope.status : void 0,
|
|
2776
|
+
timestamp: typeof envelope.timestamp === "number" ? envelope.timestamp : Date.now()
|
|
2777
|
+
};
|
|
2778
|
+
}
|
|
2779
|
+
function toSeedAppEvent(value) {
|
|
2780
|
+
if (typeof value.type !== "string") return void 0;
|
|
2781
|
+
return {
|
|
2782
|
+
type: value.type,
|
|
2783
|
+
payload: value.payload,
|
|
2784
|
+
status: typeof value.status === "string" ? value.status : void 0,
|
|
2785
|
+
timestamp: typeof value.timestamp === "number" ? value.timestamp : Date.now()
|
|
2786
|
+
};
|
|
2787
|
+
}
|
|
2788
|
+
function normalizeErrorCode(code) {
|
|
2789
|
+
return normalizeSeedAppErrorCode(code);
|
|
2790
|
+
}
|
|
2791
|
+
function isRetryableJsonRpcCode(code) {
|
|
2792
|
+
return code === -32700 || code === -32603;
|
|
2793
|
+
}
|
|
2794
|
+
function isSeedAppEvent(value) {
|
|
2795
|
+
return Boolean(value);
|
|
2796
|
+
}
|
|
2797
|
+
function toJsonRpcParams(params) {
|
|
2798
|
+
if (params === void 0) return void 0;
|
|
2799
|
+
if (params && typeof params === "object" && !Array.isArray(params)) return params;
|
|
2800
|
+
return { value: params };
|
|
2801
|
+
}
|
|
2802
|
+
function normalizeSeedAppError(error, requestId) {
|
|
2803
|
+
if (isSeedAppError(error)) return error;
|
|
2804
|
+
return createSeedAppError({
|
|
2805
|
+
code: "provider_failed",
|
|
2806
|
+
message: error instanceof Error ? error.message : String(error),
|
|
2807
|
+
requestId,
|
|
2808
|
+
retryable: false
|
|
2809
|
+
});
|
|
2810
|
+
}
|
|
2811
|
+
function isSeedAppError(value) {
|
|
2812
|
+
return Boolean(value) && typeof value === "object" && typeof value.code === "string" && typeof value.message === "string";
|
|
2813
|
+
}
|
|
2814
|
+
function isRecord$2(value) {
|
|
2815
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
2816
|
+
}
|
|
2817
|
+
//#endregion
|
|
2818
|
+
//#region src/mockHost.ts
|
|
2819
|
+
const DEFAULT_MOCK_METHODS = Array.from(/* @__PURE__ */ new Set([
|
|
2820
|
+
"seed.handshake",
|
|
2821
|
+
"seed.capabilities.query",
|
|
2822
|
+
"seed.capabilities.request",
|
|
2823
|
+
"seed.auth.getContext",
|
|
2824
|
+
"seed.auth.getAccessToken",
|
|
2825
|
+
"seed.network.fetch",
|
|
2826
|
+
"seed.bridge.diagnostics",
|
|
2827
|
+
..._seed_app_studio_protocol.seedBridgeMethodRuntimeRoutes.map((route) => route.method),
|
|
2828
|
+
"seed.agent.list",
|
|
2829
|
+
"seed.agent.config.delete",
|
|
2830
|
+
"seed.agent.config.upsert",
|
|
2831
|
+
"seed.agent.preset.update",
|
|
2832
|
+
"seed.agent.runtime.configOption.set",
|
|
2833
|
+
"seed.agent.runtime.configOptions.get",
|
|
2834
|
+
"seed.agent.runtime.mode.get",
|
|
2835
|
+
"seed.agent.runtime.mode.set",
|
|
2836
|
+
"seed.agent.runtime.model.get",
|
|
2837
|
+
"seed.agent.runtime.model.set",
|
|
2838
|
+
"seed.agent.runtime.bundled.get",
|
|
2839
|
+
"seed.agent.runtime.bundled.update",
|
|
2840
|
+
"seed.agent.runtime.local.status.get",
|
|
2841
|
+
"seed.agent.runtime.local.restart",
|
|
2842
|
+
"seed.agent.runtime.capabilities.prefetch",
|
|
2843
|
+
"seed.agent.runtime.local.stale.mark",
|
|
2844
|
+
"seed.agent.runtime.slashCommands.list",
|
|
2845
|
+
"seed.agent.runtime.slashCommand.execute",
|
|
2846
|
+
"seed.agent.runtime.session.ensure",
|
|
2847
|
+
"seed.agent.runtime.conversation.warmup",
|
|
2848
|
+
"seed.agent.runtime.openclaw.get",
|
|
2849
|
+
"seed.agent.runtime.process.snapshot.get",
|
|
2850
|
+
"seed.agent.runtime.process.release",
|
|
2851
|
+
"seed.agent.subagent.session.snapshot.get",
|
|
2852
|
+
"seed.agent.subagent.session.load",
|
|
2853
|
+
"seed.agent.run",
|
|
2854
|
+
"seed.agent.input.submit",
|
|
2855
|
+
"seed.agent.sideQuestion.ask",
|
|
2856
|
+
"seed.agent.approval.check",
|
|
2857
|
+
"seed.agent.approval.list",
|
|
2858
|
+
"seed.agent.approval.respond",
|
|
2859
|
+
"seed.agent.approval.changed",
|
|
2860
|
+
"seed.agent.elicitation.respond",
|
|
2861
|
+
"seed.event.subscribe",
|
|
2862
|
+
"seed.event.unsubscribe",
|
|
2863
|
+
"seed.conversation.changed",
|
|
2864
|
+
"seed.remotePc.presence.list",
|
|
2865
|
+
"seed.remotePc.presence.changed",
|
|
2866
|
+
"seed.remotePc.vibe.list",
|
|
2867
|
+
"seed.remotePc.vibe.get",
|
|
2868
|
+
"seed.remotePc.vibe.messages",
|
|
2869
|
+
"seed.remotePc.vibe.events",
|
|
2870
|
+
"seed.conversation.archive.import",
|
|
2871
|
+
"seed.conversation.artifact.list",
|
|
2872
|
+
"seed.conversation.artifact.access.list",
|
|
2873
|
+
"seed.conversation.artifact.access.revoke",
|
|
2874
|
+
"seed.conversation.artifact.evidence.list",
|
|
2875
|
+
"seed.conversation.artifact.materialize",
|
|
2876
|
+
"seed.conversation.artifact.policy.update",
|
|
2877
|
+
"seed.conversation.artifact.scrub.apply",
|
|
2878
|
+
"seed.conversation.artifact.scrub.dryRun",
|
|
2879
|
+
"seed.conversation.artifact.share",
|
|
2880
|
+
"seed.conversation.artifact.shareTargets.list",
|
|
2881
|
+
"seed.conversation.listByCronJob",
|
|
2882
|
+
"seed.conversation.searchMessages",
|
|
2883
|
+
"seed.team.list",
|
|
2884
|
+
"seed.team.get",
|
|
2885
|
+
"seed.team.create",
|
|
2886
|
+
"seed.team.rename",
|
|
2887
|
+
"seed.team.sessionMode.set",
|
|
2888
|
+
"seed.team.workspace.update",
|
|
2889
|
+
"seed.team.delete",
|
|
2890
|
+
"seed.team.agent.add",
|
|
2891
|
+
"seed.team.agent.remove",
|
|
2892
|
+
"seed.team.agent.rename",
|
|
2893
|
+
"seed.team.message.send",
|
|
2894
|
+
"seed.team.message.sendToAgent",
|
|
2895
|
+
"seed.team.stop",
|
|
2896
|
+
"seed.team.session.ensure",
|
|
2897
|
+
"seed.team.session.reset",
|
|
2898
|
+
"seed.memory.search",
|
|
2899
|
+
"seed.resource.read",
|
|
2900
|
+
"seed.resource.stat",
|
|
2901
|
+
"seed.resource.list",
|
|
2902
|
+
"seed.resource.open",
|
|
2903
|
+
"seed.resource.openableSchemes",
|
|
2904
|
+
"seed.skill.list",
|
|
2905
|
+
"seed.skill.autoInjected.list",
|
|
2906
|
+
"seed.skill.external.detect",
|
|
2907
|
+
"seed.skill.paths.get",
|
|
2908
|
+
"seed.skill.externalPath.add",
|
|
2909
|
+
"seed.skill.importLocal",
|
|
2910
|
+
"seed.skill.delete",
|
|
2911
|
+
"seed.skill.exportLocal",
|
|
2912
|
+
"seed.skill.autoInjected.add",
|
|
2913
|
+
"seed.skill.autoInjected.remove",
|
|
2914
|
+
"seed.skill.market.setEnabled",
|
|
2915
|
+
"seed.assistant.resource.read",
|
|
2916
|
+
"seed.assistant.resource.write",
|
|
2917
|
+
"seed.assistant.resource.delete",
|
|
2918
|
+
"seed.builtin.resource.read",
|
|
2919
|
+
"seed.roleTemplate.list",
|
|
2920
|
+
"seed.employee.list",
|
|
2921
|
+
"seed.employee.createFromAssistant",
|
|
2922
|
+
"seed.employee.createFromRoleTemplate",
|
|
2923
|
+
"seed.employee.get",
|
|
2924
|
+
"seed.employee.activity.list",
|
|
2925
|
+
"seed.employee.memory.delete",
|
|
2926
|
+
"seed.employee.memory.events.list",
|
|
2927
|
+
"seed.employee.memory.list",
|
|
2928
|
+
"seed.employee.openConversation",
|
|
2929
|
+
"seed.employee.remove",
|
|
2930
|
+
"seed.employee.update",
|
|
2931
|
+
"seed.roleTemplate.get",
|
|
2932
|
+
"seed.roleTemplate.create",
|
|
2933
|
+
"seed.roleTemplate.update",
|
|
2934
|
+
"seed.roleTemplate.delete",
|
|
2935
|
+
"seed.roleMaker.session.start",
|
|
2936
|
+
"seed.roleMaker.session.get",
|
|
2937
|
+
"seed.roleMaker.session.generate",
|
|
2938
|
+
"seed.roleMaker.session.apply",
|
|
2939
|
+
"seed.roleMaker.session.approve",
|
|
2940
|
+
"seed.roleMaker.session.discard",
|
|
2941
|
+
"seed.brainBundle.previewSummaries",
|
|
2942
|
+
"seed.mcp.listTools",
|
|
2943
|
+
"seed.mcp.callTool",
|
|
2944
|
+
"seed.workspace.capabilities.get"
|
|
2945
|
+
]));
|
|
2946
|
+
function createSeedAppMockTransport(options) {
|
|
2947
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
2948
|
+
let closed = false;
|
|
2949
|
+
const handlers = {
|
|
2950
|
+
"seed.handshake": () => ({
|
|
2951
|
+
appId: options.appId,
|
|
2952
|
+
hostVersion: options.hostVersion ?? "mock-host",
|
|
2953
|
+
grants: options.grants ?? []
|
|
2954
|
+
}),
|
|
2955
|
+
"seed.capabilities.query": () => options.grants ?? [],
|
|
2956
|
+
"seed.capabilities.request": (params) => {
|
|
2957
|
+
const requested = readRequestedCapabilities(params);
|
|
2958
|
+
const grants = options.grants ?? [];
|
|
2959
|
+
return {
|
|
2960
|
+
grants,
|
|
2961
|
+
approved: requested.filter((capability) => grants.some((grant) => grant.capability === capability && grant.policy === "enabled")),
|
|
2962
|
+
denied: requested.filter((capability) => grants.some((grant) => grant.capability === capability && grant.policy === "disabled")),
|
|
2963
|
+
pending: requested.filter((capability) => grants.some((grant) => grant.capability === capability && grant.policy === "ask"))
|
|
2964
|
+
};
|
|
2965
|
+
},
|
|
2966
|
+
"seed.auth.getContext": () => ({
|
|
2967
|
+
authenticated: false,
|
|
2968
|
+
source: "desktop",
|
|
2969
|
+
accountState: "unauthenticated"
|
|
2970
|
+
}),
|
|
2971
|
+
"seed.auth.getAccessToken": () => ({
|
|
2972
|
+
token: "mock-seed-app-token",
|
|
2973
|
+
tokenType: "seed-app",
|
|
2974
|
+
appId: options.appId,
|
|
2975
|
+
userId: "mock-user",
|
|
2976
|
+
scopes: [],
|
|
2977
|
+
issuedAt: 0,
|
|
2978
|
+
expiresAt: 0
|
|
2979
|
+
}),
|
|
2980
|
+
"seed.network.fetch": () => ({
|
|
2981
|
+
url: "https://example.test/",
|
|
2982
|
+
status: 200,
|
|
2983
|
+
statusText: "OK",
|
|
2984
|
+
headers: { "content-type": "application/json" },
|
|
2985
|
+
body: "{}"
|
|
2986
|
+
}),
|
|
2987
|
+
"seed.bridge.diagnostics": (params) => createMockBridgeDiagnostics(options, params),
|
|
2988
|
+
"seed.agent.list": () => [],
|
|
2989
|
+
"seed.agent.config.upsert": (params) => ({
|
|
2990
|
+
ok: true,
|
|
2991
|
+
assistant: {
|
|
2992
|
+
id: readRequiredNestedString(params, ["agent", "id"]),
|
|
2993
|
+
agentId: readRequiredNestedString(params, ["agent", "id"]),
|
|
2994
|
+
name: readRequiredNestedString(params, ["agent", "name"]),
|
|
2995
|
+
source: readRequiredString(params, "collection") === "preset" ? "preset" : "custom-config",
|
|
2996
|
+
kind: "acp",
|
|
2997
|
+
status: "available"
|
|
2998
|
+
}
|
|
2999
|
+
}),
|
|
3000
|
+
"seed.agent.config.delete": () => ({
|
|
3001
|
+
ok: true,
|
|
3002
|
+
deleted: true
|
|
3003
|
+
}),
|
|
3004
|
+
"seed.agent.runtime.configOptions.get": () => ({ configOptions: [{
|
|
3005
|
+
id: "reasoning_effort",
|
|
3006
|
+
name: "Reasoning",
|
|
3007
|
+
type: "select",
|
|
3008
|
+
currentValue: "medium",
|
|
3009
|
+
options: [{
|
|
3010
|
+
value: "low",
|
|
3011
|
+
name: "Low"
|
|
3012
|
+
}, {
|
|
3013
|
+
value: "medium",
|
|
3014
|
+
name: "Medium"
|
|
3015
|
+
}]
|
|
3016
|
+
}] }),
|
|
3017
|
+
"seed.agent.runtime.configOption.set": (params) => ({ configOptions: [{
|
|
3018
|
+
id: readRequiredString(params, "configId"),
|
|
3019
|
+
type: "select",
|
|
3020
|
+
currentValue: readRequiredString(params, "value"),
|
|
3021
|
+
options: [{
|
|
3022
|
+
value: readRequiredString(params, "value"),
|
|
3023
|
+
name: readRequiredString(params, "value")
|
|
3024
|
+
}]
|
|
3025
|
+
}] }),
|
|
3026
|
+
"seed.agent.runtime.model.get": () => ({ modelInfo: {
|
|
3027
|
+
currentModelId: "mock-model",
|
|
3028
|
+
currentModelLabel: "Mock Model",
|
|
3029
|
+
availableModels: [{
|
|
3030
|
+
id: "mock-model",
|
|
3031
|
+
label: "Mock Model"
|
|
3032
|
+
}],
|
|
3033
|
+
canSwitch: true,
|
|
3034
|
+
source: "models",
|
|
3035
|
+
sourceDetail: "acp-models"
|
|
3036
|
+
} }),
|
|
3037
|
+
"seed.agent.runtime.model.set": (params) => ({ modelInfo: {
|
|
3038
|
+
currentModelId: readRequiredString(params, "modelId"),
|
|
3039
|
+
currentModelLabel: readRequiredString(params, "modelId"),
|
|
3040
|
+
availableModels: [{
|
|
3041
|
+
id: readRequiredString(params, "modelId"),
|
|
3042
|
+
label: readRequiredString(params, "modelId")
|
|
3043
|
+
}],
|
|
3044
|
+
canSwitch: true,
|
|
3045
|
+
source: "models",
|
|
3046
|
+
sourceDetail: "acp-models"
|
|
3047
|
+
} }),
|
|
3048
|
+
"seed.agent.runtime.mode.get": () => ({
|
|
3049
|
+
mode: "default",
|
|
3050
|
+
initialized: true
|
|
3051
|
+
}),
|
|
3052
|
+
"seed.agent.runtime.mode.set": (params) => ({
|
|
3053
|
+
mode: readRequiredString(params, "mode"),
|
|
3054
|
+
initialized: true
|
|
3055
|
+
}),
|
|
3056
|
+
"seed.agent.runtime.bundled.get": () => ({ runtime: {
|
|
3057
|
+
backend: "seed",
|
|
3058
|
+
packageName: "@happy-creative/seed-code",
|
|
3059
|
+
available: true,
|
|
3060
|
+
bundled: true,
|
|
3061
|
+
updateSupported: true,
|
|
3062
|
+
source: "app",
|
|
3063
|
+
version: "1.2.3"
|
|
3064
|
+
} }),
|
|
3065
|
+
"seed.agent.runtime.bundled.update": (params) => ({ runtime: {
|
|
3066
|
+
backend: "seed",
|
|
3067
|
+
packageName: "@happy-creative/seed-code",
|
|
3068
|
+
available: true,
|
|
3069
|
+
bundled: true,
|
|
3070
|
+
updateSupported: true,
|
|
3071
|
+
source: "app",
|
|
3072
|
+
version: typeof params === "object" && params && "version" in params ? String(params.version) : "latest"
|
|
3073
|
+
} }),
|
|
3074
|
+
"seed.agent.runtime.local.status.get": () => ({ status: {
|
|
3075
|
+
backend: "seed",
|
|
3076
|
+
running: false,
|
|
3077
|
+
stale: false,
|
|
3078
|
+
restartRequired: false,
|
|
3079
|
+
processCount: 0,
|
|
3080
|
+
taskCount: 0,
|
|
3081
|
+
conversationIds: [],
|
|
3082
|
+
pids: [],
|
|
3083
|
+
sharedProcessEnabled: true,
|
|
3084
|
+
mcpStartupMode: "eager"
|
|
3085
|
+
} }),
|
|
3086
|
+
"seed.agent.runtime.local.restart": () => ({
|
|
3087
|
+
backend: "seed",
|
|
3088
|
+
restartedTaskCount: 0,
|
|
3089
|
+
releasedProcessCount: 0,
|
|
3090
|
+
conversationIds: []
|
|
3091
|
+
}),
|
|
3092
|
+
"seed.agent.runtime.capabilities.prefetch": (params) => ({ prefetch: {
|
|
3093
|
+
backend: readRequiredString(params, "backend"),
|
|
3094
|
+
status: "success",
|
|
3095
|
+
updatedAt: Date.now()
|
|
3096
|
+
} }),
|
|
3097
|
+
"seed.agent.runtime.local.stale.mark": (params) => ({
|
|
3098
|
+
backend: "seed",
|
|
3099
|
+
stale: true,
|
|
3100
|
+
restartRequired: true,
|
|
3101
|
+
staleReason: readRequiredString(params, "reason"),
|
|
3102
|
+
staleAt: Date.now(),
|
|
3103
|
+
mcpStartupMode: "eager"
|
|
3104
|
+
}),
|
|
3105
|
+
"seed.agent.runtime.custom.test": (params) => ({
|
|
3106
|
+
success: true,
|
|
3107
|
+
step: "acp_initialize",
|
|
3108
|
+
message: `Connection successful for ${readRequiredString(params, "command")}`
|
|
3109
|
+
}),
|
|
3110
|
+
"seed.agent.runtime.slashCommands.list": () => ({ commands: [{
|
|
3111
|
+
name: "help",
|
|
3112
|
+
description: "Display available slash commands",
|
|
3113
|
+
kind: "builtin",
|
|
3114
|
+
source: "builtin",
|
|
3115
|
+
selectionBehavior: "execute"
|
|
3116
|
+
}] }),
|
|
3117
|
+
"seed.agent.runtime.slashCommand.execute": (params) => ({
|
|
3118
|
+
ok: true,
|
|
3119
|
+
message: `Executed /${readRequiredString(params, "name")}`
|
|
3120
|
+
}),
|
|
3121
|
+
"seed.agent.runtime.session.ensure": (params) => ({
|
|
3122
|
+
conversationId: readRequiredString(params, "conversationId"),
|
|
3123
|
+
backend: readRequiredString(params, "backend"),
|
|
3124
|
+
status: "success"
|
|
3125
|
+
}),
|
|
3126
|
+
"seed.agent.runtime.conversation.warmup": (params) => ({
|
|
3127
|
+
conversationId: readRequiredString(params, "conversationId"),
|
|
3128
|
+
status: "success"
|
|
3129
|
+
}),
|
|
3130
|
+
"seed.agent.runtime.openclaw.get": (params) => ({
|
|
3131
|
+
conversationId: readRequiredString(params, "conversationId"),
|
|
3132
|
+
runtime: {}
|
|
3133
|
+
}),
|
|
3134
|
+
"seed.agent.runtime.process.snapshot.get": (params) => ({ snapshot: {
|
|
3135
|
+
conversationId: readRequiredString(params, "conversationId"),
|
|
3136
|
+
processCount: 0,
|
|
3137
|
+
pid: null,
|
|
3138
|
+
running: false,
|
|
3139
|
+
lastExitSignal: null,
|
|
3140
|
+
lastExitCode: null
|
|
3141
|
+
} }),
|
|
3142
|
+
"seed.agent.runtime.process.release": (params) => ({
|
|
3143
|
+
conversationId: readRequiredString(params, "conversationId"),
|
|
3144
|
+
releasedProcessCount: 0
|
|
3145
|
+
}),
|
|
3146
|
+
"seed.agent.subagent.session.snapshot.get": () => ({ record: null }),
|
|
3147
|
+
"seed.agent.subagent.session.load": () => ({ record: null }),
|
|
3148
|
+
"seed.agent.preset.update": (params) => ({
|
|
3149
|
+
ok: true,
|
|
3150
|
+
assistant: {
|
|
3151
|
+
id: readRequiredString(params, "assistantId"),
|
|
3152
|
+
agentId: readRequiredString(params, "assistantId"),
|
|
3153
|
+
name: readRequiredString(params, "assistantId"),
|
|
3154
|
+
source: "preset",
|
|
3155
|
+
kind: "acp",
|
|
3156
|
+
backend: readRequiredString(params, "presetAgentType"),
|
|
3157
|
+
status: "available",
|
|
3158
|
+
isPreset: true,
|
|
3159
|
+
presetAgentType: readRequiredString(params, "presetAgentType")
|
|
3160
|
+
}
|
|
3161
|
+
}),
|
|
3162
|
+
"seed.agent.run": () => ({
|
|
3163
|
+
runId: "mock-run",
|
|
3164
|
+
status: "started"
|
|
3165
|
+
}),
|
|
3166
|
+
"seed.agent.input.submit": (params) => ({
|
|
3167
|
+
conversationId: readRequiredString(params, "conversationId"),
|
|
3168
|
+
mode: readRequiredString(params, "mode"),
|
|
3169
|
+
status: "started",
|
|
3170
|
+
seedEmulated: true,
|
|
3171
|
+
qwenNative: false
|
|
3172
|
+
}),
|
|
3173
|
+
"seed.agent.sideQuestion.ask": (params) => ({
|
|
3174
|
+
status: "ok",
|
|
3175
|
+
answer: `Mock side answer for ${readRequiredString(params, "question")}`
|
|
3176
|
+
}),
|
|
3177
|
+
"seed.agent.approval.check": () => ({ approved: false }),
|
|
3178
|
+
"seed.agent.approval.list": () => [],
|
|
3179
|
+
"seed.agent.approval.respond": () => ({
|
|
3180
|
+
ok: true,
|
|
3181
|
+
eventCount: 0
|
|
3182
|
+
}),
|
|
3183
|
+
"seed.agent.elicitation.respond": () => ({
|
|
3184
|
+
ok: true,
|
|
3185
|
+
eventCount: 0
|
|
3186
|
+
}),
|
|
3187
|
+
"seed.event.subscribe": (params) => ({
|
|
3188
|
+
subscriptionId: "mock-agent-stream-subscription",
|
|
3189
|
+
channel: readAgentStreamChannel(params),
|
|
3190
|
+
events: []
|
|
3191
|
+
}),
|
|
3192
|
+
"seed.event.unsubscribe": () => void 0,
|
|
3193
|
+
"seed.conversation.listByCronJob": () => [],
|
|
3194
|
+
"seed.remotePc.presence.list": () => [],
|
|
3195
|
+
"seed.remotePc.vibe.list": () => [],
|
|
3196
|
+
"seed.remotePc.vibe.events": () => ({
|
|
3197
|
+
messages: [],
|
|
3198
|
+
timedOut: true
|
|
3199
|
+
}),
|
|
3200
|
+
"seed.conversation.archive.import": () => ({
|
|
3201
|
+
success: true,
|
|
3202
|
+
data: {
|
|
3203
|
+
conversationId: "mock-imported-conversation",
|
|
3204
|
+
sourceConversationId: "mock-source-conversation",
|
|
3205
|
+
importedMessages: 0
|
|
3206
|
+
}
|
|
3207
|
+
}),
|
|
3208
|
+
"seed.conversation.searchMessages": () => ({
|
|
3209
|
+
items: [],
|
|
3210
|
+
total: 0,
|
|
3211
|
+
page: 0,
|
|
3212
|
+
pageSize: 20,
|
|
3213
|
+
hasMore: false
|
|
3214
|
+
}),
|
|
3215
|
+
"seed.conversation.artifact.list": (params) => [{
|
|
3216
|
+
id: "mock-artifact",
|
|
3217
|
+
name: `${readRequiredString(params, "conversationId")}-artifact.txt`,
|
|
3218
|
+
kind: "file",
|
|
3219
|
+
sizeBytes: 12,
|
|
3220
|
+
mimeType: "text/plain",
|
|
3221
|
+
previewStatus: "ready",
|
|
3222
|
+
createdAt: "2026-01-01T00:00:00.000Z"
|
|
3223
|
+
}],
|
|
3224
|
+
"seed.conversation.artifact.evidence.list": (params) => [{
|
|
3225
|
+
id: "mock-artifact-evidence",
|
|
3226
|
+
artifactId: readRequiredString(params, "artifactId"),
|
|
3227
|
+
action: "artifact.previewed",
|
|
3228
|
+
message: "Mock artifact evidence",
|
|
3229
|
+
createdAt: "2026-01-01T00:00:00.000Z"
|
|
3230
|
+
}],
|
|
3231
|
+
"seed.conversation.artifact.access.list": (params) => [{
|
|
3232
|
+
id: "mock-artifact-access",
|
|
3233
|
+
artifactId: readRequiredString(params, "artifactId"),
|
|
3234
|
+
userId: "mock-user",
|
|
3235
|
+
role: "viewer",
|
|
3236
|
+
createdAt: "2026-01-01T00:00:00.000Z"
|
|
3237
|
+
}],
|
|
3238
|
+
"seed.conversation.artifact.shareTargets.list": () => [{
|
|
3239
|
+
id: "mock-user",
|
|
3240
|
+
username: "Mock User",
|
|
3241
|
+
role: "member"
|
|
3242
|
+
}],
|
|
3243
|
+
"seed.conversation.artifact.share": (params) => ({
|
|
3244
|
+
id: "mock-artifact-access",
|
|
3245
|
+
artifactId: readRequiredString(params, "artifactId"),
|
|
3246
|
+
userId: readRequiredString(params, "userId"),
|
|
3247
|
+
role: readArtifactShareRole(params),
|
|
3248
|
+
createdAt: "2026-01-01T00:00:00.000Z"
|
|
3249
|
+
}),
|
|
3250
|
+
"seed.conversation.artifact.access.revoke": (params) => ({
|
|
3251
|
+
id: "mock-artifact-access",
|
|
3252
|
+
artifactId: readRequiredString(params, "artifactId"),
|
|
3253
|
+
userId: readRequiredString(params, "userId"),
|
|
3254
|
+
role: "viewer",
|
|
3255
|
+
createdAt: "2026-01-01T00:00:00.000Z"
|
|
3256
|
+
}),
|
|
3257
|
+
"seed.conversation.artifact.materialize": (params) => ({
|
|
3258
|
+
id: "mock-artifact-materialization",
|
|
3259
|
+
artifactId: readRequiredString(params, "artifactId"),
|
|
3260
|
+
workspaceId: readRequiredString(params, "workspaceId"),
|
|
3261
|
+
fileId: "mock-workspace-file",
|
|
3262
|
+
createdAt: "2026-01-01T00:00:00.000Z"
|
|
3263
|
+
}),
|
|
3264
|
+
"seed.conversation.artifact.policy.update": (params) => {
|
|
3265
|
+
readRequiredString(params, "artifactId");
|
|
3266
|
+
assertArtifactPolicyUpdate(params);
|
|
3267
|
+
return {
|
|
3268
|
+
id: readRequiredString(params, "artifactId"),
|
|
3269
|
+
name: "mock-artifact.txt",
|
|
3270
|
+
kind: "file",
|
|
3271
|
+
mimeType: "text/plain",
|
|
3272
|
+
previewStatus: "ready",
|
|
3273
|
+
policy: {
|
|
3274
|
+
retentionStatus: isRecord$1(params) && typeof params.retentionStatus === "string" ? params.retentionStatus : "default",
|
|
3275
|
+
redactionStatus: isRecord$1(params) && typeof params.redactionStatus === "string" ? params.redactionStatus : "none",
|
|
3276
|
+
downloadPolicy: isRecord$1(params) && typeof params.downloadPolicy === "string" ? params.downloadPolicy : "allow"
|
|
3277
|
+
},
|
|
3278
|
+
createdAt: "2026-01-01T00:00:00.000Z"
|
|
3279
|
+
};
|
|
3280
|
+
},
|
|
3281
|
+
"seed.conversation.artifact.scrub.dryRun": (params) => ({
|
|
3282
|
+
artifactId: readRequiredString(params, "artifactId"),
|
|
3283
|
+
scannedBytes: 64,
|
|
3284
|
+
findingCount: 1,
|
|
3285
|
+
findings: [{
|
|
3286
|
+
className: "api-key",
|
|
3287
|
+
count: 1
|
|
3288
|
+
}],
|
|
3289
|
+
wouldRedact: true
|
|
3290
|
+
}),
|
|
3291
|
+
"seed.conversation.artifact.scrub.apply": (params) => ({
|
|
3292
|
+
artifactId: readRequiredString(params, "artifactId"),
|
|
3293
|
+
scannedBytes: 64,
|
|
3294
|
+
findingCount: 1,
|
|
3295
|
+
findings: [{
|
|
3296
|
+
className: "api-key",
|
|
3297
|
+
count: 1
|
|
3298
|
+
}],
|
|
3299
|
+
wouldRedact: true,
|
|
3300
|
+
safeObjectCreated: true,
|
|
3301
|
+
contentAddress: "sha256:mock-safe-object"
|
|
3302
|
+
}),
|
|
3303
|
+
"seed.team.list": () => [],
|
|
3304
|
+
"seed.team.get": (params) => ({
|
|
3305
|
+
id: readTeamId(params),
|
|
3306
|
+
name: "Mock Team",
|
|
3307
|
+
workspaceMode: "shared",
|
|
3308
|
+
teammateSourcePolicy: "all",
|
|
3309
|
+
leaderAgentId: "slot-leader",
|
|
3310
|
+
agents: [],
|
|
3311
|
+
createdAt: "2026-01-01T00:00:00.000Z",
|
|
3312
|
+
updatedAt: "2026-01-01T00:00:00.000Z"
|
|
3313
|
+
}),
|
|
3314
|
+
"seed.team.create": (params) => ({
|
|
3315
|
+
id: "mock-team-created",
|
|
3316
|
+
name: readName(params) ?? "Mock Team",
|
|
3317
|
+
workspaceMode: readWorkspaceMode(params),
|
|
3318
|
+
teammateSourcePolicy: readTeammateSourcePolicy(params),
|
|
3319
|
+
leaderAgentId: "slot-leader",
|
|
3320
|
+
agents: [],
|
|
3321
|
+
workspaceRef: readWorkspace(params),
|
|
3322
|
+
createdAt: "2026-01-01T00:00:00.000Z",
|
|
3323
|
+
updatedAt: "2026-01-01T00:00:00.000Z"
|
|
3324
|
+
}),
|
|
3325
|
+
"seed.team.rename": () => ({ ok: true }),
|
|
3326
|
+
"seed.team.sessionMode.set": () => ({ ok: true }),
|
|
3327
|
+
"seed.team.workspace.update": () => ({ ok: true }),
|
|
3328
|
+
"seed.team.delete": () => ({ ok: true }),
|
|
3329
|
+
"seed.team.agent.add": () => ({
|
|
3330
|
+
slotId: "slot-new",
|
|
3331
|
+
conversationId: "conversation-new",
|
|
3332
|
+
role: "teammate",
|
|
3333
|
+
agentType: "acp",
|
|
3334
|
+
agentName: "Mock Teammate",
|
|
3335
|
+
conversationType: "acp",
|
|
3336
|
+
status: "pending"
|
|
3337
|
+
}),
|
|
3338
|
+
"seed.team.agent.remove": () => ({ ok: true }),
|
|
3339
|
+
"seed.team.agent.rename": () => ({ ok: true }),
|
|
3340
|
+
"seed.team.message.send": () => ({ ok: true }),
|
|
3341
|
+
"seed.team.message.sendToAgent": () => ({ ok: true }),
|
|
3342
|
+
"seed.team.stop": () => ({ ok: true }),
|
|
3343
|
+
"seed.team.session.ensure": () => ({ ok: true }),
|
|
3344
|
+
"seed.team.session.reset": (params) => ({
|
|
3345
|
+
id: readTeamId(params),
|
|
3346
|
+
name: "Mock Team",
|
|
3347
|
+
workspaceMode: "shared",
|
|
3348
|
+
teammateSourcePolicy: "all",
|
|
3349
|
+
leaderAgentId: "slot-leader",
|
|
3350
|
+
agents: [],
|
|
3351
|
+
createdAt: "2026-01-01T00:00:00.000Z",
|
|
3352
|
+
updatedAt: "2026-01-01T00:00:00.000Z"
|
|
3353
|
+
}),
|
|
3354
|
+
"seed.memory.search": () => ({ items: [] }),
|
|
3355
|
+
"seed.resource.read": (params) => ({
|
|
3356
|
+
uri: readResourceUri(params),
|
|
3357
|
+
resolved: {
|
|
3358
|
+
uri: readResourceUri(params),
|
|
3359
|
+
provider: "mock"
|
|
3360
|
+
},
|
|
3361
|
+
content: {
|
|
3362
|
+
kind: "text",
|
|
3363
|
+
text: "",
|
|
3364
|
+
encoding: "utf8"
|
|
3365
|
+
},
|
|
3366
|
+
trace: []
|
|
3367
|
+
}),
|
|
3368
|
+
"seed.resource.stat": (params) => ({
|
|
3369
|
+
uri: readResourceUri(params),
|
|
3370
|
+
kind: "file",
|
|
3371
|
+
name: readResourceUri(params).split("/").filter(Boolean).pop(),
|
|
3372
|
+
downloadable: true
|
|
3373
|
+
}),
|
|
3374
|
+
"seed.resource.list": (params) => ({
|
|
3375
|
+
uri: readResourceUri(params),
|
|
3376
|
+
entries: []
|
|
3377
|
+
}),
|
|
3378
|
+
"seed.resource.open": (params) => ({
|
|
3379
|
+
action: "preview",
|
|
3380
|
+
resolved: {
|
|
3381
|
+
uri: readResourceUri(params),
|
|
3382
|
+
provider: "mock"
|
|
3383
|
+
}
|
|
3384
|
+
}),
|
|
3385
|
+
"seed.resource.openableSchemes": () => ({ schemes: ["workspace-fs"] }),
|
|
3386
|
+
"seed.skill.list": () => [],
|
|
3387
|
+
"seed.skill.autoInjected.list": () => [],
|
|
3388
|
+
"seed.skill.external.detect": () => ({
|
|
3389
|
+
success: true,
|
|
3390
|
+
data: []
|
|
3391
|
+
}),
|
|
3392
|
+
"seed.skill.paths.get": () => ({
|
|
3393
|
+
userSkillsDir: "",
|
|
3394
|
+
builtinSkillsDir: ""
|
|
3395
|
+
}),
|
|
3396
|
+
"seed.skill.externalPath.add": () => ({ success: true }),
|
|
3397
|
+
"seed.skill.importLocal": () => ({
|
|
3398
|
+
success: true,
|
|
3399
|
+
data: { skillName: "mock" }
|
|
3400
|
+
}),
|
|
3401
|
+
"seed.skill.delete": () => ({ success: true }),
|
|
3402
|
+
"seed.skill.exportLocal": () => ({ success: true }),
|
|
3403
|
+
"seed.skill.autoInjected.add": () => ({ success: true }),
|
|
3404
|
+
"seed.skill.autoInjected.remove": () => ({ success: true }),
|
|
3405
|
+
"seed.skill.market.setEnabled": () => ({ success: true }),
|
|
3406
|
+
"seed.assistant.resource.read": () => ({ content: "" }),
|
|
3407
|
+
"seed.assistant.resource.write": () => ({ ok: true }),
|
|
3408
|
+
"seed.assistant.resource.delete": () => ({ ok: true }),
|
|
3409
|
+
"seed.builtin.resource.read": () => ({ content: "" }),
|
|
3410
|
+
"seed.employee.list": () => [],
|
|
3411
|
+
"seed.employee.createFromAssistant": (params) => {
|
|
3412
|
+
const record = typeof params === "object" && params !== null && !Array.isArray(params) ? params : {};
|
|
3413
|
+
const assistant = "assistant" in record && typeof record.assistant === "object" && record.assistant !== null ? record.assistant : {};
|
|
3414
|
+
const assistantId = typeof assistant.id === "string" ? assistant.id : "mock-assistant";
|
|
3415
|
+
const assistantName = typeof assistant.name === "string" ? assistant.name : "Mock Assistant";
|
|
3416
|
+
return {
|
|
3417
|
+
id: "mock-employee",
|
|
3418
|
+
name: "name" in record && typeof record.name === "string" ? record.name : assistantName,
|
|
3419
|
+
roleTitle: "roleTitle" in record && typeof record.roleTitle === "string" ? record.roleTitle : typeof assistant.roleTitle === "string" ? assistant.roleTitle : assistantName,
|
|
3420
|
+
templateSourceKind: "assistant",
|
|
3421
|
+
templateSourceId: assistantId,
|
|
3422
|
+
templateSourceName: assistantName,
|
|
3423
|
+
avatar: "avatar" in record && typeof record.avatar === "string" ? record.avatar : typeof assistant.sourceAvatar === "string" ? assistant.sourceAvatar : void 0,
|
|
3424
|
+
modelId: "modelId" in record && typeof record.modelId === "string" ? record.modelId : typeof assistant.modelId === "string" ? assistant.modelId : void 0,
|
|
3425
|
+
description: "description" in record && typeof record.description === "string" ? record.description : typeof assistant.sourceDescription === "string" ? assistant.sourceDescription : void 0,
|
|
3426
|
+
status: "idle",
|
|
3427
|
+
source: "assistant"
|
|
3428
|
+
};
|
|
3429
|
+
},
|
|
3430
|
+
"seed.employee.createFromRoleTemplate": (params) => {
|
|
3431
|
+
const record = typeof params === "object" && params !== null && !Array.isArray(params) ? params : {};
|
|
3432
|
+
const roleTemplateId = "roleTemplateId" in record && typeof record.roleTemplateId === "string" ? record.roleTemplateId : "mock-role-template";
|
|
3433
|
+
return {
|
|
3434
|
+
id: "mock-employee",
|
|
3435
|
+
name: "name" in record && typeof record.name === "string" ? record.name : "Mock Employee",
|
|
3436
|
+
roleTitle: "roleTitle" in record && typeof record.roleTitle === "string" ? record.roleTitle : "Mock Role",
|
|
3437
|
+
templateSourceKind: "role-template",
|
|
3438
|
+
templateSourceId: roleTemplateId,
|
|
3439
|
+
templateSourceName: roleTemplateId,
|
|
3440
|
+
avatar: "avatar" in record && typeof record.avatar === "string" ? record.avatar : void 0,
|
|
3441
|
+
modelId: "modelId" in record && typeof record.modelId === "string" ? record.modelId : void 0,
|
|
3442
|
+
description: "description" in record && typeof record.description === "string" ? record.description : void 0,
|
|
3443
|
+
status: "idle",
|
|
3444
|
+
source: "role-template"
|
|
3445
|
+
};
|
|
3446
|
+
},
|
|
3447
|
+
"seed.employee.get": (params) => ({
|
|
3448
|
+
employee: {
|
|
3449
|
+
id: readEmployeeId(params),
|
|
3450
|
+
name: "Mock Employee",
|
|
3451
|
+
roleTitle: "Mock Role",
|
|
3452
|
+
status: "idle"
|
|
3453
|
+
},
|
|
3454
|
+
memories: [],
|
|
3455
|
+
activities: []
|
|
3456
|
+
}),
|
|
3457
|
+
"seed.employee.activity.list": (params) => [{
|
|
3458
|
+
id: "mock-employee-activity",
|
|
3459
|
+
employeeId: readEmployeeActivityEmployeeId(params),
|
|
3460
|
+
kind: "task",
|
|
3461
|
+
title: "Mock Activity",
|
|
3462
|
+
description: "Mock employee activity."
|
|
3463
|
+
}],
|
|
3464
|
+
"seed.employee.memory.list": (params) => [{
|
|
3465
|
+
id: "mock-employee-memory",
|
|
3466
|
+
employeeId: readEmployeeMemoryEmployeeId(params),
|
|
3467
|
+
kind: "project",
|
|
3468
|
+
title: "Mock Memory",
|
|
3469
|
+
content: "Remember the mock project.",
|
|
3470
|
+
confidence: 1,
|
|
3471
|
+
pinned: false
|
|
3472
|
+
}],
|
|
3473
|
+
"seed.employee.memory.delete": () => ({ ok: true }),
|
|
3474
|
+
"seed.employee.remove": () => ({ ok: true }),
|
|
3475
|
+
"seed.employee.update": (params) => {
|
|
3476
|
+
const record = typeof params === "object" && params !== null && !Array.isArray(params) ? params : {};
|
|
3477
|
+
const updates = "updates" in record && typeof record.updates === "object" && record.updates !== null ? record.updates : {};
|
|
3478
|
+
return {
|
|
3479
|
+
id: readEmployeeId(params),
|
|
3480
|
+
name: updates.name ?? "Mock Employee",
|
|
3481
|
+
roleTitle: updates.roleTitle ?? "Mock Role",
|
|
3482
|
+
status: updates.status ?? "idle",
|
|
3483
|
+
enabledSkills: updates.enabledSkills,
|
|
3484
|
+
disabledBuiltinSkills: updates.disabledBuiltinSkills
|
|
3485
|
+
};
|
|
3486
|
+
},
|
|
3487
|
+
"seed.employee.memory.events.list": (params) => [{
|
|
3488
|
+
type: "memory.created",
|
|
3489
|
+
employeeId: readEmployeeMemoryEmployeeId(params),
|
|
3490
|
+
memoryId: "mock-employee-memory",
|
|
3491
|
+
data: {
|
|
3492
|
+
id: "mock-employee-memory",
|
|
3493
|
+
employeeId: readEmployeeMemoryEmployeeId(params),
|
|
3494
|
+
kind: "project",
|
|
3495
|
+
title: "Mock Memory",
|
|
3496
|
+
content: "Remember the mock project.",
|
|
3497
|
+
confidence: 1,
|
|
3498
|
+
pinned: false
|
|
3499
|
+
},
|
|
3500
|
+
timestamp: 0
|
|
3501
|
+
}],
|
|
3502
|
+
"seed.employee.openConversation": (params) => {
|
|
3503
|
+
return {
|
|
3504
|
+
id: "mock-employee-conversation",
|
|
3505
|
+
title: "Mock Employee Conversation",
|
|
3506
|
+
status: "running",
|
|
3507
|
+
metadata: { employeeId: readEmployeeIdFromOpenConversation(params) }
|
|
3508
|
+
};
|
|
3509
|
+
},
|
|
3510
|
+
"seed.roleTemplate.list": () => [],
|
|
3511
|
+
"seed.roleTemplate.get": () => null,
|
|
3512
|
+
"seed.roleTemplate.create": () => ({
|
|
3513
|
+
id: "mock-role-template",
|
|
3514
|
+
name: "Mock Role Template"
|
|
3515
|
+
}),
|
|
3516
|
+
"seed.roleTemplate.update": (params) => ({
|
|
3517
|
+
id: readRoleTemplateId(params),
|
|
3518
|
+
name: "Mock Role Template"
|
|
3519
|
+
}),
|
|
3520
|
+
"seed.roleTemplate.delete": () => ({ ok: true }),
|
|
3521
|
+
"seed.roleMaker.session.start": () => ({
|
|
3522
|
+
session: {
|
|
3523
|
+
id: "mock-role-maker-session",
|
|
3524
|
+
conversationId: "mock-role-maker-conversation",
|
|
3525
|
+
status: "drafting"
|
|
3526
|
+
},
|
|
3527
|
+
conversationId: "mock-role-maker-conversation"
|
|
3528
|
+
}),
|
|
3529
|
+
"seed.roleMaker.session.get": (params) => ({ session: {
|
|
3530
|
+
id: readRoleMakerSessionId(params),
|
|
3531
|
+
conversationId: "mock-role-maker-conversation",
|
|
3532
|
+
status: "drafting"
|
|
3533
|
+
} }),
|
|
3534
|
+
"seed.roleMaker.session.generate": (params) => ({ session: {
|
|
3535
|
+
id: readRoleMakerSessionId(params),
|
|
3536
|
+
conversationId: "mock-role-maker-conversation",
|
|
3537
|
+
status: "needs_review",
|
|
3538
|
+
draft: readRoleMakerDraftPayload(params)
|
|
3539
|
+
} }),
|
|
3540
|
+
"seed.roleMaker.session.apply": (params) => ({ session: {
|
|
3541
|
+
id: readRoleMakerSessionId(params),
|
|
3542
|
+
conversationId: "mock-role-maker-conversation",
|
|
3543
|
+
status: "needs_review",
|
|
3544
|
+
draft: readRoleMakerDraftPayload(params)
|
|
3545
|
+
} }),
|
|
3546
|
+
"seed.roleMaker.session.approve": (params) => ({
|
|
3547
|
+
template: {
|
|
3548
|
+
id: "mock-role-template",
|
|
3549
|
+
name: "Mock Role Template"
|
|
3550
|
+
},
|
|
3551
|
+
session: {
|
|
3552
|
+
id: readRoleMakerSessionId(params),
|
|
3553
|
+
conversationId: "mock-role-maker-conversation",
|
|
3554
|
+
status: "approved",
|
|
3555
|
+
roleTemplateId: "mock-role-template"
|
|
3556
|
+
}
|
|
3557
|
+
}),
|
|
3558
|
+
"seed.roleMaker.session.discard": (params) => ({ session: {
|
|
3559
|
+
id: readRoleMakerSessionId(params),
|
|
3560
|
+
conversationId: "mock-role-maker-conversation",
|
|
3561
|
+
status: "discarded"
|
|
3562
|
+
} }),
|
|
3563
|
+
"seed.brainBundle.previewSummaries": (params) => createMockBrainBundlePreviewSummaries(params),
|
|
3564
|
+
"seed.mcp.listTools": () => [],
|
|
3565
|
+
"seed.mcp.callTool": () => ({ content: [] }),
|
|
3566
|
+
"seed.workspace.capabilities.get": () => ({
|
|
3567
|
+
listDir: true,
|
|
3568
|
+
stat: true,
|
|
3569
|
+
readFile: true,
|
|
3570
|
+
readFileBytes: true,
|
|
3571
|
+
writeFile: false,
|
|
3572
|
+
deleteEntry: false,
|
|
3573
|
+
renameEntry: false,
|
|
3574
|
+
search: true
|
|
3575
|
+
}),
|
|
3576
|
+
...options.handlers
|
|
3577
|
+
};
|
|
3578
|
+
return {
|
|
3579
|
+
async request(method, params, requestOptions) {
|
|
3580
|
+
if (closed) throw createSeedAppError({
|
|
3581
|
+
code: "host_unavailable",
|
|
3582
|
+
message: "Mock transport is closed.",
|
|
3583
|
+
requestId: "mock-closed"
|
|
3584
|
+
});
|
|
3585
|
+
const handler = handlers[method];
|
|
3586
|
+
if (!handler) throw createSeedAppError({
|
|
3587
|
+
code: "host_unavailable",
|
|
3588
|
+
message: `No mock handler registered for ${method}.`,
|
|
3589
|
+
requestId: method
|
|
3590
|
+
});
|
|
3591
|
+
const timeoutMs = requestOptions?.timeoutMs;
|
|
3592
|
+
const result = Promise.resolve(handler(params));
|
|
3593
|
+
if (timeoutMs === void 0) return result;
|
|
3594
|
+
return withTimeout(result, timeoutMs, method);
|
|
3595
|
+
},
|
|
3596
|
+
subscribe(handler) {
|
|
3597
|
+
listeners.add(handler);
|
|
3598
|
+
return () => listeners.delete(handler);
|
|
3599
|
+
},
|
|
3600
|
+
async subscribeBridgeEvent(_event, handler) {
|
|
3601
|
+
listeners.add(handler);
|
|
3602
|
+
return () => listeners.delete(handler);
|
|
3603
|
+
},
|
|
3604
|
+
emit(event) {
|
|
3605
|
+
listeners.forEach((listener) => listener(event));
|
|
3606
|
+
},
|
|
3607
|
+
emitAgentStream(event) {
|
|
3608
|
+
listeners.forEach((listener) => listener(event));
|
|
3609
|
+
},
|
|
3610
|
+
close() {
|
|
3611
|
+
closed = true;
|
|
3612
|
+
listeners.clear();
|
|
3613
|
+
}
|
|
3614
|
+
};
|
|
3615
|
+
}
|
|
3616
|
+
function createMockBrainBundlePreviewSummaries(params) {
|
|
3617
|
+
const conversationId = readConversationId(params);
|
|
3618
|
+
if (isRecord$1(params) && params.format === "rollup") return {
|
|
3619
|
+
schemaVersion: "brain-bundle-preview-summary-rollup.v0",
|
|
3620
|
+
conversationId,
|
|
3621
|
+
total: 0,
|
|
3622
|
+
byStatus: {},
|
|
3623
|
+
byState: {},
|
|
3624
|
+
blockedReasons: {},
|
|
3625
|
+
rawBlobIncluded: false
|
|
3626
|
+
};
|
|
3627
|
+
return {
|
|
3628
|
+
schemaVersion: "brain-bundle-preview-summary-page.v0",
|
|
3629
|
+
conversationId,
|
|
3630
|
+
items: [],
|
|
3631
|
+
rawBlobIncluded: false
|
|
3632
|
+
};
|
|
3633
|
+
}
|
|
3634
|
+
function readConversationId(params) {
|
|
3635
|
+
if (isRecord$1(params) && typeof params.conversationId === "string" && params.conversationId.length > 0) return params.conversationId;
|
|
3636
|
+
return "mock-conversation";
|
|
3637
|
+
}
|
|
3638
|
+
function readRequiredString(params, key) {
|
|
3639
|
+
if (!isRecord$1(params) || typeof params[key] !== "string" || !params[key].trim()) throw createSeedAppError({
|
|
3640
|
+
code: "invalid_payload_schema",
|
|
3641
|
+
message: `Missing ${key}`,
|
|
3642
|
+
requestId: "mock-request"
|
|
3643
|
+
});
|
|
3644
|
+
return params[key].trim();
|
|
3645
|
+
}
|
|
3646
|
+
function readArtifactShareRole(params) {
|
|
3647
|
+
if (isRecord$1(params) && params.role === "materializer") return "materializer";
|
|
3648
|
+
if (isRecord$1(params) && params.role === "viewer") return "viewer";
|
|
3649
|
+
throw createSeedAppError({
|
|
3650
|
+
code: "invalid_payload_schema",
|
|
3651
|
+
message: "Missing role",
|
|
3652
|
+
requestId: "mock-request"
|
|
3653
|
+
});
|
|
3654
|
+
}
|
|
3655
|
+
function assertArtifactPolicyUpdate(params) {
|
|
3656
|
+
if (isRecord$1(params) && (params.retentionStatus !== void 0 || params.retentionExpiresAt !== void 0 || params.redactionStatus !== void 0 || params.downloadPolicy !== void 0)) return;
|
|
3657
|
+
throw createSeedAppError({
|
|
3658
|
+
code: "invalid_payload_schema",
|
|
3659
|
+
message: "Artifact policy update is empty",
|
|
3660
|
+
requestId: "mock-request"
|
|
3661
|
+
});
|
|
3662
|
+
}
|
|
3663
|
+
function readRequiredNestedString(params, path) {
|
|
3664
|
+
let current = params;
|
|
3665
|
+
for (const key of path) {
|
|
3666
|
+
if (!isRecord$1(current)) throw createSeedAppError({
|
|
3667
|
+
code: "invalid_payload_schema",
|
|
3668
|
+
message: `Missing ${path.join(".")}`,
|
|
3669
|
+
requestId: "mock-request"
|
|
3670
|
+
});
|
|
3671
|
+
current = current[key];
|
|
3672
|
+
}
|
|
3673
|
+
if (typeof current !== "string" || !current.trim()) throw createSeedAppError({
|
|
3674
|
+
code: "invalid_payload_schema",
|
|
3675
|
+
message: `Missing ${path.join(".")}`,
|
|
3676
|
+
requestId: "mock-request"
|
|
3677
|
+
});
|
|
3678
|
+
return current.trim();
|
|
3679
|
+
}
|
|
3680
|
+
function readAgentStreamChannel(params) {
|
|
3681
|
+
if (isRecord$1(params) && typeof params.conversationId === "string" && params.conversationId.length > 0) return `conversation:${params.conversationId}`;
|
|
3682
|
+
if (isRecord$1(params) && typeof params.runId === "string" && params.runId.length > 0) return `run:${params.runId}`;
|
|
3683
|
+
return "run:mock-run";
|
|
3684
|
+
}
|
|
3685
|
+
function readResourceUri(params) {
|
|
3686
|
+
if (isRecord$1(params) && typeof params.uri === "string" && params.uri.length > 0) return params.uri;
|
|
3687
|
+
return "mock://resource";
|
|
3688
|
+
}
|
|
3689
|
+
function readRoleTemplateId(params) {
|
|
3690
|
+
if (isRecord$1(params) && typeof params.id === "string" && params.id.length > 0) return params.id;
|
|
3691
|
+
return "mock-role-template";
|
|
3692
|
+
}
|
|
3693
|
+
function readTeamId(params) {
|
|
3694
|
+
if (isRecord$1(params) && typeof params.id === "string" && params.id.length > 0) return params.id;
|
|
3695
|
+
return "mock-team";
|
|
3696
|
+
}
|
|
3697
|
+
function readName(params) {
|
|
3698
|
+
if (isRecord$1(params) && typeof params.name === "string" && params.name.length > 0) return params.name;
|
|
3699
|
+
}
|
|
3700
|
+
function readWorkspace(params) {
|
|
3701
|
+
if (isRecord$1(params) && typeof params.workspace === "string" && params.workspace.length > 0) return params.workspace;
|
|
3702
|
+
}
|
|
3703
|
+
function readWorkspaceMode(params) {
|
|
3704
|
+
if (isRecord$1(params) && typeof params.workspaceMode === "string" && params.workspaceMode.length > 0) return params.workspaceMode;
|
|
3705
|
+
return "shared";
|
|
3706
|
+
}
|
|
3707
|
+
function readTeammateSourcePolicy(params) {
|
|
3708
|
+
if (isRecord$1(params) && typeof params.teammateSourcePolicy === "string" && params.teammateSourcePolicy.length > 0) return params.teammateSourcePolicy;
|
|
3709
|
+
return "all";
|
|
3710
|
+
}
|
|
3711
|
+
function readEmployeeId(params) {
|
|
3712
|
+
if (isRecord$1(params) && typeof params.id === "string" && params.id.length > 0) return params.id;
|
|
3713
|
+
return "mock-employee";
|
|
3714
|
+
}
|
|
3715
|
+
function readEmployeeIdFromOpenConversation(params) {
|
|
3716
|
+
if (isRecord$1(params) && typeof params.employeeId === "string" && params.employeeId.length > 0) return params.employeeId;
|
|
3717
|
+
return "mock-employee";
|
|
3718
|
+
}
|
|
3719
|
+
function readEmployeeMemoryEmployeeId(params) {
|
|
3720
|
+
if (isRecord$1(params) && typeof params.employeeId === "string" && params.employeeId.length > 0) return params.employeeId;
|
|
3721
|
+
return "mock-employee";
|
|
3722
|
+
}
|
|
3723
|
+
function readEmployeeActivityEmployeeId(params) {
|
|
3724
|
+
if (isRecord$1(params) && typeof params.employeeId === "string" && params.employeeId.length > 0) return params.employeeId;
|
|
3725
|
+
return "mock-employee";
|
|
3726
|
+
}
|
|
3727
|
+
function readRoleMakerSessionId(params) {
|
|
3728
|
+
if (isRecord$1(params) && typeof params.sessionId === "string" && params.sessionId.length > 0) return params.sessionId;
|
|
3729
|
+
return "mock-role-maker-session";
|
|
3730
|
+
}
|
|
3731
|
+
function readRoleMakerDraftPayload(params) {
|
|
3732
|
+
if (isRecord$1(params) && "payload" in params) return params.payload;
|
|
3733
|
+
if (isRecord$1(params) && "currentDraft" in params) return params.currentDraft;
|
|
3734
|
+
if (isRecord$1(params) && typeof params.text === "string") return { text: params.text };
|
|
3735
|
+
}
|
|
3736
|
+
function isRecord$1(value) {
|
|
3737
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3738
|
+
}
|
|
3739
|
+
function readRequestedCapabilities(params) {
|
|
3740
|
+
if (!params || typeof params !== "object" || Array.isArray(params)) return [];
|
|
3741
|
+
const capabilities = params.capabilities;
|
|
3742
|
+
if (!Array.isArray(capabilities)) return [];
|
|
3743
|
+
return capabilities.filter((capability) => typeof capability === "string");
|
|
3744
|
+
}
|
|
3745
|
+
function createMockBridgeDiagnostics(options, params) {
|
|
3746
|
+
const grants = options.grants ?? [];
|
|
3747
|
+
const diagnostics = {
|
|
3748
|
+
appId: options.appId,
|
|
3749
|
+
hostVersion: options.hostVersion ?? "mock-host",
|
|
3750
|
+
runtime: "mock",
|
|
3751
|
+
transport: "mock",
|
|
3752
|
+
grants: {
|
|
3753
|
+
total: grants.length,
|
|
3754
|
+
enabled: grants.filter((grant) => grant.policy === "enabled").length,
|
|
3755
|
+
ask: grants.filter((grant) => grant.policy === "ask").length,
|
|
3756
|
+
disabled: grants.filter((grant) => grant.policy === "disabled").length
|
|
3757
|
+
},
|
|
3758
|
+
methods: DEFAULT_MOCK_METHODS.map((method) => ({
|
|
3759
|
+
method,
|
|
3760
|
+
support: "mock"
|
|
3761
|
+
})),
|
|
3762
|
+
transports: [{
|
|
3763
|
+
id: "mock",
|
|
3764
|
+
supportsStreaming: true,
|
|
3765
|
+
supportsBroadcast: true
|
|
3766
|
+
}],
|
|
3767
|
+
unsupportedMethods: ["seed.calendar.read"],
|
|
3768
|
+
checks: [{
|
|
3769
|
+
id: "mock-transport",
|
|
3770
|
+
status: "ok",
|
|
3771
|
+
message: "Mock host can resolve SDK diagnostics without a live App Studio frame."
|
|
3772
|
+
}, {
|
|
3773
|
+
id: "unsupported-method",
|
|
3774
|
+
status: "blocked",
|
|
3775
|
+
message: "seed.calendar.read is intentionally unsupported by the mock host."
|
|
3776
|
+
}],
|
|
3777
|
+
agentRuntime: {
|
|
3778
|
+
models: [],
|
|
3779
|
+
skills: [],
|
|
3780
|
+
mcp: {
|
|
3781
|
+
servers: [],
|
|
3782
|
+
tools: []
|
|
3783
|
+
}
|
|
3784
|
+
}
|
|
3785
|
+
};
|
|
3786
|
+
if (isRecord$1(params) && params.includeRoutes === true) diagnostics.routes = _seed_app_studio_protocol.seedBridgeMethodRuntimeRoutes.map((route) => ({ ...route }));
|
|
3787
|
+
return diagnostics;
|
|
3788
|
+
}
|
|
3789
|
+
function withTimeout(input, timeoutMs, method) {
|
|
3790
|
+
if (timeoutMs <= 0) return Promise.reject(createSeedAppError({
|
|
3791
|
+
code: "handshake_timeout",
|
|
3792
|
+
message: "Request timed out.",
|
|
3793
|
+
requestId: method,
|
|
3794
|
+
retryable: true
|
|
3795
|
+
}));
|
|
3796
|
+
return new Promise((resolve, reject) => {
|
|
3797
|
+
const timeout = setTimeout(() => {
|
|
3798
|
+
reject(createSeedAppError({
|
|
3799
|
+
code: "handshake_timeout",
|
|
3800
|
+
message: "Request timed out.",
|
|
3801
|
+
requestId: method,
|
|
3802
|
+
retryable: true
|
|
3803
|
+
}));
|
|
3804
|
+
}, timeoutMs);
|
|
3805
|
+
input.then((value) => {
|
|
3806
|
+
clearTimeout(timeout);
|
|
3807
|
+
resolve(value);
|
|
3808
|
+
}, (error) => {
|
|
3809
|
+
clearTimeout(timeout);
|
|
3810
|
+
reject(error);
|
|
3811
|
+
});
|
|
3812
|
+
});
|
|
3813
|
+
}
|
|
3814
|
+
//#endregion
|
|
3815
|
+
//#region src/windowTransport.ts
|
|
3816
|
+
const HOST_THEME_CHANGED_EVENT = "host.theme.changed";
|
|
3817
|
+
function createWindowSeedAppTransport(options = {}) {
|
|
3818
|
+
const currentWindow = options.currentWindow ?? globalThis.window;
|
|
3819
|
+
if (!currentWindow) throw createSeedAppError({
|
|
3820
|
+
code: "host_unavailable",
|
|
3821
|
+
message: "Window transport requires a browser window.",
|
|
3822
|
+
requestId: "window-unavailable"
|
|
3823
|
+
});
|
|
3824
|
+
const targetWindow = options.targetWindow ?? currentWindow.parent;
|
|
3825
|
+
const channel = options.channel ?? "seed-app-host";
|
|
3826
|
+
const targetOrigin = options.targetOrigin ?? "*";
|
|
3827
|
+
const timeoutMs = options.timeoutMs ?? 3e4;
|
|
3828
|
+
const pending = /* @__PURE__ */ new Map();
|
|
3829
|
+
const eventHandlers = /* @__PURE__ */ new Set();
|
|
3830
|
+
let nextId = 0;
|
|
3831
|
+
let sessionId;
|
|
3832
|
+
let nonce;
|
|
3833
|
+
const requestHost = (method, params, requestOptions) => {
|
|
3834
|
+
const id = `${options.idPrefix ?? "seed-app"}-${Date.now()}-${nextId}`;
|
|
3835
|
+
nextId += 1;
|
|
3836
|
+
const requestTimeoutMs = requestOptions?.timeoutMs ?? timeoutMs;
|
|
3837
|
+
return new Promise((resolve, reject) => {
|
|
3838
|
+
const timeout = setTimeout(() => {
|
|
3839
|
+
pending.delete(id);
|
|
3840
|
+
reject(createSeedAppError({
|
|
3841
|
+
code: "handshake_timeout",
|
|
3842
|
+
message: "Seed App Host request timed out.",
|
|
3843
|
+
requestId: id,
|
|
3844
|
+
retryable: true
|
|
3845
|
+
}));
|
|
3846
|
+
}, requestTimeoutMs);
|
|
3847
|
+
pending.set(id, {
|
|
3848
|
+
resolve,
|
|
3849
|
+
reject,
|
|
3850
|
+
timeout
|
|
3851
|
+
});
|
|
3852
|
+
const envelope = {
|
|
3853
|
+
source: "seed-app-sdk",
|
|
3854
|
+
channel,
|
|
3855
|
+
sessionId,
|
|
3856
|
+
nonce,
|
|
3857
|
+
request: {
|
|
3858
|
+
jsonrpc: "2.0",
|
|
3859
|
+
id,
|
|
3860
|
+
method,
|
|
3861
|
+
params
|
|
3862
|
+
}
|
|
3863
|
+
};
|
|
3864
|
+
targetWindow.postMessage(envelope, targetOrigin);
|
|
3865
|
+
});
|
|
3866
|
+
};
|
|
3867
|
+
const handleMessage = (event) => {
|
|
3868
|
+
const envelope = parseHostEnvelope(event.data, channel);
|
|
3869
|
+
if (!envelope) return;
|
|
3870
|
+
if (options.targetOrigin && options.targetOrigin !== "*" && event.origin !== options.targetOrigin) return;
|
|
3871
|
+
if (envelope.sessionId && sessionId && envelope.sessionId !== sessionId) return;
|
|
3872
|
+
if (envelope.nonce && nonce && envelope.nonce !== nonce) return;
|
|
3873
|
+
if (envelope.sessionId) sessionId = envelope.sessionId;
|
|
3874
|
+
if (envelope.nonce) nonce = envelope.nonce;
|
|
3875
|
+
if ("event" in envelope) {
|
|
3876
|
+
eventHandlers.forEach((handler) => handler(envelope.event));
|
|
3877
|
+
return;
|
|
3878
|
+
}
|
|
3879
|
+
settleResponse(envelope.response, pending);
|
|
3880
|
+
};
|
|
3881
|
+
currentWindow.addEventListener("message", handleMessage);
|
|
3882
|
+
return {
|
|
3883
|
+
request: requestHost,
|
|
3884
|
+
subscribe(handler) {
|
|
3885
|
+
eventHandlers.add(handler);
|
|
3886
|
+
return () => eventHandlers.delete(handler);
|
|
3887
|
+
},
|
|
3888
|
+
async subscribeBridgeEvent(event, handler, requestOptions, params) {
|
|
3889
|
+
const scopedHandler = (nextEvent) => {
|
|
3890
|
+
if (matchesSeedAppHostEventSubscription(event, nextEvent)) handler(nextEvent);
|
|
3891
|
+
};
|
|
3892
|
+
eventHandlers.add(scopedHandler);
|
|
3893
|
+
let subscriptionId;
|
|
3894
|
+
try {
|
|
3895
|
+
subscriptionId = readBridgeSubscriptionId(await requestHost("seed.event.subscribe", buildBridgeSubscribeParams(event, params), requestOptions));
|
|
3896
|
+
} catch (error) {
|
|
3897
|
+
eventHandlers.delete(scopedHandler);
|
|
3898
|
+
throw error;
|
|
3899
|
+
}
|
|
3900
|
+
return () => {
|
|
3901
|
+
eventHandlers.delete(scopedHandler);
|
|
3902
|
+
requestHost("seed.event.unsubscribe", buildBridgeUnsubscribeParams(event, subscriptionId), requestOptions).catch(() => void 0);
|
|
3903
|
+
};
|
|
3904
|
+
},
|
|
3905
|
+
close() {
|
|
3906
|
+
currentWindow.removeEventListener("message", handleMessage);
|
|
3907
|
+
pending.forEach((request, id) => {
|
|
3908
|
+
clearTimeout(request.timeout);
|
|
3909
|
+
request.reject(createSeedAppError({
|
|
3910
|
+
code: "host_unavailable",
|
|
3911
|
+
message: "Seed App Host transport closed.",
|
|
3912
|
+
requestId: id
|
|
3913
|
+
}));
|
|
3914
|
+
});
|
|
3915
|
+
pending.clear();
|
|
3916
|
+
eventHandlers.clear();
|
|
3917
|
+
}
|
|
3918
|
+
};
|
|
3919
|
+
}
|
|
3920
|
+
function buildBridgeSubscribeParams(event, params) {
|
|
3921
|
+
return {
|
|
3922
|
+
...params,
|
|
3923
|
+
type: event
|
|
3924
|
+
};
|
|
3925
|
+
}
|
|
3926
|
+
function buildBridgeUnsubscribeParams(event, subscriptionId) {
|
|
3927
|
+
return subscriptionId ? {
|
|
3928
|
+
type: event,
|
|
3929
|
+
subscriptionId
|
|
3930
|
+
} : { type: event };
|
|
3931
|
+
}
|
|
3932
|
+
function readBridgeSubscriptionId(value) {
|
|
3933
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
3934
|
+
const subscriptionId = value.subscriptionId;
|
|
3935
|
+
return typeof subscriptionId === "string" && subscriptionId.trim() ? subscriptionId.trim() : void 0;
|
|
3936
|
+
}
|
|
3937
|
+
function installSeedAppThemeSync(options = {}) {
|
|
3938
|
+
const currentWindow = options.currentWindow ?? (typeof window === "undefined" ? void 0 : window);
|
|
3939
|
+
const targetDocument = options.targetDocument ?? currentWindow?.document ?? (typeof document === "undefined" ? void 0 : document);
|
|
3940
|
+
const targetOrigin = options.targetOrigin;
|
|
3941
|
+
let snapshot;
|
|
3942
|
+
const apply = (appearance) => {
|
|
3943
|
+
snapshot = { ...appearance };
|
|
3944
|
+
applyAppearanceToDocument(snapshot, targetDocument);
|
|
3945
|
+
options.onChange?.(snapshot);
|
|
3946
|
+
};
|
|
3947
|
+
const initialAppearance = options.initialAppearance ?? (currentWindow?.location ? resolveSeedAppAppearanceFromSearch(currentWindow.location.search) : void 0);
|
|
3948
|
+
if (initialAppearance) apply(initialAppearance);
|
|
3949
|
+
const handleMessage = (event) => {
|
|
3950
|
+
if (targetOrigin && targetOrigin !== "*" && event.origin !== targetOrigin) return;
|
|
3951
|
+
const appearance = readHostThemeChangedAppearance(event.data);
|
|
3952
|
+
if (appearance) apply(appearance);
|
|
3953
|
+
};
|
|
3954
|
+
currentWindow?.addEventListener("message", handleMessage);
|
|
3955
|
+
return {
|
|
3956
|
+
apply,
|
|
3957
|
+
getSnapshot: () => snapshot,
|
|
3958
|
+
uninstall() {
|
|
3959
|
+
currentWindow?.removeEventListener("message", handleMessage);
|
|
3960
|
+
}
|
|
3961
|
+
};
|
|
3962
|
+
}
|
|
3963
|
+
function settleResponse(response, pending) {
|
|
3964
|
+
const request = pending.get(response.id);
|
|
3965
|
+
if (!request) return;
|
|
3966
|
+
pending.delete(response.id);
|
|
3967
|
+
clearTimeout(request.timeout);
|
|
3968
|
+
if (response.error) {
|
|
3969
|
+
request.reject(response.error);
|
|
3970
|
+
return;
|
|
3971
|
+
}
|
|
3972
|
+
request.resolve(response.result);
|
|
3973
|
+
}
|
|
3974
|
+
function parseHostEnvelope(value, channel) {
|
|
3975
|
+
if (!isRecord(value) || value.source !== "seed-app-host" || value.channel !== channel) return void 0;
|
|
3976
|
+
if (isRecord(value.response) && value.response.jsonrpc === "2.0" && typeof value.response.id === "string") return value;
|
|
3977
|
+
if (isRecord(value.event) && typeof value.event.type === "string") return value;
|
|
3978
|
+
}
|
|
3979
|
+
function isRecord(value) {
|
|
3980
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3981
|
+
}
|
|
3982
|
+
function readHostThemeChangedAppearance(value) {
|
|
3983
|
+
if (!isRecord(value) || value.source !== "seed-app-host") return void 0;
|
|
3984
|
+
if (!isRecord(value.event) || value.event.type !== HOST_THEME_CHANGED_EVENT) return void 0;
|
|
3985
|
+
if (!isRecord(value.event.payload)) return void 0;
|
|
3986
|
+
const theme = value.event.payload.theme;
|
|
3987
|
+
if (theme !== "light" && theme !== "dark") return void 0;
|
|
3988
|
+
const colorScheme = readString(value.event.payload.colorScheme);
|
|
3989
|
+
const fontScale = readNumber(value.event.payload.fontScale);
|
|
3990
|
+
const source = readString(value.event.payload.source);
|
|
3991
|
+
const updatedAt = readNumber(value.event.payload.updatedAt);
|
|
3992
|
+
return {
|
|
3993
|
+
theme,
|
|
3994
|
+
...colorScheme ? { colorScheme } : {},
|
|
3995
|
+
...typeof fontScale === "number" ? { fontScale } : {},
|
|
3996
|
+
...source ? { source } : {},
|
|
3997
|
+
...typeof updatedAt === "number" ? { updatedAt } : {}
|
|
3998
|
+
};
|
|
3999
|
+
}
|
|
4000
|
+
function applyAppearanceToDocument(appearance, targetDocument) {
|
|
4001
|
+
const root = targetDocument?.documentElement;
|
|
4002
|
+
if (!root) return;
|
|
4003
|
+
root.setAttribute("data-theme", appearance.theme);
|
|
4004
|
+
root.setAttribute("data-seed-theme", appearance.theme);
|
|
4005
|
+
root.style.setProperty("color-scheme", appearance.theme);
|
|
4006
|
+
if (appearance.colorScheme) root.setAttribute("data-color-scheme", appearance.colorScheme);
|
|
4007
|
+
else root.removeAttribute("data-color-scheme");
|
|
4008
|
+
if (typeof appearance.fontScale === "number" && Number.isFinite(appearance.fontScale)) root.style.setProperty("--seed-font-scale", String(appearance.fontScale));
|
|
4009
|
+
else root.style.removeProperty("--seed-font-scale");
|
|
4010
|
+
}
|
|
4011
|
+
function readString(value) {
|
|
4012
|
+
return typeof value === "string" && value.trim() ? value : void 0;
|
|
4013
|
+
}
|
|
4014
|
+
function readNumber(value) {
|
|
4015
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return void 0;
|
|
4016
|
+
return value;
|
|
4017
|
+
}
|
|
4018
|
+
//#endregion
|
|
4019
|
+
Object.defineProperty(exports, "SEED_AGUI_STREAM_EVENT_TYPE", {
|
|
4020
|
+
enumerable: true,
|
|
4021
|
+
get: function() {
|
|
4022
|
+
return _seed_app_studio_protocol.SEED_AGUI_STREAM_EVENT_TYPE;
|
|
4023
|
+
}
|
|
4024
|
+
});
|
|
4025
|
+
exports.SEED_APP_BRIDGE_TARGET_DEFAULT_TIMEOUT_MS = SEED_APP_BRIDGE_TARGET_DEFAULT_TIMEOUT_MS;
|
|
4026
|
+
Object.defineProperty(exports, "SeedAppAgentStreamConsumptionGuard", {
|
|
4027
|
+
enumerable: true,
|
|
4028
|
+
get: function() {
|
|
4029
|
+
return _seed_app_studio_protocol.SeedAppAgentStreamConsumptionGuard;
|
|
4030
|
+
}
|
|
4031
|
+
});
|
|
4032
|
+
exports.appendSeedAppAppearanceSearchParams = appendSeedAppAppearanceSearchParams;
|
|
4033
|
+
exports.appendSeedAppBridgeTargetSearchParams = appendSeedAppBridgeTargetSearchParams;
|
|
4034
|
+
Object.defineProperty(exports, "createSeedAppAgentStreamConsumptionGuard", {
|
|
4035
|
+
enumerable: true,
|
|
4036
|
+
get: function() {
|
|
4037
|
+
return _seed_app_studio_protocol.createSeedAppAgentStreamConsumptionGuard;
|
|
4038
|
+
}
|
|
4039
|
+
});
|
|
4040
|
+
Object.defineProperty(exports, "createSeedAppAgentStreamEvent", {
|
|
4041
|
+
enumerable: true,
|
|
4042
|
+
get: function() {
|
|
4043
|
+
return _seed_app_studio_protocol.createSeedAppAgentStreamEvent;
|
|
4044
|
+
}
|
|
4045
|
+
});
|
|
4046
|
+
exports.createSeedAppClient = createSeedAppClient;
|
|
4047
|
+
exports.createSeedAppElectronTransport = createSeedAppElectronTransport;
|
|
4048
|
+
exports.createSeedAppEndpointTraceBridge = createSeedAppEndpointTraceBridge;
|
|
4049
|
+
exports.createSeedAppEndpointTransport = createSeedAppEndpointTransport;
|
|
4050
|
+
exports.createSeedAppError = createSeedAppError;
|
|
4051
|
+
exports.createSeedAppMockTransport = createSeedAppMockTransport;
|
|
4052
|
+
exports.createSeedAppReactNativeTransport = createSeedAppReactNativeTransport;
|
|
4053
|
+
exports.createSeedAppRootTraceContext = createSeedAppRootTraceContext;
|
|
4054
|
+
exports.createSeedAppWebSocketTransport = createSeedAppWebSocketTransport;
|
|
4055
|
+
exports.createSeedAppWorkspaceClient = createSeedAppWorkspaceClient;
|
|
4056
|
+
exports.createSeedBridgeGoldenCaseReport = createSeedBridgeGoldenCaseReport;
|
|
4057
|
+
exports.createWindowSeedAppTransport = createWindowSeedAppTransport;
|
|
4058
|
+
exports.ensureSeedAppTraceCarrier = ensureSeedAppTraceCarrier;
|
|
4059
|
+
exports.extractSeedAppTraceContext = extractSeedAppTraceContext;
|
|
4060
|
+
exports.injectSeedAppTraceContext = injectSeedAppTraceContext;
|
|
4061
|
+
exports.installSeedAppConsoleBridge = installSeedAppConsoleBridge;
|
|
4062
|
+
exports.installSeedAppThemeSync = installSeedAppThemeSync;
|
|
4063
|
+
Object.defineProperty(exports, "isSeedAguiEvent", {
|
|
4064
|
+
enumerable: true,
|
|
4065
|
+
get: function() {
|
|
4066
|
+
return _seed_app_studio_protocol.isSeedAguiEvent;
|
|
4067
|
+
}
|
|
4068
|
+
});
|
|
4069
|
+
Object.defineProperty(exports, "isSeedAppAgentStreamEvent", {
|
|
4070
|
+
enumerable: true,
|
|
4071
|
+
get: function() {
|
|
4072
|
+
return _seed_app_studio_protocol.isSeedAppAgentStreamEvent;
|
|
4073
|
+
}
|
|
4074
|
+
});
|
|
4075
|
+
exports.isSeedAppTraceEnabled = isSeedAppTraceEnabled;
|
|
4076
|
+
exports.isSeedBridgeGoldenCaseReport = isSeedBridgeGoldenCaseReport;
|
|
4077
|
+
Object.defineProperty(exports, "matchesSeedAguiStreamScope", {
|
|
4078
|
+
enumerable: true,
|
|
4079
|
+
get: function() {
|
|
4080
|
+
return _seed_app_studio_protocol.matchesSeedAguiStreamScope;
|
|
4081
|
+
}
|
|
4082
|
+
});
|
|
4083
|
+
Object.defineProperty(exports, "resolveSeedAguiEventIdentity", {
|
|
4084
|
+
enumerable: true,
|
|
4085
|
+
get: function() {
|
|
4086
|
+
return _seed_app_studio_protocol.resolveSeedAguiEventIdentity;
|
|
4087
|
+
}
|
|
4088
|
+
});
|
|
4089
|
+
Object.defineProperty(exports, "resolveSeedAppAgentStreamEventIdentity", {
|
|
4090
|
+
enumerable: true,
|
|
4091
|
+
get: function() {
|
|
4092
|
+
return _seed_app_studio_protocol.resolveSeedAppAgentStreamEventIdentity;
|
|
4093
|
+
}
|
|
4094
|
+
});
|
|
4095
|
+
Object.defineProperty(exports, "resolveSeedAppAgentStreamSemanticKey", {
|
|
4096
|
+
enumerable: true,
|
|
4097
|
+
get: function() {
|
|
4098
|
+
return _seed_app_studio_protocol.resolveSeedAppAgentStreamSemanticKey;
|
|
4099
|
+
}
|
|
4100
|
+
});
|
|
4101
|
+
exports.resolveSeedAppAppearanceFromSearch = resolveSeedAppAppearanceFromSearch;
|
|
4102
|
+
exports.resolveSeedAppBridgeTargetFromSearch = resolveSeedAppBridgeTargetFromSearch;
|
|
4103
|
+
Object.defineProperty(exports, "seedAppAgentStreamEventTypes", {
|
|
4104
|
+
enumerable: true,
|
|
4105
|
+
get: function() {
|
|
4106
|
+
return _seed_app_studio_protocol.seedAppAgentStreamEventTypes;
|
|
4107
|
+
}
|
|
4108
|
+
});
|
|
4109
|
+
Object.defineProperty(exports, "seedAppAguiStreamEventTypes", {
|
|
4110
|
+
enumerable: true,
|
|
4111
|
+
get: function() {
|
|
4112
|
+
return _seed_app_studio_protocol.seedAppAguiStreamEventTypes;
|
|
4113
|
+
}
|
|
4114
|
+
});
|
|
4115
|
+
exports.seedAppAppearanceSearchParams = seedAppAppearanceSearchParams;
|
|
4116
|
+
exports.seedAppBridgeTargetModes = seedAppBridgeTargetModes;
|
|
4117
|
+
exports.seedAppBridgeTargetSearchParams = seedAppBridgeTargetSearchParams;
|
|
4118
|
+
exports.seedBridgeGoldenCasePrimaryTransportByRuntime = seedBridgeGoldenCasePrimaryTransportByRuntime;
|
|
4119
|
+
exports.seedBridgeGoldenCaseReportSchemaVersion = seedBridgeGoldenCaseReportSchemaVersion;
|
|
4120
|
+
exports.validateSeedBridgeGoldenCaseReport = validateSeedBridgeGoldenCaseReport;
|