@xopcai/xopc 0.0.265 → 0.0.267
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/browser-ext/dist/assets/{auth-CknJ00ae.js → auth-D_IL6naL.js} +603 -203
- package/dist/browser-ext/dist/assets/sidepanel-CEPaVLjr.css +155 -0
- package/dist/browser-ext/dist/background.js +698 -169
- package/dist/browser-ext/dist/sidepanel.html +3 -3
- package/dist/browser-ext/dist/sidepanel.js +148 -391
- package/dist/browser-ext/manifest.json +3 -6
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-DRTTU2H3.js → agent-avatar-dicebear-ZrSAMchm.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-defaults-settings-panel-BsXF7LVi.js → agent-defaults-settings-panel-DiNFN8qS.js} +1 -1
- package/dist/gateway/static/root/assets/{agents-DpjWfxYx.js → agents-Bu6Hf59K.js} +1 -1
- package/dist/gateway/static/root/assets/{app-management-settings-panel-33Mc81h2.js → app-management-settings-panel-D-L6yjS7.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-C8JECSiJ.js → appearance-settings-BW5eb53j.js} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-t64FcxIZ.js → apps-page-CiQIdz3N.js} +1 -1
- package/dist/gateway/static/root/assets/{archive-plugin-PzZ6SzfY.js → archive-plugin-DwXvu1p9.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-OhLL6K__.js → attachment-load-DqU2BYBp.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-page-BpAYwuvx.js → automations-page-C7qoj9Sd.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-workspace-B6aTT5IC.js → automations-workspace-CIARTLHO.js} +1 -1
- package/dist/gateway/static/root/assets/{binary-plugins-B5XpGsHg.js → binary-plugins-CfplDak2.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-BgEhoTnN.js → block-editor-CzLs38ru.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-automations-page-CBLunDRH.js → browser-automations-page-_SKi8s9b.js} +1 -1
- package/dist/gateway/static/root/assets/browser-settings-page-DTdx66sX.js +8 -0
- package/dist/gateway/static/root/assets/{capabilities-settings-panel-DftNnICK.js → capabilities-settings-panel-Ck9U0Olx.js} +1 -1
- package/dist/gateway/static/root/assets/{channels-settings-CpuWa5qu.js → channels-settings-I_vDpbnl.js} +1 -1
- package/dist/gateway/static/root/assets/{chat-terminal-dock-mnPsS_GJ.js → chat-terminal-dock-snvAu3hJ.js} +1 -1
- package/dist/gateway/static/root/assets/{connectors-page-D3nHGnNV.js → connectors-page-BmKTnzo1.js} +1 -1
- package/dist/gateway/static/root/assets/{cron-expression-NcGBvIfe.js → cron-expression-BhGZSbT6.js} +1 -1
- package/dist/gateway/static/root/assets/{date-picker-BrVcl7H5.js → date-picker-Cep5f9b-.js} +1 -1
- package/dist/gateway/static/root/assets/{dependency-picker--edd48Y9.js → dependency-picker-BzSEBQku.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-vuyyb6I6.js → desktop-pet-BwX_6Gah.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-DWredJwO.js → desktop-pet-settings-WrbVsU2b.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-CtBW4ebj.js → extension-debug-page-BdXSwtAZ.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-CsUrmtW7.js → extension-page-I95ukbRb.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-BUd31AuK.js → extension-settings-page-8zUisBay.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-settings-C6FQv7op.js → gateway-settings-DGj5b9Yv.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-settings-CDo1Gjp2.js → heartbeat-settings-BGQxU-Ug.js} +1 -1
- package/dist/gateway/static/root/assets/{home-page-D3_uvVJY.js → home-page-BZPpK0A3.js} +1 -1
- package/dist/gateway/static/root/assets/{index-Dpu7EKTm.js → index-D2OtD9Wa.js} +8 -8
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-CNVmrCrV.js → keyboard-shortcuts-settings-BUe_om99.js} +1 -1
- package/dist/gateway/static/root/assets/{local-app-workbench-page-B0IVDvs-.js → local-app-workbench-page-CbQnffS9.js} +1 -1
- package/dist/gateway/static/root/assets/{local-apps-page-DvkarvLt.js → local-apps-page-BZaLzfms.js} +1 -1
- package/dist/gateway/static/root/assets/{logs-page-BknY0vZf.js → logs-page-WWCHqW_x.js} +1 -1
- package/dist/gateway/static/root/assets/{management-settings-DW6hJINQ.js → management-settings-Duf8w-Bl.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-DYautOWY.js → media-plugins-2-QfzHmg.js} +1 -1
- package/dist/gateway/static/root/assets/{mobile-pairing-wizard-Dgu03cv-.js → mobile-pairing-wizard-DRaVIAt1.js} +1 -1
- package/dist/gateway/static/root/assets/{note-detail-panel-Cv9uH6Jq.js → note-detail-panel-BT-fWGxX.js} +3 -3
- package/dist/gateway/static/root/assets/{notes-page-DcIAiYJn.js → notes-page-C4705PzN.js} +1 -1
- package/dist/gateway/static/root/assets/{page-tabs-DF6Q5XvA.js → page-tabs-DaZ7QpNO.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-CQJH_jAN.js → preview-open-alternatives-CWEuqCmv.js} +1 -1
- package/dist/gateway/static/root/assets/{project-detail-page-D65JfNSw.js → project-detail-page-BZegJ6an.js} +1 -1
- package/dist/gateway/static/root/assets/{projects-page-BfWF7Sz-.js → projects-page-B7hUHeLI.js} +1 -1
- package/dist/gateway/static/root/assets/{remote-access-hub-DA1g4CUD.js → remote-access-hub-BE9hZpdh.js} +1 -1
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-0-bFSBB8.js → runtime-tools-settings-panel-CslkSM-m.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-csGbhTUr.js → schema-form-B2gvVrBq.js} +1 -1
- package/dist/gateway/static/root/assets/{sessions-page-DXPBNhrZ.js → sessions-page-DmAAPMo1.js} +1 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-CvBocGDA.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-YdrdY8B3.js → settings-form-section-CmlnuSO3.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-Cv9waRn3.js → settings-page-DWWgyelT.js} +1 -1
- package/dist/gateway/static/root/assets/{setup-status-panel-DPf-_05Z.js → setup-status-panel-CJrN3m1o.js} +1 -1
- package/dist/gateway/static/root/assets/{share-preview-page-DBoEHTPn.js → share-preview-page-C12oyIKk.js} +1 -1
- package/dist/gateway/static/root/assets/{shares-settings-BQP5yNcO.js → shares-settings-D4BtSiLq.js} +1 -1
- package/dist/gateway/static/root/assets/{skill-reload-api-B5T8cgjq.js → skill-reload-api-CzKGn056.js} +1 -1
- package/dist/gateway/static/root/assets/{skills-page-D1G8h2Lt.js → skills-page-yhW7LLqk.js} +1 -1
- package/dist/gateway/static/root/assets/{skills-page.utils-BNZYTop7.js → skills-page.utils-FaTlq8t-.js} +1 -1
- package/dist/gateway/static/root/assets/{system-settings-panel-ByuppbHi.js → system-settings-panel-CMrkoJLN.js} +1 -1
- package/dist/gateway/static/root/assets/{task-detail-page-DNmkrF6y.js → task-detail-page-BUaO62cy.js} +1 -1
- package/dist/gateway/static/root/assets/{tunnel-api-CxWGpbwr.js → tunnel-api-C2tAZaJz.js} +1 -1
- package/dist/gateway/static/root/assets/{use-autosave-B0l4qubU.js → use-autosave-BsDRt4QI.js} +1 -1
- package/dist/gateway/static/root/assets/{user-model-page-DMsI6YI7.js → user-model-page-Dy8h1SSJ.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-api-key-field-Bcz7boJg.js → voice-api-key-field-CS5heYMP.js} +1 -1
- package/dist/gateway/static/root/assets/{work-discovery-overlay-Dujq1j_u.js → work-discovery-overlay-BeSz5Th5.js} +1 -1
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-CxNYbLNB.js → workflow-run-setup-panel-BebW0CDK.js} +1 -1
- package/dist/gateway/static/root/assets/{workflows-page-Dk1jK2nv.js → workflows-page-CU8jFgPF.js} +1 -1
- package/dist/gateway/static/root/index.html +1 -1
- package/dist/package.js +1 -1
- package/dist/packages/browser-control-contract/src/index.js +30 -1
- package/dist/src/agent/agent-manager.js +4 -4
- package/dist/src/agent/background-review/run-background-review.js +1 -1
- package/dist/src/agent/embedded/model-runtime.js +1 -1
- package/dist/src/agent/embedded/run-for-session.js +1 -1
- package/dist/src/agent/external-tools/endpoint-provider.js +3 -1
- package/dist/src/agent/image/tool-model-config.js +1 -1
- package/dist/src/agent/mcp/oauth/mcp-oauth-store.js +1 -1
- package/dist/src/agent/service.js +2 -2
- package/dist/src/agent/session/session-inspector.js +1 -1
- package/dist/src/agent/skills/hub-lock.js +1 -1
- package/dist/src/agent/tasks/task-judge-service.js +3 -3
- package/dist/src/agent/tools/factory.js +1 -1
- package/dist/src/ai-assist/text-assist.js +1 -1
- package/dist/src/auth/credentials.js +2 -2
- package/dist/src/auth/oauth/xopc-cloud.js +1 -1
- package/dist/src/auth/profiles/store.js +1 -1
- package/dist/src/auth/sync-provider-auth.js +2 -2
- package/dist/src/browser/automations/runtime-service.d.ts +2 -0
- package/dist/src/browser/automations/runtime-service.js +1 -1
- package/dist/src/browser/drivers/create-driver.d.ts +2 -1
- package/dist/src/browser/drivers/create-driver.js +4 -6
- package/dist/src/browser/drivers/extension-driver.d.ts +7 -2
- package/dist/src/browser/enrollment.d.ts +8 -0
- package/dist/src/browser/enrollment.js +25 -0
- package/dist/src/browser/extension-identity.d.ts +2 -0
- package/dist/src/browser/extension-identity.js +5 -0
- package/dist/src/browser/native-messaging-host.d.ts +6 -3
- package/dist/src/browser/native-messaging-host.js +23 -5
- package/dist/src/browser/providers/browser-ext-install.d.ts +4 -1
- package/dist/src/browser/providers/browser-ext-install.js +47 -8
- package/dist/src/browser/providers/extension.js +1 -1
- package/dist/src/browser/providers/realtime-extension.d.ts +29 -0
- package/dist/src/browser/providers/realtime-extension.js +83 -0
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
- package/dist/src/cli/commands/models.js +1 -1
- package/dist/src/config/index.js +4 -4
- package/dist/src/config/loader.js +2 -2
- package/dist/src/config/models-json.js +1 -1
- package/dist/src/endpoint-tools/policy.js +10 -3
- package/dist/src/extensions/lockfile.js +1 -1
- package/dist/src/gateway/agents-admin.js +1 -1
- package/dist/src/gateway/file-path-classifier.js +1 -1
- package/dist/src/gateway/hono/app.js +6 -1
- package/dist/src/gateway/hono/lib/config-payload.js +1 -1
- package/dist/src/gateway/hono/routes/browser.js +20 -30
- package/dist/src/gateway/hono/routes/models.js +1 -1
- package/dist/src/gateway/security/origin-check.js +5 -0
- package/dist/src/gateway/service/config-coordinator.d.ts +0 -2
- package/dist/src/gateway/service/config-coordinator.js +1 -3
- package/dist/src/gateway/service.d.ts +0 -11
- package/dist/src/gateway/service.js +5 -83
- package/dist/src/providers/auth-runtime/resolve-auth.js +1 -1
- package/dist/src/providers/index.js +2 -2
- package/dist/src/providers/model-registry.js +1 -1
- package/dist/src/providers/register-runtime-providers.js +1 -1
- package/dist/src/storage/sqlite/device-pairing-approval.js +24 -3
- package/dist/src/storage/sqlite/device-pairing-repository.d.ts +10 -1
- package/dist/src/storage/sqlite/device-pairing-repository.js +6 -5
- package/dist/src/storage/sqlite/migrations/157_browser_endpoint_principals.sql +50 -0
- package/dist/src/storage/sqlite/migrations/158_browser_native_enrollment.sql +4 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +3 -3
- package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/tui/tui-startup-resources.js +1 -1
- package/dist/src/tui/tui.js +1 -1
- package/dist/src/work-discovery/service.js +1 -1
- package/dist/src/workflows/service/workflow-run-service.js +1 -1
- package/package.json +3 -3
- package/dist/browser-ext/dist/assets/sidepanel-C69P3v9u.css +0 -113
- package/dist/gateway/static/root/assets/browser-settings-page-CSfpPCuD.js +0 -8
- package/dist/gateway/static/root/assets/settings-advanced-gate-DxeZm67O.js +0 -1
|
@@ -5099,6 +5099,562 @@ function superRefine(fn, params) {
|
|
|
5099
5099
|
return _superRefine(fn, params);
|
|
5100
5100
|
}
|
|
5101
5101
|
|
|
5102
|
+
const ENDPOINT_PROTOCOL_VERSION = 2;
|
|
5103
|
+
const endpointKindSchema = _enum(["web", "desktop", "mobile", "browser"]);
|
|
5104
|
+
const endpointAvailabilitySchema = _enum(["foreground", "background"]);
|
|
5105
|
+
const endpointEffectSchema = _enum(["read", "write", "destructive"]);
|
|
5106
|
+
const endpointConfirmationSchema = _enum(["never", "always"]);
|
|
5107
|
+
const endpointResultKindSchema = _enum(["text", "json", "file"]);
|
|
5108
|
+
const endpointSensitivitySchema = _enum(["public", "personal"]);
|
|
5109
|
+
const identifierSchema = string().min(1).max(160);
|
|
5110
|
+
const turnTokenSchema = string().min(32).max(160).regex(/^[A-Za-z0-9_-]+$/);
|
|
5111
|
+
const turnOriginSchema = discriminatedUnion("type", [
|
|
5112
|
+
strictObject({ type: literal("endpoint"), endpointId: identifierSchema }),
|
|
5113
|
+
strictObject({ type: literal("channel"), channel: string().min(1).max(64) }),
|
|
5114
|
+
strictObject({
|
|
5115
|
+
type: literal("system"),
|
|
5116
|
+
source: _enum(["cli", "automation", "heartbeat", "workflow", "internal"])
|
|
5117
|
+
})
|
|
5118
|
+
]);
|
|
5119
|
+
const endpointTurnOriginSchema = turnOriginSchema.options[0];
|
|
5120
|
+
endpointTurnOriginSchema.extend({ token: turnTokenSchema });
|
|
5121
|
+
const messageIdSchema$1 = uuid();
|
|
5122
|
+
const timestampSchema$1 = number().int().nonnegative();
|
|
5123
|
+
const jsonObjectSchema = record(string(), unknown());
|
|
5124
|
+
const endpointToolDescriptorSchema = strictObject({
|
|
5125
|
+
name: string().regex(/^[a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)+$/).max(120),
|
|
5126
|
+
title: string().min(1).max(120),
|
|
5127
|
+
description: string().min(1).max(2e3),
|
|
5128
|
+
inputSchema: jsonObjectSchema,
|
|
5129
|
+
outputSchema: jsonObjectSchema,
|
|
5130
|
+
policyId: string().regex(/^[a-z][a-z0-9_.-]+$/).max(120),
|
|
5131
|
+
sensitivity: endpointSensitivitySchema,
|
|
5132
|
+
effect: endpointEffectSchema,
|
|
5133
|
+
confirmation: endpointConfirmationSchema,
|
|
5134
|
+
requiresForeground: boolean(),
|
|
5135
|
+
requiredPermissions: array(string().min(1).max(120)).max(20),
|
|
5136
|
+
timeoutMs: number().int().min(1e3).max(3e5),
|
|
5137
|
+
maxConcurrency: number().int().min(1).max(4),
|
|
5138
|
+
supportsCancellation: boolean(),
|
|
5139
|
+
idempotent: boolean(),
|
|
5140
|
+
resultKinds: array(endpointResultKindSchema).min(1).max(3)
|
|
5141
|
+
});
|
|
5142
|
+
strictObject({
|
|
5143
|
+
principalId: identifierSchema,
|
|
5144
|
+
displayName: string().min(1).max(120),
|
|
5145
|
+
kind: endpointKindSchema,
|
|
5146
|
+
platform: string().min(1).max(80),
|
|
5147
|
+
publicKey: string().min(32).max(2048)
|
|
5148
|
+
});
|
|
5149
|
+
strictObject({
|
|
5150
|
+
endpointId: identifierSchema
|
|
5151
|
+
});
|
|
5152
|
+
const backgroundCapabilitySchema = string().regex(/^[a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)+$/).max(120);
|
|
5153
|
+
strictObject({
|
|
5154
|
+
jobId: uuid(),
|
|
5155
|
+
endpointId: identifierSchema,
|
|
5156
|
+
capability: backgroundCapabilitySchema,
|
|
5157
|
+
input: jsonObjectSchema,
|
|
5158
|
+
idempotencyKey: identifierSchema,
|
|
5159
|
+
consentGrantId: identifierSchema,
|
|
5160
|
+
createdAt: timestampSchema$1,
|
|
5161
|
+
expiresAt: timestampSchema$1
|
|
5162
|
+
});
|
|
5163
|
+
strictObject({
|
|
5164
|
+
jobId: uuid(),
|
|
5165
|
+
status: _enum(["queued", "running", "succeeded", "failed", "cancelled"]),
|
|
5166
|
+
updatedAt: timestampSchema$1,
|
|
5167
|
+
output: jsonObjectSchema.optional(),
|
|
5168
|
+
error: strictObject({
|
|
5169
|
+
code: identifierSchema,
|
|
5170
|
+
message: string().min(1).max(2e3)
|
|
5171
|
+
}).optional()
|
|
5172
|
+
});
|
|
5173
|
+
strictObject({
|
|
5174
|
+
eventId: uuid(),
|
|
5175
|
+
endpointId: identifierSchema,
|
|
5176
|
+
subscriptionId: identifierSchema,
|
|
5177
|
+
eventType: backgroundCapabilitySchema,
|
|
5178
|
+
data: jsonObjectSchema,
|
|
5179
|
+
occurredAt: timestampSchema$1
|
|
5180
|
+
});
|
|
5181
|
+
const endpointHelloPayloadSchema = strictObject({
|
|
5182
|
+
principalId: identifierSchema,
|
|
5183
|
+
endpointId: identifierSchema,
|
|
5184
|
+
connectionInstanceId: uuid(),
|
|
5185
|
+
displayName: string().min(1).max(120),
|
|
5186
|
+
kind: endpointKindSchema,
|
|
5187
|
+
platform: string().min(1).max(80),
|
|
5188
|
+
appVersion: string().min(1).max(80),
|
|
5189
|
+
availability: endpointAvailabilitySchema,
|
|
5190
|
+
nonce: identifierSchema,
|
|
5191
|
+
signedAt: timestampSchema$1,
|
|
5192
|
+
signature: string().min(16).max(1024),
|
|
5193
|
+
tools: array(endpointToolDescriptorSchema).max(100)
|
|
5194
|
+
});
|
|
5195
|
+
const toolInvokePayloadSchema = strictObject({
|
|
5196
|
+
invocationId: uuid(),
|
|
5197
|
+
toolCallId: identifierSchema,
|
|
5198
|
+
toolName: endpointToolDescriptorSchema.shape.name,
|
|
5199
|
+
arguments: jsonObjectSchema,
|
|
5200
|
+
descriptorRevision: string().min(1).max(128),
|
|
5201
|
+
confirmationRequired: boolean(),
|
|
5202
|
+
deadlineAt: timestampSchema$1,
|
|
5203
|
+
uploadGrant: strictObject({
|
|
5204
|
+
path: string().startsWith("/").max(500),
|
|
5205
|
+
token: identifierSchema,
|
|
5206
|
+
maxBytes: number().int().positive(),
|
|
5207
|
+
maxFiles: number().int().min(1).max(20),
|
|
5208
|
+
expiresAt: timestampSchema$1
|
|
5209
|
+
}).optional()
|
|
5210
|
+
});
|
|
5211
|
+
const toolCancelPayloadSchema = strictObject({
|
|
5212
|
+
invocationId: uuid(),
|
|
5213
|
+
reason: string().min(1).max(500)
|
|
5214
|
+
});
|
|
5215
|
+
const invocationPayloadSchema = strictObject({ invocationId: uuid() });
|
|
5216
|
+
const endpointToolContentSchema = discriminatedUnion("type", [
|
|
5217
|
+
strictObject({ type: literal("text"), text: string().max(256 * 1024) }),
|
|
5218
|
+
strictObject({ type: literal("json"), value: unknown() }),
|
|
5219
|
+
strictObject({
|
|
5220
|
+
type: literal("file"),
|
|
5221
|
+
fileId: identifierSchema,
|
|
5222
|
+
name: string().min(1).max(255),
|
|
5223
|
+
mimeType: string().min(1).max(255),
|
|
5224
|
+
size: number().int().nonnegative(),
|
|
5225
|
+
sha256: string().regex(/^[a-f0-9]{64}$/)
|
|
5226
|
+
})
|
|
5227
|
+
]);
|
|
5228
|
+
const endpointToolErrorCodeSchema = _enum([
|
|
5229
|
+
"ENDPOINT_OFFLINE",
|
|
5230
|
+
"ENDPOINT_NOT_FOREGROUND",
|
|
5231
|
+
"ENDPOINT_DISCONNECTED",
|
|
5232
|
+
"TOOL_NOT_FOUND",
|
|
5233
|
+
"TOOL_REVISION_MISMATCH",
|
|
5234
|
+
"INVALID_ARGUMENTS",
|
|
5235
|
+
"PERMISSION_DENIED",
|
|
5236
|
+
"USER_DENIED",
|
|
5237
|
+
"TOOL_BUSY",
|
|
5238
|
+
"TOOL_TIMEOUT",
|
|
5239
|
+
"TOOL_CANCELLED",
|
|
5240
|
+
"RESULT_TOO_LARGE",
|
|
5241
|
+
"PROTOCOL_ERROR"
|
|
5242
|
+
]);
|
|
5243
|
+
const envelope$1 = (type, payload) => strictObject({
|
|
5244
|
+
protocolVersion: literal(ENDPOINT_PROTOCOL_VERSION),
|
|
5245
|
+
messageId: messageIdSchema$1,
|
|
5246
|
+
type: literal(type),
|
|
5247
|
+
sentAt: timestampSchema$1,
|
|
5248
|
+
payload
|
|
5249
|
+
});
|
|
5250
|
+
const clientEndpointMessageSchema = discriminatedUnion("type", [
|
|
5251
|
+
envelope$1("endpoint.availability_changed", strictObject({ availability: endpointAvailabilitySchema })),
|
|
5252
|
+
envelope$1("tool.received", invocationPayloadSchema),
|
|
5253
|
+
envelope$1("tool.progress", strictObject({
|
|
5254
|
+
invocationId: uuid(),
|
|
5255
|
+
message: string().max(500).optional(),
|
|
5256
|
+
percent: number().min(0).max(100).optional()
|
|
5257
|
+
})),
|
|
5258
|
+
envelope$1("tool.result", strictObject({
|
|
5259
|
+
invocationId: uuid(),
|
|
5260
|
+
content: array(endpointToolContentSchema).min(1).max(20),
|
|
5261
|
+
details: jsonObjectSchema.optional()
|
|
5262
|
+
})),
|
|
5263
|
+
envelope$1("tool.error", strictObject({
|
|
5264
|
+
invocationId: uuid(),
|
|
5265
|
+
code: endpointToolErrorCodeSchema,
|
|
5266
|
+
message: string().min(1).max(2e3)
|
|
5267
|
+
})),
|
|
5268
|
+
envelope$1("tool.cancelled", invocationPayloadSchema)
|
|
5269
|
+
]);
|
|
5270
|
+
const serverEndpointMessageSchema = discriminatedUnion("type", [
|
|
5271
|
+
envelope$1("tool.invoke", toolInvokePayloadSchema),
|
|
5272
|
+
envelope$1("tool.cancel", toolCancelPayloadSchema)
|
|
5273
|
+
]);
|
|
5274
|
+
function canonicalJson(value) {
|
|
5275
|
+
if (value === null) return "null";
|
|
5276
|
+
if (typeof value === "string" || typeof value === "boolean") return JSON.stringify(value);
|
|
5277
|
+
if (typeof value === "number") {
|
|
5278
|
+
if (!Number.isFinite(value)) throw new Error("Canonical JSON only supports finite numbers");
|
|
5279
|
+
return JSON.stringify(value);
|
|
5280
|
+
}
|
|
5281
|
+
if (typeof value !== "object") throw new Error(`Canonical JSON does not support ${typeof value}`);
|
|
5282
|
+
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
|
|
5283
|
+
const prototype = Object.getPrototypeOf(value);
|
|
5284
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
5285
|
+
throw new Error("Canonical JSON only supports plain objects");
|
|
5286
|
+
}
|
|
5287
|
+
const record = value;
|
|
5288
|
+
return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(",")}}`;
|
|
5289
|
+
}
|
|
5290
|
+
function endpointHelloSigningPayload(payload) {
|
|
5291
|
+
const { signature: _signature, ...unsigned } = payload;
|
|
5292
|
+
return canonicalJson(unsigned);
|
|
5293
|
+
}
|
|
5294
|
+
|
|
5295
|
+
const REALTIME_PROTOCOL_VERSION = 1;
|
|
5296
|
+
const REALTIME_HEARTBEAT_INTERVAL_MS = 15e3;
|
|
5297
|
+
const REALTIME_HEARTBEAT_TIMEOUT_MS = 45e3;
|
|
5298
|
+
const realtimeClientKindSchema = _enum([
|
|
5299
|
+
"web",
|
|
5300
|
+
"desktop",
|
|
5301
|
+
"mobile",
|
|
5302
|
+
"browser_extension",
|
|
5303
|
+
"tui",
|
|
5304
|
+
"mcp"
|
|
5305
|
+
]);
|
|
5306
|
+
const realtimeTopicSchema = string().min(1).max(512);
|
|
5307
|
+
const realtimeEventNameSchema = string().regex(/^[a-z][a-z0-9_.-]*$/).max(160);
|
|
5308
|
+
const messageIdSchema = uuid();
|
|
5309
|
+
const timestampSchema = number().int().nonnegative();
|
|
5310
|
+
const sequenceSchema = number().int().positive();
|
|
5311
|
+
const envelope = (kind, payload) => strictObject({
|
|
5312
|
+
protocolVersion: literal(REALTIME_PROTOCOL_VERSION),
|
|
5313
|
+
messageId: messageIdSchema,
|
|
5314
|
+
kind: literal(kind),
|
|
5315
|
+
sentAt: timestampSchema,
|
|
5316
|
+
payload
|
|
5317
|
+
});
|
|
5318
|
+
const realtimeSubscriptionSchema = strictObject({
|
|
5319
|
+
topic: realtimeTopicSchema,
|
|
5320
|
+
afterSeq: number().int().nonnegative().optional()
|
|
5321
|
+
});
|
|
5322
|
+
discriminatedUnion("kind", [
|
|
5323
|
+
envelope("realtime.hello", strictObject({
|
|
5324
|
+
ticket: string().min(32).max(512),
|
|
5325
|
+
clientId: string().min(1).max(160),
|
|
5326
|
+
clientKind: realtimeClientKindSchema,
|
|
5327
|
+
subscriptions: array(realtimeSubscriptionSchema).max(100).default([]),
|
|
5328
|
+
endpoint: endpointHelloPayloadSchema.optional()
|
|
5329
|
+
})),
|
|
5330
|
+
envelope("realtime.subscribe", strictObject({
|
|
5331
|
+
subscriptions: array(realtimeSubscriptionSchema).min(1).max(100)
|
|
5332
|
+
})),
|
|
5333
|
+
envelope("realtime.unsubscribe", strictObject({
|
|
5334
|
+
topics: array(realtimeTopicSchema).min(1).max(100)
|
|
5335
|
+
})),
|
|
5336
|
+
envelope("realtime.ping", strictObject({})),
|
|
5337
|
+
envelope("endpoint.message", clientEndpointMessageSchema)
|
|
5338
|
+
]);
|
|
5339
|
+
const realtimeEventPayloadSchema = strictObject({
|
|
5340
|
+
topic: realtimeTopicSchema,
|
|
5341
|
+
seq: sequenceSchema,
|
|
5342
|
+
event: realtimeEventNameSchema,
|
|
5343
|
+
data: unknown()
|
|
5344
|
+
});
|
|
5345
|
+
const serverRealtimeMessageSchema = discriminatedUnion("kind", [
|
|
5346
|
+
envelope("realtime.ready", strictObject({
|
|
5347
|
+
connectionId: uuid(),
|
|
5348
|
+
heartbeatIntervalMs: literal(REALTIME_HEARTBEAT_INTERVAL_MS),
|
|
5349
|
+
heartbeatTimeoutMs: literal(REALTIME_HEARTBEAT_TIMEOUT_MS),
|
|
5350
|
+
endpoint: strictObject({
|
|
5351
|
+
endpointId: string().min(1).max(160),
|
|
5352
|
+
turnToken: string().min(32).max(160)
|
|
5353
|
+
}).optional()
|
|
5354
|
+
})),
|
|
5355
|
+
envelope("realtime.event", realtimeEventPayloadSchema),
|
|
5356
|
+
envelope("realtime.subscribed", strictObject({
|
|
5357
|
+
topic: realtimeTopicSchema,
|
|
5358
|
+
cursor: number().int().nonnegative()
|
|
5359
|
+
})),
|
|
5360
|
+
envelope("realtime.gap", strictObject({
|
|
5361
|
+
topic: realtimeTopicSchema,
|
|
5362
|
+
requestedSeq: number().int().nonnegative(),
|
|
5363
|
+
earliestSeq: sequenceSchema,
|
|
5364
|
+
recoverable: boolean()
|
|
5365
|
+
})),
|
|
5366
|
+
envelope("realtime.pong", strictObject({})),
|
|
5367
|
+
envelope("realtime.error", strictObject({
|
|
5368
|
+
code: string().min(1).max(80),
|
|
5369
|
+
message: string().min(1).max(500)
|
|
5370
|
+
})),
|
|
5371
|
+
envelope("endpoint.message", serverEndpointMessageSchema)
|
|
5372
|
+
]);
|
|
5373
|
+
function parseServerRealtimeMessage(value) {
|
|
5374
|
+
return serverRealtimeMessageSchema.parse(value);
|
|
5375
|
+
}
|
|
5376
|
+
|
|
5377
|
+
function frameText(data) {
|
|
5378
|
+
if (typeof data === "string") return data;
|
|
5379
|
+
if (data instanceof ArrayBuffer) return new TextDecoder().decode(data);
|
|
5380
|
+
if (ArrayBuffer.isView(data)) {
|
|
5381
|
+
const bytes = Uint8Array.from(new Uint8Array(data.buffer, data.byteOffset, data.byteLength));
|
|
5382
|
+
return new TextDecoder().decode(bytes);
|
|
5383
|
+
}
|
|
5384
|
+
throw new Error("Realtime server sent a non-text frame");
|
|
5385
|
+
}
|
|
5386
|
+
function isNonRetryableCloseCode(code) {
|
|
5387
|
+
return code === 1009 || code === 4400 || code === 4401 || code === 4413;
|
|
5388
|
+
}
|
|
5389
|
+
class RealtimeClient {
|
|
5390
|
+
constructor(options) {
|
|
5391
|
+
this.options = options;
|
|
5392
|
+
}
|
|
5393
|
+
options;
|
|
5394
|
+
desiredSubscriptions = /* @__PURE__ */ new Map();
|
|
5395
|
+
cursors = /* @__PURE__ */ new Map();
|
|
5396
|
+
socket;
|
|
5397
|
+
reconnectTimer;
|
|
5398
|
+
heartbeatTimer;
|
|
5399
|
+
connectionTimer;
|
|
5400
|
+
ticketAbort;
|
|
5401
|
+
generation = 0;
|
|
5402
|
+
reconnectAttempts = 0;
|
|
5403
|
+
shouldReconnect = false;
|
|
5404
|
+
ready = false;
|
|
5405
|
+
lastServerMessageAt = 0;
|
|
5406
|
+
endpointBinding;
|
|
5407
|
+
reconnectWhenSocketOpens = false;
|
|
5408
|
+
connect() {
|
|
5409
|
+
if (this.shouldReconnect) return;
|
|
5410
|
+
this.shouldReconnect = true;
|
|
5411
|
+
void this.open(false);
|
|
5412
|
+
}
|
|
5413
|
+
disconnect() {
|
|
5414
|
+
this.shouldReconnect = false;
|
|
5415
|
+
this.reconnectWhenSocketOpens = false;
|
|
5416
|
+
this.generation += 1;
|
|
5417
|
+
this.clearTimers();
|
|
5418
|
+
this.ready = false;
|
|
5419
|
+
this.endpointBinding?.onDisconnected?.();
|
|
5420
|
+
this.socket?.close(1e3, "Client disconnected");
|
|
5421
|
+
this.socket = void 0;
|
|
5422
|
+
this.options.onStateChange?.("disconnected");
|
|
5423
|
+
}
|
|
5424
|
+
reconnect() {
|
|
5425
|
+
this.reconnectWhenSocketOpens = false;
|
|
5426
|
+
this.generation += 1;
|
|
5427
|
+
this.clearTimers();
|
|
5428
|
+
this.ready = false;
|
|
5429
|
+
this.endpointBinding?.onDisconnected?.();
|
|
5430
|
+
this.socket?.close(1e3, "Client reconnecting");
|
|
5431
|
+
this.socket = void 0;
|
|
5432
|
+
this.reconnectAttempts = 0;
|
|
5433
|
+
this.shouldReconnect = true;
|
|
5434
|
+
void this.open(true);
|
|
5435
|
+
}
|
|
5436
|
+
subscribe(topic, afterSeq) {
|
|
5437
|
+
this.desiredSubscriptions.set(topic, afterSeq);
|
|
5438
|
+
if (afterSeq !== void 0) this.cursors.set(topic, afterSeq);
|
|
5439
|
+
if (this.ready) {
|
|
5440
|
+
this.sendSubscriptions([{ topic, afterSeq: this.cursors.get(topic) ?? afterSeq }]);
|
|
5441
|
+
}
|
|
5442
|
+
}
|
|
5443
|
+
unsubscribe(topic) {
|
|
5444
|
+
this.desiredSubscriptions.delete(topic);
|
|
5445
|
+
if (this.ready) this.send(this.clientMessage("realtime.unsubscribe", { topics: [topic] }));
|
|
5446
|
+
}
|
|
5447
|
+
sendEndpointMessage(message) {
|
|
5448
|
+
if (!this.ready) throw new Error("Realtime connection is not ready");
|
|
5449
|
+
this.send(this.clientMessage("endpoint.message", message));
|
|
5450
|
+
}
|
|
5451
|
+
setEndpoint(binding) {
|
|
5452
|
+
this.endpointBinding = binding;
|
|
5453
|
+
this.refreshEndpointConnection();
|
|
5454
|
+
}
|
|
5455
|
+
clearEndpoint(binding) {
|
|
5456
|
+
if (binding && this.endpointBinding !== binding) return;
|
|
5457
|
+
const previous = this.endpointBinding;
|
|
5458
|
+
this.endpointBinding = void 0;
|
|
5459
|
+
previous?.onDisconnected?.();
|
|
5460
|
+
this.refreshEndpointConnection();
|
|
5461
|
+
}
|
|
5462
|
+
refreshEndpointConnection() {
|
|
5463
|
+
if (!this.shouldReconnect) return;
|
|
5464
|
+
if (this.socket?.readyState === 0) {
|
|
5465
|
+
this.reconnectWhenSocketOpens = true;
|
|
5466
|
+
return;
|
|
5467
|
+
}
|
|
5468
|
+
this.reconnect();
|
|
5469
|
+
}
|
|
5470
|
+
async open(reconnecting) {
|
|
5471
|
+
const generation = ++this.generation;
|
|
5472
|
+
const timeoutMs = this.options.connectionTimeoutMs ?? 15e3;
|
|
5473
|
+
const ticketAbort = new AbortController();
|
|
5474
|
+
this.ticketAbort?.abort();
|
|
5475
|
+
this.ticketAbort = ticketAbort;
|
|
5476
|
+
let socket;
|
|
5477
|
+
let closed = false;
|
|
5478
|
+
let messageQueue;
|
|
5479
|
+
const failAttempt = (error, retry = true) => {
|
|
5480
|
+
if (closed || generation !== this.generation) return;
|
|
5481
|
+
closed = true;
|
|
5482
|
+
this.clearConnectionTimer();
|
|
5483
|
+
ticketAbort.abort();
|
|
5484
|
+
if (this.ticketAbort === ticketAbort) this.ticketAbort = void 0;
|
|
5485
|
+
this.ready = false;
|
|
5486
|
+
this.endpointBinding?.onDisconnected?.();
|
|
5487
|
+
this.clearHeartbeat();
|
|
5488
|
+
if (this.socket === socket) this.socket = void 0;
|
|
5489
|
+
if (socket) {
|
|
5490
|
+
socket.onopen = null;
|
|
5491
|
+
socket.onmessage = null;
|
|
5492
|
+
socket.onerror = null;
|
|
5493
|
+
socket.onclose = null;
|
|
5494
|
+
socket.close(4e3, error.slice(0, 120));
|
|
5495
|
+
}
|
|
5496
|
+
if (this.shouldReconnect && retry) this.scheduleReconnect(error);
|
|
5497
|
+
else if (this.shouldReconnect) {
|
|
5498
|
+
this.shouldReconnect = false;
|
|
5499
|
+
this.options.onStateChange?.("error", error);
|
|
5500
|
+
} else this.options.onStateChange?.("disconnected");
|
|
5501
|
+
};
|
|
5502
|
+
this.connectionTimer = setTimeout(() => failAttempt("Realtime connection timed out"), timeoutMs);
|
|
5503
|
+
this.options.onStateChange?.(reconnecting ? "reconnecting" : "connecting");
|
|
5504
|
+
try {
|
|
5505
|
+
const [ticket, endpoint] = await Promise.all([
|
|
5506
|
+
this.options.issueTicket(ticketAbort.signal),
|
|
5507
|
+
this.endpointBinding?.createHello()
|
|
5508
|
+
]);
|
|
5509
|
+
if (closed || !this.shouldReconnect || generation !== this.generation) return;
|
|
5510
|
+
socket = this.options.createWebSocket(this.options.getWebSocketUrl());
|
|
5511
|
+
this.socket = socket;
|
|
5512
|
+
socket.onopen = () => {
|
|
5513
|
+
if (generation !== this.generation) return;
|
|
5514
|
+
if (this.reconnectWhenSocketOpens) {
|
|
5515
|
+
this.reconnect();
|
|
5516
|
+
return;
|
|
5517
|
+
}
|
|
5518
|
+
const subscriptions = [...this.desiredSubscriptions].map(([topic, afterSeq]) => ({
|
|
5519
|
+
topic,
|
|
5520
|
+
afterSeq: this.cursors.get(topic) ?? afterSeq
|
|
5521
|
+
}));
|
|
5522
|
+
this.send(this.clientMessage("realtime.hello", {
|
|
5523
|
+
ticket,
|
|
5524
|
+
clientId: this.options.clientId,
|
|
5525
|
+
clientKind: this.options.clientKind,
|
|
5526
|
+
subscriptions,
|
|
5527
|
+
...endpoint ? { endpoint } : {}
|
|
5528
|
+
}));
|
|
5529
|
+
};
|
|
5530
|
+
socket.onmessage = (event) => {
|
|
5531
|
+
if (generation !== this.generation) return;
|
|
5532
|
+
const process = () => {
|
|
5533
|
+
if (closed || generation !== this.generation) return;
|
|
5534
|
+
return this.handleServerMessage(frameText(event.data));
|
|
5535
|
+
};
|
|
5536
|
+
if (!messageQueue) {
|
|
5537
|
+
try {
|
|
5538
|
+
const pending = process();
|
|
5539
|
+
if (!pending) return;
|
|
5540
|
+
const current2 = pending.catch((error) => failAttempt(error instanceof Error ? error.message : "Invalid realtime server message")).then(() => void 0);
|
|
5541
|
+
messageQueue = current2;
|
|
5542
|
+
void current2.finally(() => {
|
|
5543
|
+
if (messageQueue === current2) messageQueue = void 0;
|
|
5544
|
+
});
|
|
5545
|
+
} catch (error) {
|
|
5546
|
+
failAttempt(error instanceof Error ? error.message : "Invalid realtime server message");
|
|
5547
|
+
}
|
|
5548
|
+
return;
|
|
5549
|
+
}
|
|
5550
|
+
const current = messageQueue.then(process).catch((error) => failAttempt(error instanceof Error ? error.message : "Invalid realtime server message")).then(() => void 0);
|
|
5551
|
+
messageQueue = current;
|
|
5552
|
+
void current.finally(() => {
|
|
5553
|
+
if (messageQueue === current) messageQueue = void 0;
|
|
5554
|
+
});
|
|
5555
|
+
};
|
|
5556
|
+
socket.onerror = () => {
|
|
5557
|
+
};
|
|
5558
|
+
socket.onclose = (event) => {
|
|
5559
|
+
failAttempt(
|
|
5560
|
+
event.reason || `WebSocket closed (${event.code ?? 0})`,
|
|
5561
|
+
!isNonRetryableCloseCode(event.code)
|
|
5562
|
+
);
|
|
5563
|
+
};
|
|
5564
|
+
} catch (error) {
|
|
5565
|
+
failAttempt(error instanceof Error ? error.message : "Realtime connection failed");
|
|
5566
|
+
}
|
|
5567
|
+
}
|
|
5568
|
+
handleServerMessage(text) {
|
|
5569
|
+
const message = parseServerRealtimeMessage(JSON.parse(text));
|
|
5570
|
+
this.lastServerMessageAt = Date.now();
|
|
5571
|
+
if (message.kind === "realtime.ready") {
|
|
5572
|
+
this.ready = true;
|
|
5573
|
+
this.reconnectAttempts = 0;
|
|
5574
|
+
this.clearConnectionTimer();
|
|
5575
|
+
this.ticketAbort = void 0;
|
|
5576
|
+
this.options.onStateChange?.("connected");
|
|
5577
|
+
if (message.payload.endpoint) this.endpointBinding?.onReady(message.payload.endpoint);
|
|
5578
|
+
this.clearHeartbeat();
|
|
5579
|
+
this.heartbeatTimer = setInterval(() => {
|
|
5580
|
+
if (!this.ready) return;
|
|
5581
|
+
if (Date.now() - this.lastServerMessageAt >= message.payload.heartbeatTimeoutMs) {
|
|
5582
|
+
this.reconnect();
|
|
5583
|
+
return;
|
|
5584
|
+
}
|
|
5585
|
+
this.send(this.clientMessage("realtime.ping", {}));
|
|
5586
|
+
}, message.payload.heartbeatIntervalMs);
|
|
5587
|
+
} else if (message.kind === "realtime.event") {
|
|
5588
|
+
const cursor = this.cursors.get(message.payload.topic);
|
|
5589
|
+
if (cursor !== void 0 && message.payload.seq <= cursor) return;
|
|
5590
|
+
this.options.onEvent?.(message.payload);
|
|
5591
|
+
this.cursors.set(message.payload.topic, message.payload.seq);
|
|
5592
|
+
} else if (message.kind === "realtime.subscribed") {
|
|
5593
|
+
this.cursors.set(message.payload.topic, message.payload.cursor);
|
|
5594
|
+
} else if (message.kind === "realtime.gap") {
|
|
5595
|
+
this.cursors.set(message.payload.topic, message.payload.earliestSeq - 1);
|
|
5596
|
+
return this.options.onGap?.(message.payload);
|
|
5597
|
+
} else if (message.kind === "realtime.error") {
|
|
5598
|
+
this.options.onStateChange?.("error", message.payload.message);
|
|
5599
|
+
} else if (message.kind === "endpoint.message") {
|
|
5600
|
+
this.endpointBinding?.onMessage(message.payload);
|
|
5601
|
+
this.options.onEndpointMessage?.(message.payload);
|
|
5602
|
+
}
|
|
5603
|
+
}
|
|
5604
|
+
sendSubscriptions(subscriptions) {
|
|
5605
|
+
this.send(this.clientMessage("realtime.subscribe", { subscriptions }));
|
|
5606
|
+
}
|
|
5607
|
+
clientMessage(kind, payload) {
|
|
5608
|
+
const messageId = this.options.createMessageId?.() ?? globalThis.crypto?.randomUUID?.();
|
|
5609
|
+
if (!messageId) throw new Error("RealtimeClient requires createMessageId when crypto.randomUUID is unavailable");
|
|
5610
|
+
return {
|
|
5611
|
+
protocolVersion: REALTIME_PROTOCOL_VERSION,
|
|
5612
|
+
messageId,
|
|
5613
|
+
kind,
|
|
5614
|
+
sentAt: Date.now(),
|
|
5615
|
+
payload
|
|
5616
|
+
};
|
|
5617
|
+
}
|
|
5618
|
+
send(message) {
|
|
5619
|
+
if (!this.socket || this.socket.readyState !== 1) return;
|
|
5620
|
+
this.socket.send(JSON.stringify(message));
|
|
5621
|
+
}
|
|
5622
|
+
scheduleReconnect(error) {
|
|
5623
|
+
if (!this.shouldReconnect) return;
|
|
5624
|
+
this.reconnectAttempts += 1;
|
|
5625
|
+
const max = this.options.maxReconnectAttempts;
|
|
5626
|
+
if (max !== void 0 && this.reconnectAttempts > max) {
|
|
5627
|
+
this.shouldReconnect = false;
|
|
5628
|
+
this.options.onStateChange?.("error", error);
|
|
5629
|
+
return;
|
|
5630
|
+
}
|
|
5631
|
+
this.options.onStateChange?.("reconnecting", error);
|
|
5632
|
+
const cap = Math.min(3e4, 1e3 * 2 ** Math.min(this.reconnectAttempts - 1, 5));
|
|
5633
|
+
const delay = Math.round(cap * (0.5 + Math.random() * 0.5));
|
|
5634
|
+
if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
|
|
5635
|
+
this.reconnectTimer = setTimeout(() => {
|
|
5636
|
+
this.reconnectTimer = void 0;
|
|
5637
|
+
void this.open(true);
|
|
5638
|
+
}, delay);
|
|
5639
|
+
}
|
|
5640
|
+
clearHeartbeat() {
|
|
5641
|
+
if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);
|
|
5642
|
+
this.heartbeatTimer = void 0;
|
|
5643
|
+
}
|
|
5644
|
+
clearTimers() {
|
|
5645
|
+
if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
|
|
5646
|
+
this.reconnectTimer = void 0;
|
|
5647
|
+
this.clearConnectionTimer();
|
|
5648
|
+
this.ticketAbort?.abort();
|
|
5649
|
+
this.ticketAbort = void 0;
|
|
5650
|
+
this.clearHeartbeat();
|
|
5651
|
+
}
|
|
5652
|
+
clearConnectionTimer() {
|
|
5653
|
+
if (this.connectionTimer) clearTimeout(this.connectionTimer);
|
|
5654
|
+
this.connectionTimer = void 0;
|
|
5655
|
+
}
|
|
5656
|
+
}
|
|
5657
|
+
|
|
5102
5658
|
const devicePairingKeySchema = strictObject({
|
|
5103
5659
|
kty: literal("EC"),
|
|
5104
5660
|
crv: literal("P-256"),
|
|
@@ -5312,212 +5868,45 @@ ${marker.timeOrigin}`),
|
|
|
5312
5868
|
};
|
|
5313
5869
|
}
|
|
5314
5870
|
|
|
5315
|
-
const ENDPOINT_PROTOCOL_VERSION = 2;
|
|
5316
|
-
const endpointKindSchema = _enum(["web", "desktop", "mobile", "browser"]);
|
|
5317
|
-
const endpointAvailabilitySchema = _enum(["foreground", "background"]);
|
|
5318
|
-
const endpointEffectSchema = _enum(["read", "write", "destructive"]);
|
|
5319
|
-
const endpointConfirmationSchema = _enum(["never", "always"]);
|
|
5320
|
-
const endpointResultKindSchema = _enum(["text", "json", "file"]);
|
|
5321
|
-
const endpointSensitivitySchema = _enum(["public", "personal"]);
|
|
5322
|
-
const identifierSchema = string().min(1).max(160);
|
|
5323
|
-
const turnTokenSchema = string().min(32).max(160).regex(/^[A-Za-z0-9_-]+$/);
|
|
5324
|
-
const turnOriginSchema = discriminatedUnion("type", [
|
|
5325
|
-
strictObject({ type: literal("endpoint"), endpointId: identifierSchema }),
|
|
5326
|
-
strictObject({ type: literal("channel"), channel: string().min(1).max(64) }),
|
|
5327
|
-
strictObject({
|
|
5328
|
-
type: literal("system"),
|
|
5329
|
-
source: _enum(["cli", "automation", "heartbeat", "workflow", "internal"])
|
|
5330
|
-
})
|
|
5331
|
-
]);
|
|
5332
|
-
const endpointTurnOriginSchema = turnOriginSchema.options[0];
|
|
5333
|
-
endpointTurnOriginSchema.extend({ token: turnTokenSchema });
|
|
5334
|
-
const messageIdSchema = uuid();
|
|
5335
|
-
const timestampSchema = number().int().nonnegative();
|
|
5336
|
-
const jsonObjectSchema = record(string(), unknown());
|
|
5337
|
-
const endpointToolDescriptorSchema = strictObject({
|
|
5338
|
-
name: string().regex(/^[a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)+$/).max(120),
|
|
5339
|
-
title: string().min(1).max(120),
|
|
5340
|
-
description: string().min(1).max(2e3),
|
|
5341
|
-
inputSchema: jsonObjectSchema,
|
|
5342
|
-
outputSchema: jsonObjectSchema,
|
|
5343
|
-
policyId: string().regex(/^[a-z][a-z0-9_.-]+$/).max(120),
|
|
5344
|
-
sensitivity: endpointSensitivitySchema,
|
|
5345
|
-
effect: endpointEffectSchema,
|
|
5346
|
-
confirmation: endpointConfirmationSchema,
|
|
5347
|
-
requiresForeground: boolean(),
|
|
5348
|
-
requiredPermissions: array(string().min(1).max(120)).max(20),
|
|
5349
|
-
timeoutMs: number().int().min(1e3).max(3e5),
|
|
5350
|
-
maxConcurrency: number().int().min(1).max(4),
|
|
5351
|
-
supportsCancellation: boolean(),
|
|
5352
|
-
idempotent: boolean(),
|
|
5353
|
-
resultKinds: array(endpointResultKindSchema).min(1).max(3)
|
|
5354
|
-
});
|
|
5355
|
-
strictObject({
|
|
5356
|
-
principalId: identifierSchema,
|
|
5357
|
-
displayName: string().min(1).max(120),
|
|
5358
|
-
kind: endpointKindSchema,
|
|
5359
|
-
platform: string().min(1).max(80),
|
|
5360
|
-
publicKey: string().min(32).max(2048)
|
|
5361
|
-
});
|
|
5362
|
-
strictObject({
|
|
5363
|
-
endpointId: identifierSchema
|
|
5364
|
-
});
|
|
5365
|
-
const backgroundCapabilitySchema = string().regex(/^[a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)+$/).max(120);
|
|
5366
|
-
strictObject({
|
|
5367
|
-
jobId: uuid(),
|
|
5368
|
-
endpointId: identifierSchema,
|
|
5369
|
-
capability: backgroundCapabilitySchema,
|
|
5370
|
-
input: jsonObjectSchema,
|
|
5371
|
-
idempotencyKey: identifierSchema,
|
|
5372
|
-
consentGrantId: identifierSchema,
|
|
5373
|
-
createdAt: timestampSchema,
|
|
5374
|
-
expiresAt: timestampSchema
|
|
5375
|
-
});
|
|
5376
|
-
strictObject({
|
|
5377
|
-
jobId: uuid(),
|
|
5378
|
-
status: _enum(["queued", "running", "succeeded", "failed", "cancelled"]),
|
|
5379
|
-
updatedAt: timestampSchema,
|
|
5380
|
-
output: jsonObjectSchema.optional(),
|
|
5381
|
-
error: strictObject({
|
|
5382
|
-
code: identifierSchema,
|
|
5383
|
-
message: string().min(1).max(2e3)
|
|
5384
|
-
}).optional()
|
|
5385
|
-
});
|
|
5386
|
-
strictObject({
|
|
5387
|
-
eventId: uuid(),
|
|
5388
|
-
endpointId: identifierSchema,
|
|
5389
|
-
subscriptionId: identifierSchema,
|
|
5390
|
-
eventType: backgroundCapabilitySchema,
|
|
5391
|
-
data: jsonObjectSchema,
|
|
5392
|
-
occurredAt: timestampSchema
|
|
5393
|
-
});
|
|
5394
|
-
const endpointHelloPayloadSchema = strictObject({
|
|
5395
|
-
principalId: identifierSchema,
|
|
5396
|
-
endpointId: identifierSchema,
|
|
5397
|
-
connectionInstanceId: uuid(),
|
|
5398
|
-
displayName: string().min(1).max(120),
|
|
5399
|
-
kind: endpointKindSchema,
|
|
5400
|
-
platform: string().min(1).max(80),
|
|
5401
|
-
appVersion: string().min(1).max(80),
|
|
5402
|
-
availability: endpointAvailabilitySchema,
|
|
5403
|
-
nonce: identifierSchema,
|
|
5404
|
-
signedAt: timestampSchema,
|
|
5405
|
-
signature: string().min(16).max(1024),
|
|
5406
|
-
tools: array(endpointToolDescriptorSchema).max(100)
|
|
5407
|
-
});
|
|
5408
|
-
const toolInvokePayloadSchema = strictObject({
|
|
5409
|
-
invocationId: uuid(),
|
|
5410
|
-
toolCallId: identifierSchema,
|
|
5411
|
-
toolName: endpointToolDescriptorSchema.shape.name,
|
|
5412
|
-
arguments: jsonObjectSchema,
|
|
5413
|
-
descriptorRevision: string().min(1).max(128),
|
|
5414
|
-
confirmationRequired: boolean(),
|
|
5415
|
-
deadlineAt: timestampSchema,
|
|
5416
|
-
uploadGrant: strictObject({
|
|
5417
|
-
path: string().startsWith("/").max(500),
|
|
5418
|
-
token: identifierSchema,
|
|
5419
|
-
maxBytes: number().int().positive(),
|
|
5420
|
-
maxFiles: number().int().min(1).max(20),
|
|
5421
|
-
expiresAt: timestampSchema
|
|
5422
|
-
}).optional()
|
|
5423
|
-
});
|
|
5424
|
-
const toolCancelPayloadSchema = strictObject({
|
|
5425
|
-
invocationId: uuid(),
|
|
5426
|
-
reason: string().min(1).max(500)
|
|
5427
|
-
});
|
|
5428
|
-
const invocationPayloadSchema = strictObject({ invocationId: uuid() });
|
|
5429
|
-
const endpointToolContentSchema = discriminatedUnion("type", [
|
|
5430
|
-
strictObject({ type: literal("text"), text: string().max(256 * 1024) }),
|
|
5431
|
-
strictObject({ type: literal("json"), value: unknown() }),
|
|
5432
|
-
strictObject({
|
|
5433
|
-
type: literal("file"),
|
|
5434
|
-
fileId: identifierSchema,
|
|
5435
|
-
name: string().min(1).max(255),
|
|
5436
|
-
mimeType: string().min(1).max(255),
|
|
5437
|
-
size: number().int().nonnegative(),
|
|
5438
|
-
sha256: string().regex(/^[a-f0-9]{64}$/)
|
|
5439
|
-
})
|
|
5440
|
-
]);
|
|
5441
|
-
const endpointToolErrorCodeSchema = _enum([
|
|
5442
|
-
"ENDPOINT_OFFLINE",
|
|
5443
|
-
"ENDPOINT_NOT_FOREGROUND",
|
|
5444
|
-
"ENDPOINT_DISCONNECTED",
|
|
5445
|
-
"TOOL_NOT_FOUND",
|
|
5446
|
-
"TOOL_REVISION_MISMATCH",
|
|
5447
|
-
"INVALID_ARGUMENTS",
|
|
5448
|
-
"PERMISSION_DENIED",
|
|
5449
|
-
"USER_DENIED",
|
|
5450
|
-
"TOOL_BUSY",
|
|
5451
|
-
"TOOL_TIMEOUT",
|
|
5452
|
-
"TOOL_CANCELLED",
|
|
5453
|
-
"RESULT_TOO_LARGE",
|
|
5454
|
-
"PROTOCOL_ERROR"
|
|
5455
|
-
]);
|
|
5456
|
-
const envelope = (type, payload) => strictObject({
|
|
5457
|
-
protocolVersion: literal(ENDPOINT_PROTOCOL_VERSION),
|
|
5458
|
-
messageId: messageIdSchema,
|
|
5459
|
-
type: literal(type),
|
|
5460
|
-
sentAt: timestampSchema,
|
|
5461
|
-
payload
|
|
5462
|
-
});
|
|
5463
|
-
const clientEndpointMessageSchema = discriminatedUnion("type", [
|
|
5464
|
-
envelope("endpoint.availability_changed", strictObject({ availability: endpointAvailabilitySchema })),
|
|
5465
|
-
envelope("tool.received", invocationPayloadSchema),
|
|
5466
|
-
envelope("tool.progress", strictObject({
|
|
5467
|
-
invocationId: uuid(),
|
|
5468
|
-
message: string().max(500).optional(),
|
|
5469
|
-
percent: number().min(0).max(100).optional()
|
|
5470
|
-
})),
|
|
5471
|
-
envelope("tool.result", strictObject({
|
|
5472
|
-
invocationId: uuid(),
|
|
5473
|
-
content: array(endpointToolContentSchema).min(1).max(20),
|
|
5474
|
-
details: jsonObjectSchema.optional()
|
|
5475
|
-
})),
|
|
5476
|
-
envelope("tool.error", strictObject({
|
|
5477
|
-
invocationId: uuid(),
|
|
5478
|
-
code: endpointToolErrorCodeSchema,
|
|
5479
|
-
message: string().min(1).max(2e3)
|
|
5480
|
-
})),
|
|
5481
|
-
envelope("tool.cancelled", invocationPayloadSchema)
|
|
5482
|
-
]);
|
|
5483
|
-
const serverEndpointMessageSchema = discriminatedUnion("type", [
|
|
5484
|
-
envelope("tool.invoke", toolInvokePayloadSchema),
|
|
5485
|
-
envelope("tool.cancel", toolCancelPayloadSchema)
|
|
5486
|
-
]);
|
|
5487
|
-
function canonicalJson(value) {
|
|
5488
|
-
if (value === null) return "null";
|
|
5489
|
-
if (typeof value === "string" || typeof value === "boolean") return JSON.stringify(value);
|
|
5490
|
-
if (typeof value === "number") {
|
|
5491
|
-
if (!Number.isFinite(value)) throw new Error("Canonical JSON only supports finite numbers");
|
|
5492
|
-
return JSON.stringify(value);
|
|
5493
|
-
}
|
|
5494
|
-
if (typeof value !== "object") throw new Error(`Canonical JSON does not support ${typeof value}`);
|
|
5495
|
-
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
|
|
5496
|
-
const prototype = Object.getPrototypeOf(value);
|
|
5497
|
-
if (prototype !== Object.prototype && prototype !== null) {
|
|
5498
|
-
throw new Error("Canonical JSON only supports plain objects");
|
|
5499
|
-
}
|
|
5500
|
-
const record = value;
|
|
5501
|
-
return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(",")}}`;
|
|
5502
|
-
}
|
|
5503
|
-
function endpointHelloSigningPayload(payload) {
|
|
5504
|
-
const { signature: _signature, ...unsigned } = payload;
|
|
5505
|
-
return canonicalJson(unsigned);
|
|
5506
|
-
}
|
|
5507
|
-
|
|
5508
5871
|
const PROFILE_KEY = "xopc.browser.profile";
|
|
5872
|
+
const AUTO_CONNECT_KEY = "xopc.browser.auto-connect";
|
|
5509
5873
|
const KEY_DATABASE = "xopc-browser-identity";
|
|
5510
5874
|
const KEY_STORE = "identity";
|
|
5511
5875
|
const KEY_NAME = "device-key";
|
|
5512
5876
|
const NATIVE_HOST_NAME = "ai.xopc.browser";
|
|
5513
5877
|
let refreshTask;
|
|
5514
|
-
async function
|
|
5878
|
+
async function browserPublicKeyThumbprint(jwk) {
|
|
5879
|
+
const canonical = JSON.stringify({ crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y });
|
|
5880
|
+
return encodeBase64Url(new Uint8Array(await crypto.subtle.digest(
|
|
5881
|
+
"SHA-256",
|
|
5882
|
+
new TextEncoder().encode(canonical)
|
|
5883
|
+
)));
|
|
5884
|
+
}
|
|
5885
|
+
async function isAutoConnectEnabled() {
|
|
5886
|
+
const stored = await chrome.storage.local.get(AUTO_CONNECT_KEY);
|
|
5887
|
+
return stored[AUTO_CONNECT_KEY] !== false;
|
|
5888
|
+
}
|
|
5889
|
+
async function setAutoConnectEnabled(enabled) {
|
|
5890
|
+
await chrome.storage.local.set({ [AUTO_CONNECT_KEY]: enabled });
|
|
5891
|
+
}
|
|
5892
|
+
async function discoverLocalGateway(options) {
|
|
5515
5893
|
try {
|
|
5894
|
+
if (!options?.force && !await isAutoConnectEnabled()) return void 0;
|
|
5895
|
+
const pair = await getOrCreateKeyPair();
|
|
5896
|
+
const key = await publicKeyJwk(pair);
|
|
5897
|
+
const nonce = randomNonce();
|
|
5516
5898
|
const response = await chrome.runtime.sendNativeMessage(
|
|
5517
5899
|
NATIVE_HOST_NAME,
|
|
5518
|
-
{
|
|
5900
|
+
{
|
|
5901
|
+
type: "bootstrap",
|
|
5902
|
+
extensionId: chrome.runtime.id,
|
|
5903
|
+
publicKeyJwk: key,
|
|
5904
|
+
publicKeyThumbprint: await browserPublicKeyThumbprint(key),
|
|
5905
|
+
nonce
|
|
5906
|
+
}
|
|
5519
5907
|
);
|
|
5520
5908
|
if (response.ok !== true || typeof response.gatewayUrl !== "string" || typeof response.pairingLink !== "string" || typeof response.expiresAt !== "number") {
|
|
5909
|
+
if (options?.force) throw new Error(response.error || "The local xopc enrollment host returned an invalid response");
|
|
5521
5910
|
return void 0;
|
|
5522
5911
|
}
|
|
5523
5912
|
return {
|
|
@@ -5525,7 +5914,8 @@ async function discoverLocalGateway() {
|
|
|
5525
5914
|
pairingLink: response.pairingLink,
|
|
5526
5915
|
expiresAt: response.expiresAt
|
|
5527
5916
|
};
|
|
5528
|
-
} catch {
|
|
5917
|
+
} catch (cause) {
|
|
5918
|
+
if (options?.force) throw cause;
|
|
5529
5919
|
return void 0;
|
|
5530
5920
|
}
|
|
5531
5921
|
}
|
|
@@ -5598,6 +5988,14 @@ async function getOrCreateKeyPair() {
|
|
|
5598
5988
|
}).finally(() => database.close());
|
|
5599
5989
|
return pair;
|
|
5600
5990
|
}
|
|
5991
|
+
async function forgetIdentity() {
|
|
5992
|
+
await new Promise((resolve, reject) => {
|
|
5993
|
+
const request = indexedDB.deleteDatabase(KEY_DATABASE);
|
|
5994
|
+
request.onsuccess = () => resolve();
|
|
5995
|
+
request.onerror = () => reject(request.error);
|
|
5996
|
+
request.onblocked = () => reject(new Error("Browser identity is still in use"));
|
|
5997
|
+
});
|
|
5998
|
+
}
|
|
5601
5999
|
async function publicKeyJwk(pair) {
|
|
5602
6000
|
const exported = await crypto.subtle.exportKey("jwk", pair.publicKey);
|
|
5603
6001
|
if (!exported.x || !exported.y) throw new Error("Browser identity key export failed");
|
|
@@ -5718,7 +6116,7 @@ async function pairGateway(link, onApproval) {
|
|
|
5718
6116
|
const requestId = crypto.randomUUID();
|
|
5719
6117
|
const initialRefreshToken = createRefreshToken();
|
|
5720
6118
|
let result = await signedPairingRequest(pair, payload, origin, requestId, "request");
|
|
5721
|
-
onApproval(result.request.confirmationCode);
|
|
6119
|
+
if (result.request.status === "pending") onApproval(result.request.confirmationCode);
|
|
5722
6120
|
while (result.request.status === "pending") {
|
|
5723
6121
|
await wait(1500);
|
|
5724
6122
|
result = await signedPairingRequest(pair, payload, origin, requestId, "status");
|
|
@@ -5801,7 +6199,7 @@ async function registerBrowserEndpoint() {
|
|
|
5801
6199
|
});
|
|
5802
6200
|
if (!response.ok) throw new Error(`Browser endpoint registration failed (${response.status})`);
|
|
5803
6201
|
}
|
|
5804
|
-
async function createBrowserEndpointHello() {
|
|
6202
|
+
async function createBrowserEndpointHello(tools = []) {
|
|
5805
6203
|
const profile = await getAccessProfile();
|
|
5806
6204
|
const pair = await getOrCreateKeyPair();
|
|
5807
6205
|
const unsigned = {
|
|
@@ -5816,7 +6214,7 @@ async function createBrowserEndpointHello() {
|
|
|
5816
6214
|
nonce: crypto.randomUUID(),
|
|
5817
6215
|
signedAt: Date.now(),
|
|
5818
6216
|
signature: "pending",
|
|
5819
|
-
tools
|
|
6217
|
+
tools
|
|
5820
6218
|
};
|
|
5821
6219
|
return {
|
|
5822
6220
|
...unsigned,
|
|
@@ -5835,10 +6233,12 @@ async function revokeAndForgetProfile() {
|
|
|
5835
6233
|
});
|
|
5836
6234
|
} finally {
|
|
5837
6235
|
await forgetProfile();
|
|
6236
|
+
try {
|
|
6237
|
+
await forgetIdentity();
|
|
6238
|
+
} finally {
|
|
6239
|
+
await setAutoConnectEnabled(false);
|
|
6240
|
+
}
|
|
5838
6241
|
}
|
|
5839
6242
|
}
|
|
5840
|
-
async function signBrowserBridgeChallenge(payload) {
|
|
5841
|
-
return sign(await getOrCreateKeyPair(), payload);
|
|
5842
|
-
}
|
|
5843
6243
|
|
|
5844
|
-
export { PENDING_CONTEXT_KEY as P,
|
|
6244
|
+
export { ENDPOINT_PROTOCOL_VERSION as E, PENDING_CONTEXT_KEY as P, RealtimeClient as R, TAB_BINDING_PREFIX as T, activeTabId as a, captureTabPage as b, canonicalJson as c, captureTabWithPermission as d, createBrowserEndpointHello as e, currentTabDescriptor as f, discoverLocalGateway as g, endpointToolContentSchema as h, gatewayFetch as i, getAccessProfile as j, registerBrowserEndpoint as k, revokeAndForgetProfile as l, pairGateway as p, readProfile as r, setAutoConnectEnabled as s };
|