dsh-agora-plugin 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +520 -0
- package/client/index.js +492 -0
- package/cordis.patch.yml +14 -0
- package/dsh.plugin.json +7 -0
- package/lib/agora-client.d.ts +73 -0
- package/lib/agora-client.d.ts.map +1 -0
- package/lib/agora-client.js +207 -0
- package/lib/agora-client.js.map +1 -0
- package/lib/client.js +492 -0
- package/lib/command-adapter.d.ts +44 -0
- package/lib/command-adapter.d.ts.map +1 -0
- package/lib/command-adapter.js +70 -0
- package/lib/command-adapter.js.map +1 -0
- package/lib/command.d.ts +53 -0
- package/lib/command.d.ts.map +1 -0
- package/lib/command.js +375 -0
- package/lib/command.js.map +1 -0
- package/lib/context-types.d.ts +51 -0
- package/lib/context-types.d.ts.map +1 -0
- package/lib/context-types.js +2 -0
- package/lib/context-types.js.map +1 -0
- package/lib/contracts.d.ts +402 -0
- package/lib/contracts.d.ts.map +1 -0
- package/lib/contracts.js +2 -0
- package/lib/contracts.js.map +1 -0
- package/lib/extension-sdk.d.ts +74 -0
- package/lib/extension-sdk.d.ts.map +1 -0
- package/lib/extension-sdk.js +160 -0
- package/lib/extension-sdk.js.map +1 -0
- package/lib/harness-runtime.d.ts +29 -0
- package/lib/harness-runtime.d.ts.map +1 -0
- package/lib/harness-runtime.js +422 -0
- package/lib/harness-runtime.js.map +1 -0
- package/lib/http-api.d.ts +10 -0
- package/lib/http-api.d.ts.map +1 -0
- package/lib/http-api.js +282 -0
- package/lib/http-api.js.map +1 -0
- package/lib/im-bridge-v1.d.ts +37 -0
- package/lib/im-bridge-v1.d.ts.map +1 -0
- package/lib/im-bridge-v1.js +43 -0
- package/lib/im-bridge-v1.js.map +1 -0
- package/lib/im-gateway.d.ts +24 -0
- package/lib/im-gateway.d.ts.map +1 -0
- package/lib/im-gateway.js +53 -0
- package/lib/im-gateway.js.map +1 -0
- package/lib/index.d.ts +46 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +210 -0
- package/lib/index.js.map +1 -0
- package/lib/node-worker.d.ts +52 -0
- package/lib/node-worker.d.ts.map +1 -0
- package/lib/node-worker.js +372 -0
- package/lib/node-worker.js.map +1 -0
- package/lib/service.d.ts +51 -0
- package/lib/service.d.ts.map +1 -0
- package/lib/service.js +196 -0
- package/lib/service.js.map +1 -0
- package/lib/tool.d.ts +72 -0
- package/lib/tool.d.ts.map +1 -0
- package/lib/tool.js +206 -0
- package/lib/tool.js.map +1 -0
- package/package.json +74 -0
- package/patches/dsh-im/@xmanrui__dsh-im@2.1.0.patch +920 -0
- package/patches/dsh-im/@xmanrui__dsh-im@2.3.0.patch +984 -0
- package/patches/dsh-im/README.md +82 -0
- package/src/agora-client.ts +333 -0
- package/src/command-adapter.ts +106 -0
- package/src/command.ts +424 -0
- package/src/context-types.ts +54 -0
- package/src/contracts.ts +413 -0
- package/src/extension-sdk.ts +225 -0
- package/src/harness-runtime.ts +518 -0
- package/src/http-api.ts +269 -0
- package/src/im-bridge-v1.ts +73 -0
- package/src/im-gateway.ts +82 -0
- package/src/index.ts +260 -0
- package/src/node-worker.ts +442 -0
- package/src/service.ts +262 -0
- package/src/tool.ts +269 -0
package/lib/index.js
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { hostname } from 'node:os';
|
|
3
|
+
import { AgoraClient } from './agora-client.js';
|
|
4
|
+
import { registerHttpApi } from './http-api.js';
|
|
5
|
+
import { registerImCommand } from './im-gateway.js';
|
|
6
|
+
import { discoverImBridge } from './im-bridge-v1.js';
|
|
7
|
+
import { DshAgoraService } from './service.js';
|
|
8
|
+
import { createAgoraTool } from './tool.js';
|
|
9
|
+
import { DshAgoraExtensionRegistry, DSH_AGORA_EXTENSION_PROTOCOL } from './extension-sdk.js';
|
|
10
|
+
import { HarnessRuntimeAdapter } from './harness-runtime.js';
|
|
11
|
+
import { RuntimeNodeWorker } from './node-worker.js';
|
|
12
|
+
export { AgoraApiError, AgoraClient } from './agora-client.js';
|
|
13
|
+
export { AgoraCommandParseError, executeAgoraCommand, parseAgoraCommand } from './command.js';
|
|
14
|
+
export * from './contracts.js';
|
|
15
|
+
export { API_PREFIX, handleHttpRequest, registerHttpApi } from './http-api.js';
|
|
16
|
+
export * from './im-gateway.js';
|
|
17
|
+
export * from './im-bridge-v1.js';
|
|
18
|
+
export * from './command-adapter.js';
|
|
19
|
+
export * from './extension-sdk.js';
|
|
20
|
+
export * from './harness-runtime.js';
|
|
21
|
+
export { RuntimeNodeWorker } from './node-worker.js';
|
|
22
|
+
export { DshAgoraService } from './service.js';
|
|
23
|
+
export * from './tool.js';
|
|
24
|
+
export const name = 'dsh-agora';
|
|
25
|
+
// webServer is required for both the local Harness RPC adapter and the runtime
|
|
26
|
+
// node worker. Declaring it here also makes Cordis delay apply() until the web
|
|
27
|
+
// host is initialized instead of silently starting in command-only mode.
|
|
28
|
+
export const inject = ['commands', 'tools', 'webServer'];
|
|
29
|
+
const PLUGIN_VERSION = '0.6.0';
|
|
30
|
+
export function apply(ctx, config = {}) {
|
|
31
|
+
const commandName = normalizeCommandName(config.commandName ?? 'agora');
|
|
32
|
+
const nodeId = normalizeNodeId(config.nodeId ?? process.env.DSH_AGORA_NODE_ID ?? hostname());
|
|
33
|
+
const webServer = safeGet(ctx, 'webServer');
|
|
34
|
+
const imBridgeDiscovery = discoverImBridge(ctx, unique(config.imBridgeServices ?? ['dshImBridge', 'dshImAgoraBridge']));
|
|
35
|
+
let activeImBridge = imBridgeDiscovery.bridge;
|
|
36
|
+
let worker = null;
|
|
37
|
+
const registry = new DshAgoraExtensionRegistry({
|
|
38
|
+
...(config.extensionSecurity?.requireSignedThirdParty === undefined
|
|
39
|
+
? {}
|
|
40
|
+
: { requireSignedThirdParty: config.extensionSecurity.requireSignedThirdParty }),
|
|
41
|
+
...(config.extensionSecurity?.trustedPublicKeys === undefined
|
|
42
|
+
? {}
|
|
43
|
+
: { trustedPublicKeys: config.extensionSecurity.trustedPublicKeys }),
|
|
44
|
+
builtInExtensionIds: ['dsh-runtime'],
|
|
45
|
+
});
|
|
46
|
+
const client = new AgoraClient({
|
|
47
|
+
serverUrl: config.serverUrl ?? process.env.AGORA_SERVER_URL ?? 'http://127.0.0.1:18008',
|
|
48
|
+
apiToken: config.apiToken ?? process.env.AGORA_API_TOKEN,
|
|
49
|
+
timeoutMs: config.requestTimeoutMs,
|
|
50
|
+
});
|
|
51
|
+
const workerClient = new AgoraClient({
|
|
52
|
+
serverUrl: config.serverUrl ?? process.env.AGORA_SERVER_URL ?? 'http://127.0.0.1:18008',
|
|
53
|
+
apiToken: config.nodeApiToken ?? process.env.AGORA_NODE_API_TOKEN ?? config.apiToken ?? process.env.AGORA_API_TOKEN,
|
|
54
|
+
timeoutMs: config.requestTimeoutMs,
|
|
55
|
+
});
|
|
56
|
+
const service = new DshAgoraService({
|
|
57
|
+
client,
|
|
58
|
+
commandName,
|
|
59
|
+
defaultCreator: config.defaultCreator?.trim() || 'dsh',
|
|
60
|
+
registry,
|
|
61
|
+
imBridge: imBridgeDiscovery.bridge,
|
|
62
|
+
nodeId,
|
|
63
|
+
});
|
|
64
|
+
service.setImBridgeStatus(imBridgeDiscovery.status);
|
|
65
|
+
const refreshImBridge = () => {
|
|
66
|
+
const discovery = discoverImBridge(ctx, unique(config.imBridgeServices ?? ['dshImBridge', 'dshImAgoraBridge']));
|
|
67
|
+
// A bridge discovered by the optional Cordis injection below is scoped to
|
|
68
|
+
// its child context and is intentionally not visible from this parent.
|
|
69
|
+
// Do not let the legacy poller overwrite that live injected bridge.
|
|
70
|
+
if (discovery.bridge === null && activeImBridge !== null)
|
|
71
|
+
return;
|
|
72
|
+
activeImBridge = discovery.bridge;
|
|
73
|
+
service.setImBridge(discovery.bridge, discovery.status);
|
|
74
|
+
worker?.setImBridge(discovery.bridge);
|
|
75
|
+
};
|
|
76
|
+
const imBridgeTimer = setInterval(refreshImBridge, 5_000);
|
|
77
|
+
imBridgeTimer.unref?.();
|
|
78
|
+
own(ctx, () => clearInterval(imBridgeTimer), 'dsh-agora: dsh-im bridge discovery');
|
|
79
|
+
// Optional dependency: this child fiber stays pending when dsh-im is absent,
|
|
80
|
+
// while the main dsh-agora plugin remains fully usable in headless mode. When
|
|
81
|
+
// dsh-im appears or reloads, Cordis activates this callback automatically.
|
|
82
|
+
ctx.inject?.(['dshImBridge'], bridgeCtx => {
|
|
83
|
+
const discovery = discoverImBridge(bridgeCtx, ['dshImBridge']);
|
|
84
|
+
if (discovery.bridge === null)
|
|
85
|
+
return;
|
|
86
|
+
activeImBridge = discovery.bridge;
|
|
87
|
+
service.setImBridge(discovery.bridge, discovery.status);
|
|
88
|
+
worker?.setImBridge(discovery.bridge);
|
|
89
|
+
return () => {
|
|
90
|
+
if (activeImBridge !== discovery.bridge)
|
|
91
|
+
return;
|
|
92
|
+
activeImBridge = null;
|
|
93
|
+
worker?.setImBridge(null);
|
|
94
|
+
service.setImBridge(null, {
|
|
95
|
+
state: 'unavailable',
|
|
96
|
+
reason: 'dsh-im.bridge/v1 provider was unloaded',
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
if (isWebServer(webServer) && Number.isInteger(webServer.port)) {
|
|
101
|
+
const runtime = new HarnessRuntimeAdapter({
|
|
102
|
+
baseUrl: `http://127.0.0.1:${webServer.port}`,
|
|
103
|
+
agents: config.runtimeAgents ?? [{ id: 'default', displayName: 'DeepSeek Harness', workspace: process.cwd() }],
|
|
104
|
+
...(config.runtimeReplyTimeoutMs === undefined ? {} : { replyTimeoutMs: config.runtimeReplyTimeoutMs }),
|
|
105
|
+
});
|
|
106
|
+
const unregisterRuntime = service.registerExtension({
|
|
107
|
+
protocol: DSH_AGORA_EXTENSION_PROTOCOL,
|
|
108
|
+
id: 'dsh-runtime',
|
|
109
|
+
kind: 'runtime',
|
|
110
|
+
capabilities: ['runtime.execute', 'session.create', 'session.resume', 'session.prompt', 'session.cancel'],
|
|
111
|
+
runtime,
|
|
112
|
+
});
|
|
113
|
+
own(ctx, unregisterRuntime, 'dsh-agora: built-in DSH runtime adapter');
|
|
114
|
+
}
|
|
115
|
+
const unregisterCommand = ctx.commands.register({
|
|
116
|
+
name: commandName,
|
|
117
|
+
description: 'create and inspect governed Agora tasks',
|
|
118
|
+
input: { hint: '[health|nodes|agents|list|show|status|dispatch-status|create|dashboard|im]' },
|
|
119
|
+
handler: invocation => service.executeCommand(invocation.rawInput, { actorId: invocation.agent.id ?? invocation.agent.session?.id ?? 'dsh' }, invocation.signal),
|
|
120
|
+
});
|
|
121
|
+
// commands.register() is already owned by the current Cordis effect. Keeping
|
|
122
|
+
// the returned capability alive is enough; wrapping it would double-dispose.
|
|
123
|
+
void unregisterCommand;
|
|
124
|
+
// Primary IM-independent path: dsh-im only delivers text into a DSH Session;
|
|
125
|
+
// the Agent uses this normal DSH tool and dsh-im relays the ordinary reply.
|
|
126
|
+
const unregisterTool = ctx.tools.register(createAgoraTool(service));
|
|
127
|
+
void unregisterTool;
|
|
128
|
+
const imRegistration = registerImCommand(ctx, unique(config.imGatewayServices ?? ['dshImCommandGateway', 'dshImGateway']), {
|
|
129
|
+
name: commandName,
|
|
130
|
+
description: 'create and inspect governed Agora tasks',
|
|
131
|
+
execute: invocation => service.executeCommand(invocation.rawInput, invocation, invocation.signal),
|
|
132
|
+
});
|
|
133
|
+
service.setImStatus(imRegistration.status);
|
|
134
|
+
if (imRegistration.dispose !== undefined)
|
|
135
|
+
own(ctx, imRegistration.dispose, 'dsh-agora: dsh-im command gateway');
|
|
136
|
+
if (isWebServer(webServer)) {
|
|
137
|
+
own(ctx, registerHttpApi(webServer, service, {
|
|
138
|
+
accessToken: config.apiAccessToken ?? process.env.DSH_AGORA_API_TOKEN,
|
|
139
|
+
}), 'dsh-agora: host API');
|
|
140
|
+
}
|
|
141
|
+
if (config.nodeEnabled !== false && isWebServer(webServer) && Number.isInteger(webServer.port)) {
|
|
142
|
+
worker = new RuntimeNodeWorker({
|
|
143
|
+
client: workerClient,
|
|
144
|
+
registry,
|
|
145
|
+
nodeId,
|
|
146
|
+
instanceId: randomUUID(),
|
|
147
|
+
pluginVersion: PLUGIN_VERSION,
|
|
148
|
+
...(config.heartbeatIntervalMs === undefined ? {} : { heartbeatIntervalMs: config.heartbeatIntervalMs }),
|
|
149
|
+
...(config.dispatchPollIntervalMs === undefined ? {} : { dispatchPollIntervalMs: config.dispatchPollIntervalMs }),
|
|
150
|
+
...(config.nodeLeaseSeconds === undefined ? {} : { leaseSeconds: config.nodeLeaseSeconds }),
|
|
151
|
+
...(config.dispatchLeaseSeconds === undefined ? {} : { dispatchLeaseSeconds: config.dispatchLeaseSeconds }),
|
|
152
|
+
...(config.dispatchRenewIntervalMs === undefined ? {} : { dispatchRenewIntervalMs: config.dispatchRenewIntervalMs }),
|
|
153
|
+
...(config.deliveryPollIntervalMs === undefined ? {} : { deliveryPollIntervalMs: config.deliveryPollIntervalMs }),
|
|
154
|
+
...(config.deliveryLeaseSeconds === undefined ? {} : { deliveryLeaseSeconds: config.deliveryLeaseSeconds }),
|
|
155
|
+
...(config.maxConcurrent === undefined ? {} : { maxConcurrent: config.maxConcurrent }),
|
|
156
|
+
imBridge: activeImBridge,
|
|
157
|
+
...(config.nodeMetadata === undefined ? {} : { metadata: config.nodeMetadata }),
|
|
158
|
+
onStatus: status => service.setNodeStatus(status),
|
|
159
|
+
});
|
|
160
|
+
worker.start();
|
|
161
|
+
own(ctx, () => worker?.stop(), 'dsh-agora: runtime node worker');
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
service.setNodeStatus({ state: 'disabled', nodeId });
|
|
165
|
+
}
|
|
166
|
+
ctx.accessor?.('dshAgora', { get: () => service });
|
|
167
|
+
ctx.accessor?.('dshAgoraCommandAdapter', { get: () => service.commandAdapter });
|
|
168
|
+
}
|
|
169
|
+
function normalizeNodeId(value) {
|
|
170
|
+
const nodeId = value.trim().toLowerCase().replace(/[^a-z0-9._-]+/gu, '-');
|
|
171
|
+
if (!/^[a-z0-9][a-z0-9._-]{0,127}$/u.test(nodeId))
|
|
172
|
+
throw new TypeError('nodeId is invalid');
|
|
173
|
+
return nodeId;
|
|
174
|
+
}
|
|
175
|
+
function normalizeCommandName(value) {
|
|
176
|
+
const name = value.trim().toLowerCase();
|
|
177
|
+
if (!/^[a-z][a-z0-9-]*$/u.test(name))
|
|
178
|
+
throw new TypeError('commandName must be lowercase letters, digits, or hyphens');
|
|
179
|
+
return name;
|
|
180
|
+
}
|
|
181
|
+
function unique(values) {
|
|
182
|
+
return [...new Set(values.map(value => value.trim()).filter(Boolean))];
|
|
183
|
+
}
|
|
184
|
+
function safeGet(ctx, name) {
|
|
185
|
+
try {
|
|
186
|
+
// Cordis accessors (such as the public dsh-im bridge) are resolved through
|
|
187
|
+
// the context proxy, while ctx.get() only reads provided service values.
|
|
188
|
+
const accessorValue = Reflect.get(ctx, name);
|
|
189
|
+
if (accessorValue !== undefined)
|
|
190
|
+
return accessorValue;
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
// Unknown, non-injected proxy properties throw by design; fall through to
|
|
194
|
+
// the provider lookup for ordinary Cordis services.
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
return ctx.get?.(name);
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
return undefined;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function isWebServer(value) {
|
|
204
|
+
return typeof value === 'object' && value !== null && typeof value.register === 'function';
|
|
205
|
+
}
|
|
206
|
+
function own(ctx, dispose, label) {
|
|
207
|
+
if (ctx.effect !== undefined)
|
|
208
|
+
ctx.effect(() => dispose, label);
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAClC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAE/C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAC3C,OAAO,EAAE,yBAAyB,EAAE,4BAA4B,EAAE,MAAM,oBAAoB,CAAA;AAC5F,OAAO,EAAE,qBAAqB,EAA2B,MAAM,sBAAsB,CAAA;AACrF,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAEpD,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC9D,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAC7F,cAAc,gBAAgB,CAAA;AAC9B,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAC9E,cAAc,iBAAiB,CAAA;AAC/B,cAAc,mBAAmB,CAAA;AACjC,cAAc,sBAAsB,CAAA;AACpC,cAAc,oBAAoB,CAAA;AAClC,cAAc,sBAAsB,CAAA;AACpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAC9C,cAAc,WAAW,CAAA;AAEzB,MAAM,CAAC,MAAM,IAAI,GAAG,WAAW,CAAA;AAC/B,+EAA+E;AAC/E,+EAA+E;AAC/E,yEAAyE;AACzE,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,UAAU,EAAE,OAAO,EAAE,WAAW,CAAC,CAAA;AACxD,MAAM,cAAc,GAAG,OAAO,CAAA;AA+B9B,MAAM,UAAU,KAAK,CAAC,GAAoB,EAAE,SAAiB,EAAE;IAC7D,MAAM,WAAW,GAAG,oBAAoB,CAAC,MAAM,CAAC,WAAW,IAAI,OAAO,CAAC,CAAA;IACvE,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,QAAQ,EAAE,CAAC,CAAA;IAC5F,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,EAAE,WAAW,CAAC,CAAA;IAC3C,MAAM,iBAAiB,GAAG,gBAAgB,CACxC,GAAG,EACH,MAAM,CAAC,MAAM,CAAC,gBAAgB,IAAI,CAAC,aAAa,EAAE,kBAAkB,CAAC,CAAC,CACvE,CAAA;IACD,IAAI,cAAc,GAAG,iBAAiB,CAAC,MAAM,CAAA;IAC7C,IAAI,MAAM,GAA6B,IAAI,CAAA;IAC3C,MAAM,QAAQ,GAAG,IAAI,yBAAyB,CAAC;QAC7C,GAAG,CAAC,MAAM,CAAC,iBAAiB,EAAE,uBAAuB,KAAK,SAAS;YACjE,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,uBAAuB,EAAE,MAAM,CAAC,iBAAiB,CAAC,uBAAuB,EAAE,CAAC;QAClF,GAAG,CAAC,MAAM,CAAC,iBAAiB,EAAE,iBAAiB,KAAK,SAAS;YAC3D,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,iBAAiB,EAAE,MAAM,CAAC,iBAAiB,CAAC,iBAAiB,EAAE,CAAC;QACtE,mBAAmB,EAAE,CAAC,aAAa,CAAC;KACrC,CAAC,CAAA;IACF,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC;QAC7B,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,wBAAwB;QACvF,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe;QACxD,SAAS,EAAE,MAAM,CAAC,gBAAgB;KACnC,CAAC,CAAA;IACF,MAAM,YAAY,GAAG,IAAI,WAAW,CAAC;QACnC,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,wBAAwB;QACvF,QAAQ,EAAE,MAAM,CAAC,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,MAAM,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe;QACnH,SAAS,EAAE,MAAM,CAAC,gBAAgB;KACnC,CAAC,CAAA;IACF,MAAM,OAAO,GAAG,IAAI,eAAe,CAAC;QAClC,MAAM;QACN,WAAW;QACX,cAAc,EAAE,MAAM,CAAC,cAAc,EAAE,IAAI,EAAE,IAAI,KAAK;QACtD,QAAQ;QACR,QAAQ,EAAE,iBAAiB,CAAC,MAAM;QAClC,MAAM;KACP,CAAC,CAAA;IACF,OAAO,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAA;IAEnD,MAAM,eAAe,GAAG,GAAS,EAAE;QACjC,MAAM,SAAS,GAAG,gBAAgB,CAChC,GAAG,EACH,MAAM,CAAC,MAAM,CAAC,gBAAgB,IAAI,CAAC,aAAa,EAAE,kBAAkB,CAAC,CAAC,CACvE,CAAA;QACD,0EAA0E;QAC1E,uEAAuE;QACvE,oEAAoE;QACpE,IAAI,SAAS,CAAC,MAAM,KAAK,IAAI,IAAI,cAAc,KAAK,IAAI;YAAE,OAAM;QAChE,cAAc,GAAG,SAAS,CAAC,MAAM,CAAA;QACjC,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,CAAA;QACvD,MAAM,EAAE,WAAW,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;IACvC,CAAC,CAAA;IACD,MAAM,aAAa,GAAG,WAAW,CAAC,eAAe,EAAE,KAAK,CAAC,CAAA;IACzD,aAAa,CAAC,KAAK,EAAE,EAAE,CAAA;IACvB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,aAAa,CAAC,EAAE,oCAAoC,CAAC,CAAA;IAElF,6EAA6E;IAC7E,8EAA8E;IAC9E,2EAA2E;IAC3E,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,aAAa,CAAC,EAAE,SAAS,CAAC,EAAE;QACxC,MAAM,SAAS,GAAG,gBAAgB,CAAC,SAAS,EAAE,CAAC,aAAa,CAAC,CAAC,CAAA;QAC9D,IAAI,SAAS,CAAC,MAAM,KAAK,IAAI;YAAE,OAAM;QACrC,cAAc,GAAG,SAAS,CAAC,MAAM,CAAA;QACjC,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,CAAA;QACvD,MAAM,EAAE,WAAW,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;QACrC,OAAO,GAAG,EAAE;YACV,IAAI,cAAc,KAAK,SAAS,CAAC,MAAM;gBAAE,OAAM;YAC/C,cAAc,GAAG,IAAI,CAAA;YACrB,MAAM,EAAE,WAAW,CAAC,IAAI,CAAC,CAAA;YACzB,OAAO,CAAC,WAAW,CAAC,IAAI,EAAE;gBACxB,KAAK,EAAE,aAAa;gBACpB,MAAM,EAAE,wCAAwC;aACjD,CAAC,CAAA;QACJ,CAAC,CAAA;IACH,CAAC,CAAC,CAAA;IAEF,IAAI,WAAW,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/D,MAAM,OAAO,GAAG,IAAI,qBAAqB,CAAC;YACxC,OAAO,EAAE,oBAAoB,SAAS,CAAC,IAAI,EAAE;YAC7C,MAAM,EAAE,MAAM,CAAC,aAAa,IAAI,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,WAAW,EAAE,kBAAkB,EAAE,SAAS,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC;YAC9G,GAAG,CAAC,MAAM,CAAC,qBAAqB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,MAAM,CAAC,qBAAqB,EAAE,CAAC;SACxG,CAAC,CAAA;QACF,MAAM,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;YAClD,QAAQ,EAAE,4BAA4B;YACtC,EAAE,EAAE,aAAa;YACjB,IAAI,EAAE,SAAS;YACf,YAAY,EAAE,CAAC,iBAAiB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,gBAAgB,CAAC;YACzG,OAAO;SACR,CAAC,CAAA;QACF,GAAG,CAAC,GAAG,EAAE,iBAAiB,EAAE,yCAAyC,CAAC,CAAA;IACxE,CAAC;IAED,MAAM,iBAAiB,GAAG,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAC9C,IAAI,EAAE,WAAW;QACjB,WAAW,EAAE,yCAAyC;QACtD,KAAK,EAAE,EAAE,IAAI,EAAE,4EAA4E,EAAE;QAC7F,OAAO,EAAE,UAAU,CAAC,EAAE,CAAC,OAAO,CAAC,cAAc,CAC3C,UAAU,CAAC,QAAQ,EACnB,EAAE,OAAO,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,IAAI,UAAU,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,KAAK,EAAE,EACzE,UAAU,CAAC,MAAM,CAClB;KACF,CAAC,CAAA;IACF,6EAA6E;IAC7E,6EAA6E;IAC7E,KAAK,iBAAiB,CAAA;IAEtB,6EAA6E;IAC7E,4EAA4E;IAC5E,MAAM,cAAc,GAAG,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAA;IACnE,KAAK,cAAc,CAAA;IAEnB,MAAM,cAAc,GAAG,iBAAiB,CACtC,GAAG,EACH,MAAM,CAAC,MAAM,CAAC,iBAAiB,IAAI,CAAC,qBAAqB,EAAE,cAAc,CAAC,CAAC,EAC3E;QACE,IAAI,EAAE,WAAW;QACjB,WAAW,EAAE,yCAAyC;QACtD,OAAO,EAAE,UAAU,CAAC,EAAE,CAAC,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,QAAQ,EAAE,UAAU,EAAE,UAAU,CAAC,MAAM,CAAC;KAClG,CACF,CAAA;IACD,OAAO,CAAC,WAAW,CAAC,cAAc,CAAC,MAAM,CAAC,CAAA;IAC1C,IAAI,cAAc,CAAC,OAAO,KAAK,SAAS;QAAE,GAAG,CAAC,GAAG,EAAE,cAAc,CAAC,OAAO,EAAE,mCAAmC,CAAC,CAAA;IAE/G,IAAI,WAAW,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3B,GAAG,CAAC,GAAG,EAAE,eAAe,CAAC,SAAS,EAAE,OAAO,EAAE;YAC3C,WAAW,EAAE,MAAM,CAAC,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC,mBAAmB;SACtE,CAAC,EAAE,qBAAqB,CAAC,CAAA;IAC5B,CAAC;IAED,IAAI,MAAM,CAAC,WAAW,KAAK,KAAK,IAAI,WAAW,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/F,MAAM,GAAG,IAAI,iBAAiB,CAAC;YAC7B,MAAM,EAAE,YAAY;YACpB,QAAQ;YACR,MAAM;YACN,UAAU,EAAE,UAAU,EAAE;YACxB,aAAa,EAAE,cAAc;YAC7B,GAAG,CAAC,MAAM,CAAC,mBAAmB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,mBAAmB,EAAE,MAAM,CAAC,mBAAmB,EAAE,CAAC;YACxG,GAAG,CAAC,MAAM,CAAC,sBAAsB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,sBAAsB,EAAE,MAAM,CAAC,sBAAsB,EAAE,CAAC;YACjH,GAAG,CAAC,MAAM,CAAC,gBAAgB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,MAAM,CAAC,gBAAgB,EAAE,CAAC;YAC3F,GAAG,CAAC,MAAM,CAAC,oBAAoB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,oBAAoB,EAAE,MAAM,CAAC,oBAAoB,EAAE,CAAC;YAC3G,GAAG,CAAC,MAAM,CAAC,uBAAuB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,uBAAuB,EAAE,MAAM,CAAC,uBAAuB,EAAE,CAAC;YACpH,GAAG,CAAC,MAAM,CAAC,sBAAsB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,sBAAsB,EAAE,MAAM,CAAC,sBAAsB,EAAE,CAAC;YACjH,GAAG,CAAC,MAAM,CAAC,oBAAoB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,oBAAoB,EAAE,MAAM,CAAC,oBAAoB,EAAE,CAAC;YAC3G,GAAG,CAAC,MAAM,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,MAAM,CAAC,aAAa,EAAE,CAAC;YACtF,QAAQ,EAAE,cAAc;YACxB,GAAG,CAAC,MAAM,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC;YAC/E,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC;SAClD,CAAC,CAAA;QACF,MAAM,CAAC,KAAK,EAAE,CAAA;QACd,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,gCAAgC,CAAC,CAAA;IAClE,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,aAAa,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,CAAA;IACtD,CAAC;IAED,GAAG,CAAC,QAAQ,EAAE,CAAC,UAAU,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAA;IAClD,GAAG,CAAC,QAAQ,EAAE,CAAC,wBAAwB,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,CAAA;AACjF,CAAC;AAED,SAAS,eAAe,CAAC,KAAa;IACpC,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAA;IACzE,IAAI,CAAC,+BAA+B,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,mBAAmB,CAAC,CAAA;IAC3F,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAa;IACzC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;IACvC,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,2DAA2D,CAAC,CAAA;IACtH,OAAO,IAAI,CAAA;AACb,CAAC;AAED,SAAS,MAAM,CAAC,MAAyB;IACvC,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;AACxE,CAAC;AAED,SAAS,OAAO,CAAC,GAAoB,EAAE,IAAY;IACjD,IAAI,CAAC;QACH,2EAA2E;QAC3E,yEAAyE;QACzE,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,GAAa,EAAE,IAAI,CAAC,CAAA;QACtD,IAAI,aAAa,KAAK,SAAS;YAAE,OAAO,aAAa,CAAA;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,0EAA0E;QAC1E,oDAAoD;IACtD,CAAC;IACD,IAAI,CAAC;QACH,OAAO,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAA;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,KAAc;IACjC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,OAAQ,KAA+B,CAAC,QAAQ,KAAK,UAAU,CAAA;AACvH,CAAC;AAED,SAAS,GAAG,CAAC,GAAoB,EAAE,OAAmB,EAAE,KAAa;IACnE,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS;QAAE,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;AAChE,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type AgoraClient } from './agora-client.js';
|
|
2
|
+
import { type DshAgoraNodeStatus } from './contracts.js';
|
|
3
|
+
import type { DshAgoraExtensionRegistry } from './extension-sdk.js';
|
|
4
|
+
import type { DshImBridgeV1 } from './im-bridge-v1.js';
|
|
5
|
+
export interface RuntimeNodeWorkerOptions {
|
|
6
|
+
readonly client: AgoraClient;
|
|
7
|
+
readonly registry: DshAgoraExtensionRegistry;
|
|
8
|
+
readonly nodeId: string;
|
|
9
|
+
readonly instanceId: string;
|
|
10
|
+
readonly pluginVersion: string;
|
|
11
|
+
readonly heartbeatIntervalMs?: number;
|
|
12
|
+
readonly dispatchPollIntervalMs?: number;
|
|
13
|
+
readonly leaseSeconds?: number;
|
|
14
|
+
readonly dispatchLeaseSeconds?: number;
|
|
15
|
+
readonly dispatchRenewIntervalMs?: number;
|
|
16
|
+
readonly deliveryPollIntervalMs?: number;
|
|
17
|
+
readonly deliveryLeaseSeconds?: number;
|
|
18
|
+
readonly maxConcurrent?: number;
|
|
19
|
+
readonly imBridge?: DshImBridgeV1 | null;
|
|
20
|
+
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
21
|
+
onStatus?(status: DshAgoraNodeStatus): void;
|
|
22
|
+
}
|
|
23
|
+
export declare class RuntimeNodeWorker {
|
|
24
|
+
private readonly options;
|
|
25
|
+
private readonly abortController;
|
|
26
|
+
private heartbeatTimer;
|
|
27
|
+
private dispatchTimer;
|
|
28
|
+
private deliveryTimer;
|
|
29
|
+
private active;
|
|
30
|
+
private started;
|
|
31
|
+
private imBridge;
|
|
32
|
+
constructor(options: RuntimeNodeWorkerOptions);
|
|
33
|
+
setImBridge(bridge: DshImBridgeV1 | null): void;
|
|
34
|
+
start(): void;
|
|
35
|
+
stop(): void;
|
|
36
|
+
private heartbeatLoop;
|
|
37
|
+
private dispatchLoop;
|
|
38
|
+
private execute;
|
|
39
|
+
private deliveryLoop;
|
|
40
|
+
private renewLease;
|
|
41
|
+
private fail;
|
|
42
|
+
private presentationPayload;
|
|
43
|
+
private deliver;
|
|
44
|
+
private listBots;
|
|
45
|
+
private setStatus;
|
|
46
|
+
private get leaseSeconds();
|
|
47
|
+
private get maxConcurrent();
|
|
48
|
+
private get dispatchLeaseSeconds();
|
|
49
|
+
private get dispatchRenewIntervalMs();
|
|
50
|
+
private get deliveryLeaseSeconds();
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=node-worker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-worker.d.ts","sourceRoot":"","sources":["../src/node-worker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACnE,OAAO,EAEL,KAAK,kBAAkB,EAMxB,MAAM,gBAAgB,CAAA;AACvB,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAA;AACnE,OAAO,KAAK,EAAE,aAAa,EAAsB,MAAM,mBAAmB,CAAA;AAE1E,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;IAC5B,QAAQ,CAAC,QAAQ,EAAE,yBAAyB,CAAA;IAC5C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAA;IACrC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,MAAM,CAAA;IACxC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAA;IACtC,QAAQ,CAAC,uBAAuB,CAAC,EAAE,MAAM,CAAA;IACzC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,MAAM,CAAA;IACxC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAA;IACtC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,GAAG,IAAI,CAAA;IACxC,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IACrD,QAAQ,CAAC,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAAA;CAC5C;AAED,qBAAa,iBAAiB;IAShB,OAAO,CAAC,QAAQ,CAAC,OAAO;IARpC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAwB;IACxD,OAAO,CAAC,cAAc,CAA6C;IACnE,OAAO,CAAC,aAAa,CAA6C;IAClE,OAAO,CAAC,aAAa,CAA6C;IAClE,OAAO,CAAC,MAAM,CAAI;IAClB,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,QAAQ,CAAsB;gBAET,OAAO,EAAE,wBAAwB;IAI9D,WAAW,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,GAAG,IAAI;IAI/C,KAAK,IAAI,IAAI;IASb,IAAI,IAAI,IAAI;YAYE,aAAa;YAwCb,YAAY;YA0BZ,OAAO;YAgGP,YAAY;YAwBZ,UAAU;YAgBV,IAAI;IAclB,OAAO,CAAC,mBAAmB;YAcb,OAAO;YAgEP,QAAQ;IAItB,OAAO,CAAC,SAAS;IAIjB,OAAO,KAAK,YAAY,GAEvB;IAED,OAAO,KAAK,aAAa,GAExB;IAED,OAAO,KAAK,oBAAoB,GAE/B;IAED,OAAO,KAAK,uBAAuB,GAElC;IAED,OAAO,KAAK,oBAAoB,GAE/B;CACF"}
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import { AgoraApiError } from './agora-client.js';
|
|
2
|
+
import { DSH_AGORA_NODE_PROTOCOL, } from './contracts.js';
|
|
3
|
+
export class RuntimeNodeWorker {
|
|
4
|
+
options;
|
|
5
|
+
abortController = new AbortController();
|
|
6
|
+
heartbeatTimer = null;
|
|
7
|
+
dispatchTimer = null;
|
|
8
|
+
deliveryTimer = null;
|
|
9
|
+
active = 0;
|
|
10
|
+
started = false;
|
|
11
|
+
imBridge;
|
|
12
|
+
constructor(options) {
|
|
13
|
+
this.options = options;
|
|
14
|
+
this.imBridge = options.imBridge ?? null;
|
|
15
|
+
}
|
|
16
|
+
setImBridge(bridge) {
|
|
17
|
+
this.imBridge = bridge;
|
|
18
|
+
}
|
|
19
|
+
start() {
|
|
20
|
+
if (this.started)
|
|
21
|
+
return;
|
|
22
|
+
this.started = true;
|
|
23
|
+
this.setStatus({ state: 'connecting', nodeId: this.options.nodeId });
|
|
24
|
+
void this.heartbeatLoop();
|
|
25
|
+
void this.dispatchLoop();
|
|
26
|
+
void this.deliveryLoop();
|
|
27
|
+
}
|
|
28
|
+
stop() {
|
|
29
|
+
if (!this.started)
|
|
30
|
+
return;
|
|
31
|
+
this.started = false;
|
|
32
|
+
this.abortController.abort(new DOMException('dsh-agora node worker stopped', 'AbortError'));
|
|
33
|
+
if (this.heartbeatTimer !== null)
|
|
34
|
+
clearTimeout(this.heartbeatTimer);
|
|
35
|
+
if (this.dispatchTimer !== null)
|
|
36
|
+
clearTimeout(this.dispatchTimer);
|
|
37
|
+
if (this.deliveryTimer !== null)
|
|
38
|
+
clearTimeout(this.deliveryTimer);
|
|
39
|
+
this.heartbeatTimer = null;
|
|
40
|
+
this.dispatchTimer = null;
|
|
41
|
+
this.deliveryTimer = null;
|
|
42
|
+
}
|
|
43
|
+
async heartbeatLoop() {
|
|
44
|
+
if (this.abortController.signal.aborted)
|
|
45
|
+
return;
|
|
46
|
+
try {
|
|
47
|
+
const agents = (await Promise.all(this.options.registry.listExtensions()
|
|
48
|
+
.filter(extension => extension.runtime !== undefined)
|
|
49
|
+
.map(extension => extension.runtime.describeAgents()))).flat();
|
|
50
|
+
if (agents.length === 0)
|
|
51
|
+
throw new Error('no runtime agents are registered');
|
|
52
|
+
const bots = await this.listBots();
|
|
53
|
+
const now = new Date().toISOString();
|
|
54
|
+
await this.options.client.heartbeatRuntimeNode(this.options.nodeId, {
|
|
55
|
+
protocol: DSH_AGORA_NODE_PROTOCOL,
|
|
56
|
+
instance_id: this.options.instanceId,
|
|
57
|
+
plugin_version: this.options.pluginVersion,
|
|
58
|
+
host_framework: 'deepseek-harness',
|
|
59
|
+
runtime_provider: 'dsh',
|
|
60
|
+
agents,
|
|
61
|
+
bots,
|
|
62
|
+
capacity: { max_concurrent: this.maxConcurrent, active: this.active },
|
|
63
|
+
lease_seconds: this.leaseSeconds,
|
|
64
|
+
metadata: this.options.metadata ?? null,
|
|
65
|
+
}, this.abortController.signal);
|
|
66
|
+
this.setStatus({ state: 'online', nodeId: this.options.nodeId, lastHeartbeatAt: now });
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
if (!this.abortController.signal.aborted) {
|
|
70
|
+
this.setStatus({
|
|
71
|
+
state: 'error', nodeId: this.options.nodeId,
|
|
72
|
+
error: error instanceof Error ? error.message : String(error),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
this.heartbeatTimer = schedule(() => void this.heartbeatLoop(), this.options.heartbeatIntervalMs ?? 30_000, this.abortController.signal);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async dispatchLoop() {
|
|
81
|
+
if (this.abortController.signal.aborted)
|
|
82
|
+
return;
|
|
83
|
+
try {
|
|
84
|
+
while (this.active < this.maxConcurrent && !this.abortController.signal.aborted) {
|
|
85
|
+
const dispatch = await this.options.client.claimRuntimeDispatch(this.options.nodeId, this.options.instanceId, this.dispatchLeaseSeconds, this.abortController.signal);
|
|
86
|
+
if (!dispatch)
|
|
87
|
+
break;
|
|
88
|
+
this.active += 1;
|
|
89
|
+
void this.execute(dispatch).finally(() => { this.active -= 1; });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// Heartbeat owns the operator-visible status. Dispatch polling is retried
|
|
94
|
+
// independently so a transient queue error does not flap node presence.
|
|
95
|
+
}
|
|
96
|
+
finally {
|
|
97
|
+
this.dispatchTimer = schedule(() => void this.dispatchLoop(), this.options.dispatchPollIntervalMs ?? 2_000, this.abortController.signal);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async execute(dispatch) {
|
|
101
|
+
const runtime = this.options.registry.runtimeForTarget(dispatch.runtime_target_ref);
|
|
102
|
+
if (!runtime) {
|
|
103
|
+
await this.fail(dispatch, `no runtime adapter accepts ${dispatch.runtime_target_ref}`);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (!dispatch.claim_token) {
|
|
107
|
+
await this.fail(dispatch, 'claimed dispatch has no fencing token');
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const renewalAbort = new AbortController();
|
|
111
|
+
const leaseLostAbort = new AbortController();
|
|
112
|
+
const executionSignal = AbortSignal.any([
|
|
113
|
+
this.abortController.signal,
|
|
114
|
+
leaseLostAbort.signal,
|
|
115
|
+
]);
|
|
116
|
+
const renewal = this.renewLease(dispatch, renewalAbort.signal).catch(error => {
|
|
117
|
+
if (!renewalAbort.signal.aborted && !this.abortController.signal.aborted) {
|
|
118
|
+
leaseLostAbort.abort(error);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
let progressSequence = 0;
|
|
122
|
+
let progressSupported = true;
|
|
123
|
+
const reportProgress = async (event) => {
|
|
124
|
+
if (!progressSupported || !dispatch.claim_token)
|
|
125
|
+
return;
|
|
126
|
+
progressSequence += 1;
|
|
127
|
+
try {
|
|
128
|
+
await this.options.client.recordRuntimeDispatchProgress(this.options.nodeId, dispatch.id, {
|
|
129
|
+
instance_id: this.options.instanceId,
|
|
130
|
+
claim_token: dispatch.claim_token,
|
|
131
|
+
sequence: progressSequence,
|
|
132
|
+
...event,
|
|
133
|
+
}, executionSignal);
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
if (error instanceof AgoraApiError && (error.status === 404 || error.status === 405)) {
|
|
137
|
+
progressSupported = false;
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (error instanceof AgoraApiError && error.status === 409)
|
|
141
|
+
throw error;
|
|
142
|
+
// Progress telemetry must not make durable work fail during a transient
|
|
143
|
+
// network outage; the independently renewed lease remains authoritative.
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
try {
|
|
147
|
+
await reportProgress({ phase: 'claimed', message: 'Dispatch claimed by runtime node', percent: 0 });
|
|
148
|
+
const result = await runtime.execute(dispatch, executionSignal, { reportProgress });
|
|
149
|
+
await reportProgress({ phase: 'finalizing', message: 'Persisting the agent result', percent: 95 });
|
|
150
|
+
renewalAbort.abort();
|
|
151
|
+
await renewal;
|
|
152
|
+
if (leaseLostAbort.signal.aborted)
|
|
153
|
+
return;
|
|
154
|
+
const deliveryPayload = this.presentationPayload(dispatch, result.answer);
|
|
155
|
+
await this.options.client.completeRuntimeDispatch(this.options.nodeId, dispatch.id, {
|
|
156
|
+
instance_id: this.options.instanceId,
|
|
157
|
+
claim_token: dispatch.claim_token,
|
|
158
|
+
status: 'completed',
|
|
159
|
+
session_id: result.sessionId,
|
|
160
|
+
result: {
|
|
161
|
+
answer: result.answer,
|
|
162
|
+
reason: result.reason ?? null,
|
|
163
|
+
...(result.metadata ?? {}),
|
|
164
|
+
},
|
|
165
|
+
result_envelope: result.resultEnvelope ?? defaultResultEnvelope(dispatch, result.answer),
|
|
166
|
+
...(deliveryPayload === null ? {} : { delivery_payload: deliveryPayload }),
|
|
167
|
+
}, this.abortController.signal);
|
|
168
|
+
if (dispatch.task_id && dispatch.participant_binding_id) {
|
|
169
|
+
try {
|
|
170
|
+
await this.options.client.bindRuntimeSession(dispatch.task_id, dispatch.participant_binding_id, result.sessionId, dispatch.runtime_target_ref, this.abortController.signal);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
// The durable result is authoritative. Session binding is repaired by
|
|
174
|
+
// the normal reconciliation path instead of rolling back completion.
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
renewalAbort.abort();
|
|
180
|
+
await renewal;
|
|
181
|
+
if (this.abortController.signal.aborted || leaseLostAbort.signal.aborted)
|
|
182
|
+
return;
|
|
183
|
+
await this.fail(dispatch, error instanceof Error ? error.message : String(error));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
async deliveryLoop() {
|
|
187
|
+
if (this.abortController.signal.aborted)
|
|
188
|
+
return;
|
|
189
|
+
try {
|
|
190
|
+
while (!this.abortController.signal.aborted) {
|
|
191
|
+
const delivery = await this.options.client.claimRuntimeDelivery(this.options.nodeId, this.options.instanceId, this.deliveryLeaseSeconds, this.abortController.signal);
|
|
192
|
+
if (!delivery)
|
|
193
|
+
break;
|
|
194
|
+
await this.deliver(delivery);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
// Durable delivery remains pending or is released when its lease expires.
|
|
199
|
+
}
|
|
200
|
+
finally {
|
|
201
|
+
this.deliveryTimer = schedule(() => void this.deliveryLoop(), this.options.deliveryPollIntervalMs ?? 2_000, this.abortController.signal);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
async renewLease(dispatch, signal) {
|
|
205
|
+
const claimToken = dispatch.claim_token;
|
|
206
|
+
if (!claimToken)
|
|
207
|
+
throw new Error(`runtime dispatch ${dispatch.id} has no fencing token`);
|
|
208
|
+
while (!signal.aborted && !this.abortController.signal.aborted) {
|
|
209
|
+
await sleep(this.dispatchRenewIntervalMs, AbortSignal.any([signal, this.abortController.signal]));
|
|
210
|
+
await this.options.client.renewRuntimeDispatch(this.options.nodeId, dispatch.id, this.options.instanceId, claimToken, this.dispatchLeaseSeconds, AbortSignal.any([signal, this.abortController.signal]));
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
async fail(dispatch, error) {
|
|
214
|
+
try {
|
|
215
|
+
if (!dispatch.claim_token)
|
|
216
|
+
return;
|
|
217
|
+
await this.options.client.completeRuntimeDispatch(this.options.nodeId, dispatch.id, { instance_id: this.options.instanceId, claim_token: dispatch.claim_token, status: 'failed', error }, this.abortController.signal);
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
// The claim lease will expire and make the durable dispatch retryable.
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
presentationPayload(dispatch, answer) {
|
|
224
|
+
if (!answer.trim())
|
|
225
|
+
return null;
|
|
226
|
+
const metadata = asRecord(dispatch.metadata);
|
|
227
|
+
const target = asRecord(metadata?.presentation_target);
|
|
228
|
+
if (!target || target.mode !== 'destination_bot')
|
|
229
|
+
return null;
|
|
230
|
+
if (!stringValue(target.provider) || !stringValue(target.conversation_ref))
|
|
231
|
+
return null;
|
|
232
|
+
return {
|
|
233
|
+
protocol: 'dsh-agora.presentation/v1',
|
|
234
|
+
runtime_target_ref: dispatch.runtime_target_ref,
|
|
235
|
+
text: answer,
|
|
236
|
+
target,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
async deliver(delivery) {
|
|
240
|
+
const claimToken = delivery.claim_token;
|
|
241
|
+
if (!claimToken)
|
|
242
|
+
return;
|
|
243
|
+
try {
|
|
244
|
+
const payload = asRecord(delivery.payload);
|
|
245
|
+
if (payload?.protocol !== 'dsh-agora.presentation/v1') {
|
|
246
|
+
throw new Error('unsupported runtime delivery payload');
|
|
247
|
+
}
|
|
248
|
+
const target = asRecord(payload.target);
|
|
249
|
+
const text = stringValue(payload.text);
|
|
250
|
+
const runtimeTargetRef = stringValue(payload.runtime_target_ref);
|
|
251
|
+
if (!target || !text || !runtimeTargetRef)
|
|
252
|
+
throw new Error('invalid presentation delivery payload');
|
|
253
|
+
const bridge = this.imBridge;
|
|
254
|
+
if (!bridge)
|
|
255
|
+
throw new Error('dsh-im bridge is unavailable');
|
|
256
|
+
const provider = stringValue(target.provider);
|
|
257
|
+
const conversationRef = stringValue(target.conversation_ref);
|
|
258
|
+
if (!provider || !conversationRef)
|
|
259
|
+
throw new Error('presentation target is incomplete');
|
|
260
|
+
const bots = await bridge.listBots();
|
|
261
|
+
const agentRef = runtimeTargetRef.split(':').at(-1);
|
|
262
|
+
const bot = bots.find(item => item.connected && item.provider === provider && item.agent_ref === agentRef)
|
|
263
|
+
?? bots.find(item => item.connected && item.provider === provider);
|
|
264
|
+
if (!bot)
|
|
265
|
+
throw new Error(`no connected ${provider} bot on destination node`);
|
|
266
|
+
const request = {
|
|
267
|
+
provider,
|
|
268
|
+
bot_ref: bot.bot_ref,
|
|
269
|
+
conversation_ref: conversationRef,
|
|
270
|
+
thread_ref: stringValue(target.thread_ref),
|
|
271
|
+
reply_to_message_ref: stringValue(target.reply_to_message_ref),
|
|
272
|
+
text,
|
|
273
|
+
idempotency_key: `agora:${delivery.dispatch_id}:result`,
|
|
274
|
+
};
|
|
275
|
+
const receipt = await bridge.send(request);
|
|
276
|
+
await this.options.client.completeRuntimeDelivery(this.options.nodeId, delivery.id, {
|
|
277
|
+
instance_id: this.options.instanceId,
|
|
278
|
+
claim_token: claimToken,
|
|
279
|
+
status: 'delivered',
|
|
280
|
+
receipt: { provider_message_refs: receipt.provider_message_refs },
|
|
281
|
+
}, this.abortController.signal);
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
if (this.abortController.signal.aborted)
|
|
285
|
+
return;
|
|
286
|
+
try {
|
|
287
|
+
await this.options.client.completeRuntimeDelivery(this.options.nodeId, delivery.id, {
|
|
288
|
+
instance_id: this.options.instanceId,
|
|
289
|
+
claim_token: claimToken,
|
|
290
|
+
status: 'retry',
|
|
291
|
+
error: error instanceof Error ? error.message : String(error),
|
|
292
|
+
retry_delay_seconds: deliveryRetryDelaySeconds(delivery.attempt),
|
|
293
|
+
}, this.abortController.signal);
|
|
294
|
+
}
|
|
295
|
+
catch {
|
|
296
|
+
// The delivery lease expires and the same idempotency key is retried.
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
async listBots() {
|
|
301
|
+
try {
|
|
302
|
+
return await this.imBridge?.listBots() ?? [];
|
|
303
|
+
}
|
|
304
|
+
catch {
|
|
305
|
+
return [];
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
setStatus(status) {
|
|
309
|
+
this.options.onStatus?.(status);
|
|
310
|
+
}
|
|
311
|
+
get leaseSeconds() {
|
|
312
|
+
return this.options.leaseSeconds ?? 90;
|
|
313
|
+
}
|
|
314
|
+
get maxConcurrent() {
|
|
315
|
+
return this.options.maxConcurrent ?? 1;
|
|
316
|
+
}
|
|
317
|
+
get dispatchLeaseSeconds() {
|
|
318
|
+
return this.options.dispatchLeaseSeconds ?? 120;
|
|
319
|
+
}
|
|
320
|
+
get dispatchRenewIntervalMs() {
|
|
321
|
+
return this.options.dispatchRenewIntervalMs ?? Math.max(1_000, Math.floor(this.dispatchLeaseSeconds * 1_000 / 3));
|
|
322
|
+
}
|
|
323
|
+
get deliveryLeaseSeconds() {
|
|
324
|
+
return this.options.deliveryLeaseSeconds ?? 60;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
function schedule(callback, delay, signal) {
|
|
328
|
+
if (signal.aborted)
|
|
329
|
+
return null;
|
|
330
|
+
const timer = setTimeout(callback, delay);
|
|
331
|
+
timer.unref?.();
|
|
332
|
+
return timer;
|
|
333
|
+
}
|
|
334
|
+
function sleep(ms, signal) {
|
|
335
|
+
return new Promise((resolve, reject) => {
|
|
336
|
+
if (signal.aborted) {
|
|
337
|
+
reject(signal.reason);
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
const timer = setTimeout(resolve, ms);
|
|
341
|
+
timer.unref?.();
|
|
342
|
+
signal.addEventListener('abort', () => {
|
|
343
|
+
clearTimeout(timer);
|
|
344
|
+
reject(signal.reason);
|
|
345
|
+
}, { once: true });
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
function asRecord(value) {
|
|
349
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
350
|
+
? value
|
|
351
|
+
: null;
|
|
352
|
+
}
|
|
353
|
+
function stringValue(value) {
|
|
354
|
+
return typeof value === 'string' && value.trim() ? value.trim() : null;
|
|
355
|
+
}
|
|
356
|
+
function deliveryRetryDelaySeconds(attempt) {
|
|
357
|
+
return Math.min(300, Math.max(5, 2 ** Math.min(Math.max(attempt, 1), 8)));
|
|
358
|
+
}
|
|
359
|
+
function defaultResultEnvelope(dispatch, answer) {
|
|
360
|
+
return {
|
|
361
|
+
schema: 'agora.runtime-result/v1',
|
|
362
|
+
answer,
|
|
363
|
+
claims: [],
|
|
364
|
+
evidence: [],
|
|
365
|
+
environment: {
|
|
366
|
+
runtime_provider: 'dsh',
|
|
367
|
+
agent_ref: dispatch.runtime_target_ref.split(':').at(-1) ?? null,
|
|
368
|
+
workspace_alias: dispatch.workspace_alias ?? null,
|
|
369
|
+
},
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
//# sourceMappingURL=node-worker.js.map
|