ds-harness-remote 0.3.36 → 0.4.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 +26 -24
- package/dist/client-runtime.d.ts +9 -4
- package/dist/client-runtime.d.ts.map +1 -1
- package/dist/client-runtime.js +130 -23
- package/dist/client-runtime.js.map +1 -1
- package/dist/harness-api-bridge.d.ts +2 -9
- package/dist/harness-api-bridge.d.ts.map +1 -1
- package/dist/harness-api-bridge.js.map +1 -1
- package/dist/harness-remote-bridge.d.ts +46 -0
- package/dist/harness-remote-bridge.d.ts.map +1 -0
- package/dist/harness-remote-bridge.js +321 -0
- package/dist/harness-remote-bridge.js.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +847 -68
- package/dist/index.js.map +4 -4
- package/dist/remote-typert-gateway.d.ts +13 -0
- package/dist/remote-typert-gateway.d.ts.map +1 -0
- package/dist/remote-typert-gateway.js +242 -0
- package/dist/remote-typert-gateway.js.map +1 -0
- package/dist/rpc-router.d.ts +7 -2
- package/dist/rpc-router.d.ts.map +1 -1
- package/dist/rpc-router.js +61 -12
- package/dist/rpc-router.js.map +1 -1
- package/dist/service.d.ts +5 -3
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +26 -8
- package/dist/service.js.map +1 -1
- package/dist/typert-gateway-contract.d.ts +48 -0
- package/dist/typert-gateway-contract.d.ts.map +1 -0
- package/dist/typert-gateway-contract.js +2 -0
- package/dist/typert-gateway-contract.js.map +1 -0
- package/dist/typert-gateway-switch.d.ts +28 -7
- package/dist/typert-gateway-switch.d.ts.map +1 -1
- package/dist/typert-gateway-switch.js +114 -23
- package/dist/typert-gateway-switch.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +13 -8
package/dist/index.js
CHANGED
|
@@ -4082,6 +4082,7 @@ var controlFrameTypes = [
|
|
|
4082
4082
|
"error"
|
|
4083
4083
|
];
|
|
4084
4084
|
var rpcMethods = [
|
|
4085
|
+
"harness.transport.describe",
|
|
4085
4086
|
"harness.api.call",
|
|
4086
4087
|
"harness.api.transfer.open",
|
|
4087
4088
|
"harness.api.transfer.chunk",
|
|
@@ -4091,6 +4092,14 @@ var rpcMethods = [
|
|
|
4091
4092
|
"harness.api.respond",
|
|
4092
4093
|
"harness.api.stream.open",
|
|
4093
4094
|
"harness.api.stream.close",
|
|
4095
|
+
"harness.remote.call",
|
|
4096
|
+
"harness.remote.transfer.open",
|
|
4097
|
+
"harness.remote.transfer.chunk",
|
|
4098
|
+
"harness.remote.transfer.commit",
|
|
4099
|
+
"harness.remote.transfer.read",
|
|
4100
|
+
"harness.remote.transfer.close",
|
|
4101
|
+
"harness.remote.stream.open",
|
|
4102
|
+
"harness.remote.stream.close",
|
|
4094
4103
|
"fileviewer.call"
|
|
4095
4104
|
];
|
|
4096
4105
|
function normalizeSdpMLineIndex(value) {
|
|
@@ -13460,6 +13469,224 @@ function base64ToBytes(value) {
|
|
|
13460
13469
|
return bytes;
|
|
13461
13470
|
}
|
|
13462
13471
|
|
|
13472
|
+
// src/remote-typert-gateway.ts
|
|
13473
|
+
var DIRECT_REMOTE_CALL_BYTES = 2 * 1024 * 1024;
|
|
13474
|
+
var RemoteTypertGateway = class {
|
|
13475
|
+
constructor(client) {
|
|
13476
|
+
this.client = client;
|
|
13477
|
+
}
|
|
13478
|
+
async invoke(request) {
|
|
13479
|
+
const result = await this.dispatch(
|
|
13480
|
+
`${request.namespace}/${request.method}`,
|
|
13481
|
+
{ args: request.args },
|
|
13482
|
+
request.signal ?? new AbortController().signal
|
|
13483
|
+
);
|
|
13484
|
+
if (result.ok) return result.value;
|
|
13485
|
+
throw remoteFailure(result.error);
|
|
13486
|
+
}
|
|
13487
|
+
async dispatch(endpoint, payload, signal) {
|
|
13488
|
+
const request = { endpoint, payload };
|
|
13489
|
+
const encoded = new TextEncoder().encode(JSON.stringify(request));
|
|
13490
|
+
let response;
|
|
13491
|
+
if (encoded.byteLength > DIRECT_REMOTE_CALL_BYTES) {
|
|
13492
|
+
response = await this.callTransferred(encoded, signal);
|
|
13493
|
+
} else {
|
|
13494
|
+
try {
|
|
13495
|
+
response = await this.client.rpc("harness.remote.call", request, signal);
|
|
13496
|
+
} catch (error) {
|
|
13497
|
+
if (!hasErrorCode(error, "RESPONSE_TOO_LARGE")) throw error;
|
|
13498
|
+
response = await this.callTransferred(encoded, signal);
|
|
13499
|
+
}
|
|
13500
|
+
}
|
|
13501
|
+
return parseRpcResult(response);
|
|
13502
|
+
}
|
|
13503
|
+
async open(endpoint, payload, signal) {
|
|
13504
|
+
const streamId = uuidV7();
|
|
13505
|
+
const queue = new AsyncValueQueue();
|
|
13506
|
+
const unsubscribe = this.client.onEvent((event) => routeStreamEvent2(event, streamId, queue));
|
|
13507
|
+
const unsubscribeClose = this.client.onClose(() => {
|
|
13508
|
+
queue.fail(new RemoteClientError("TRANSPORT_CLOSED", "The remote Harness stream transport closed."));
|
|
13509
|
+
});
|
|
13510
|
+
const onAbort = () => queue.close();
|
|
13511
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
13512
|
+
try {
|
|
13513
|
+
await this.client.rpc("harness.remote.stream.open", { streamId, endpoint, payload }, signal);
|
|
13514
|
+
} catch (error) {
|
|
13515
|
+
signal.removeEventListener("abort", onAbort);
|
|
13516
|
+
unsubscribe();
|
|
13517
|
+
unsubscribeClose();
|
|
13518
|
+
throw error;
|
|
13519
|
+
}
|
|
13520
|
+
return this.iterate(streamId, queue, unsubscribe, unsubscribeClose, signal, onAbort);
|
|
13521
|
+
}
|
|
13522
|
+
async *iterate(streamId, queue, unsubscribe, unsubscribeClose, signal, onAbort) {
|
|
13523
|
+
try {
|
|
13524
|
+
yield* queue;
|
|
13525
|
+
} finally {
|
|
13526
|
+
signal.removeEventListener("abort", onAbort);
|
|
13527
|
+
unsubscribe();
|
|
13528
|
+
unsubscribeClose();
|
|
13529
|
+
queue.close();
|
|
13530
|
+
await this.client.rpc("harness.remote.stream.close", { streamId }).catch(() => void 0);
|
|
13531
|
+
}
|
|
13532
|
+
}
|
|
13533
|
+
async callTransferred(encoded, signal) {
|
|
13534
|
+
if (encoded.byteLength > MAX_HARNESS_API_TRANSFER_BYTES) {
|
|
13535
|
+
throw new Error("The Harness Remote request exceeds the transfer limit.");
|
|
13536
|
+
}
|
|
13537
|
+
const transferId = uuidV7();
|
|
13538
|
+
const totalChunks = Math.ceil(encoded.byteLength / HARNESS_API_TRANSFER_CHUNK_BYTES);
|
|
13539
|
+
let opened = false;
|
|
13540
|
+
try {
|
|
13541
|
+
await this.client.rpc("harness.remote.transfer.open", {
|
|
13542
|
+
transferId,
|
|
13543
|
+
totalBytes: encoded.byteLength,
|
|
13544
|
+
totalChunks
|
|
13545
|
+
}, signal);
|
|
13546
|
+
opened = true;
|
|
13547
|
+
for (let index = 0; index < totalChunks; index += 1) {
|
|
13548
|
+
const start = index * HARNESS_API_TRANSFER_CHUNK_BYTES;
|
|
13549
|
+
const chunk = encoded.subarray(start, Math.min(start + HARNESS_API_TRANSFER_CHUNK_BYTES, encoded.byteLength));
|
|
13550
|
+
await this.client.rpc("harness.remote.transfer.chunk", {
|
|
13551
|
+
transferId,
|
|
13552
|
+
index,
|
|
13553
|
+
data: bytesToBase642(chunk)
|
|
13554
|
+
}, signal);
|
|
13555
|
+
}
|
|
13556
|
+
const committed = await this.client.rpc(
|
|
13557
|
+
"harness.remote.transfer.commit",
|
|
13558
|
+
{ transferId },
|
|
13559
|
+
signal
|
|
13560
|
+
);
|
|
13561
|
+
if (committed.kind === "inline") return committed.response;
|
|
13562
|
+
if (committed.transferId !== transferId || committed.totalBytes <= 0 || committed.totalBytes > MAX_HARNESS_API_TRANSFER_BYTES || committed.totalChunks !== Math.ceil(committed.totalBytes / HARNESS_API_TRANSFER_CHUNK_BYTES)) {
|
|
13563
|
+
throw new Error("The remote Host returned an invalid Harness Remote transfer descriptor.");
|
|
13564
|
+
}
|
|
13565
|
+
const responseBytes = new Uint8Array(committed.totalBytes);
|
|
13566
|
+
let offset = 0;
|
|
13567
|
+
for (let index = 0; index < committed.totalChunks; index += 1) {
|
|
13568
|
+
const result = await this.client.rpc(
|
|
13569
|
+
"harness.remote.transfer.read",
|
|
13570
|
+
{ transferId, index },
|
|
13571
|
+
signal
|
|
13572
|
+
);
|
|
13573
|
+
if (result.transferId !== transferId || result.index !== index) {
|
|
13574
|
+
throw new Error("The remote Host returned an out-of-order Harness Remote transfer chunk.");
|
|
13575
|
+
}
|
|
13576
|
+
const chunk = base64ToBytes2(result.data);
|
|
13577
|
+
const expectedBytes = Math.min(HARNESS_API_TRANSFER_CHUNK_BYTES, committed.totalBytes - offset);
|
|
13578
|
+
if (chunk.byteLength !== expectedBytes) {
|
|
13579
|
+
throw new Error("The remote Host returned an invalid Harness Remote transfer chunk.");
|
|
13580
|
+
}
|
|
13581
|
+
responseBytes.set(chunk, offset);
|
|
13582
|
+
offset += chunk.byteLength;
|
|
13583
|
+
}
|
|
13584
|
+
return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(responseBytes));
|
|
13585
|
+
} finally {
|
|
13586
|
+
if (opened) await this.client.rpc("harness.remote.transfer.close", { transferId }).catch(() => void 0);
|
|
13587
|
+
}
|
|
13588
|
+
}
|
|
13589
|
+
};
|
|
13590
|
+
var AsyncValueQueue = class {
|
|
13591
|
+
values = [];
|
|
13592
|
+
waiters = [];
|
|
13593
|
+
closed = false;
|
|
13594
|
+
error;
|
|
13595
|
+
push(value) {
|
|
13596
|
+
if (this.closed) return;
|
|
13597
|
+
const waiter = this.waiters.shift();
|
|
13598
|
+
if (waiter === void 0) this.values.push(value);
|
|
13599
|
+
else waiter({ done: false, value });
|
|
13600
|
+
}
|
|
13601
|
+
close() {
|
|
13602
|
+
if (this.closed) return;
|
|
13603
|
+
this.closed = true;
|
|
13604
|
+
for (const waiter of this.waiters.splice(0)) waiter({ done: true, value: void 0 });
|
|
13605
|
+
}
|
|
13606
|
+
fail(error) {
|
|
13607
|
+
if (this.closed) return;
|
|
13608
|
+
this.error = error;
|
|
13609
|
+
this.close();
|
|
13610
|
+
}
|
|
13611
|
+
async *[Symbol.asyncIterator]() {
|
|
13612
|
+
while (true) {
|
|
13613
|
+
if (this.values.length > 0) {
|
|
13614
|
+
yield this.values.shift();
|
|
13615
|
+
continue;
|
|
13616
|
+
}
|
|
13617
|
+
if (this.closed) {
|
|
13618
|
+
if (this.error !== void 0) throw this.error;
|
|
13619
|
+
return;
|
|
13620
|
+
}
|
|
13621
|
+
const next = await new Promise((resolve2) => this.waiters.push(resolve2));
|
|
13622
|
+
if (next.done) {
|
|
13623
|
+
if (this.error !== void 0) throw this.error;
|
|
13624
|
+
return;
|
|
13625
|
+
}
|
|
13626
|
+
yield next.value;
|
|
13627
|
+
}
|
|
13628
|
+
}
|
|
13629
|
+
};
|
|
13630
|
+
function routeStreamEvent2(event, streamId, queue) {
|
|
13631
|
+
if (event.event === "harness.remote.frame") {
|
|
13632
|
+
const data2 = event.data;
|
|
13633
|
+
if (data2.streamId === streamId && data2.hasValue === true) queue.push(data2.value);
|
|
13634
|
+
return;
|
|
13635
|
+
}
|
|
13636
|
+
if (event.event !== "harness.remote.stream.closed") return;
|
|
13637
|
+
const data = event.data;
|
|
13638
|
+
if (data.streamId !== streamId) return;
|
|
13639
|
+
if (data.reason === "failed") {
|
|
13640
|
+
const failure = data.failure;
|
|
13641
|
+
queue.fail(remoteFailure({
|
|
13642
|
+
code: typeof failure?.code === "string" ? failure.code : "internal",
|
|
13643
|
+
message: typeof failure?.message === "string" ? failure.message : "The remote Harness stream failed.",
|
|
13644
|
+
details: isRecord(failure?.details) ? failure.details : {}
|
|
13645
|
+
}));
|
|
13646
|
+
} else {
|
|
13647
|
+
queue.close();
|
|
13648
|
+
}
|
|
13649
|
+
}
|
|
13650
|
+
function parseRpcResult(value) {
|
|
13651
|
+
if (!isRecord(value)) throw new Error("The remote Host returned an invalid Gateway result.");
|
|
13652
|
+
if (value.ok === true) return Object.hasOwn(value, "value") ? { ok: true, value: value.value } : { ok: true };
|
|
13653
|
+
if (value.ok !== false || !isRecord(value.error) || typeof value.error.code !== "string" || typeof value.error.message !== "string" || !isRecord(value.error.details)) {
|
|
13654
|
+
throw new Error("The remote Host returned an invalid Gateway failure.");
|
|
13655
|
+
}
|
|
13656
|
+
return {
|
|
13657
|
+
ok: false,
|
|
13658
|
+
error: { code: value.error.code, message: value.error.message, details: value.error.details }
|
|
13659
|
+
};
|
|
13660
|
+
}
|
|
13661
|
+
function remoteFailure(failure) {
|
|
13662
|
+
return Object.assign(new Error(failure.message), { code: failure.code, details: failure.details });
|
|
13663
|
+
}
|
|
13664
|
+
function bytesToBase642(bytes) {
|
|
13665
|
+
let binary = "";
|
|
13666
|
+
for (let offset = 0; offset < bytes.byteLength; offset += 32768) {
|
|
13667
|
+
binary += String.fromCharCode(...bytes.subarray(offset, Math.min(offset + 32768, bytes.byteLength)));
|
|
13668
|
+
}
|
|
13669
|
+
return btoa(binary);
|
|
13670
|
+
}
|
|
13671
|
+
function base64ToBytes2(value) {
|
|
13672
|
+
let binary;
|
|
13673
|
+
try {
|
|
13674
|
+
binary = atob(value);
|
|
13675
|
+
} catch {
|
|
13676
|
+
throw new Error("The remote Host returned malformed Harness Remote transfer data.");
|
|
13677
|
+
}
|
|
13678
|
+
const bytes = new Uint8Array(binary.length);
|
|
13679
|
+
for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index);
|
|
13680
|
+
if (bytesToBase642(bytes) !== value) throw new Error("The remote Host returned non-canonical Harness Remote transfer data.");
|
|
13681
|
+
return bytes;
|
|
13682
|
+
}
|
|
13683
|
+
function isRecord(value) {
|
|
13684
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13685
|
+
}
|
|
13686
|
+
function hasErrorCode(error, code) {
|
|
13687
|
+
return error instanceof Error && "code" in error && error.code === code;
|
|
13688
|
+
}
|
|
13689
|
+
|
|
13463
13690
|
// src/server-api.ts
|
|
13464
13691
|
import { platform } from "node:os";
|
|
13465
13692
|
|
|
@@ -13543,7 +13770,7 @@ function normalizeServerUrl(value) {
|
|
|
13543
13770
|
}
|
|
13544
13771
|
|
|
13545
13772
|
// src/version.ts
|
|
13546
|
-
var PLUGIN_VERSION = "0.
|
|
13773
|
+
var PLUGIN_VERSION = "0.4.0";
|
|
13547
13774
|
|
|
13548
13775
|
// src/server-api.ts
|
|
13549
13776
|
var HostServerApi = class {
|
|
@@ -13944,51 +14171,133 @@ function requireRecord(value, name2) {
|
|
|
13944
14171
|
var REMOTE_COMMAND_METHODS = ["execute", "list"];
|
|
13945
14172
|
var ALL_REMOTE_COMMANDS = { execute: true, list: true };
|
|
13946
14173
|
var TypertGatewaySwitch = class {
|
|
13947
|
-
|
|
13948
|
-
this.gateway = gateway;
|
|
13949
|
-
this.originalInvoke = gateway.invoke;
|
|
13950
|
-
this.localInvoke = this.originalInvoke.bind(gateway);
|
|
13951
|
-
}
|
|
14174
|
+
runtime;
|
|
13952
14175
|
originalInvoke;
|
|
13953
14176
|
localInvoke;
|
|
14177
|
+
originalStream;
|
|
14178
|
+
localStream;
|
|
14179
|
+
originalDispatch;
|
|
14180
|
+
localDispatch;
|
|
14181
|
+
originalOpen;
|
|
14182
|
+
localOpen;
|
|
13954
14183
|
remoteInvoke;
|
|
14184
|
+
remoteTarget;
|
|
13955
14185
|
remoteSupport = { execute: false, list: false };
|
|
14186
|
+
target;
|
|
13956
14187
|
installed = false;
|
|
13957
|
-
|
|
14188
|
+
constructor(gateway) {
|
|
14189
|
+
this.runtime = gateway;
|
|
14190
|
+
this.originalInvoke = gateway.invoke;
|
|
14191
|
+
this.localInvoke = this.originalInvoke.bind(gateway);
|
|
14192
|
+
this.originalStream = gateway.stream;
|
|
14193
|
+
this.localStream = gateway.stream?.bind(gateway);
|
|
14194
|
+
this.originalDispatch = this.runtime.dispatchRpc;
|
|
14195
|
+
this.localDispatch = this.runtime.dispatchRpc?.bind(gateway);
|
|
14196
|
+
this.originalOpen = this.runtime.openWireStream;
|
|
14197
|
+
this.localOpen = this.runtime.openWireStream?.bind(gateway) ?? gateway.wireStream?.open.bind(gateway.wireStream);
|
|
14198
|
+
}
|
|
14199
|
+
/** Original local dispatcher, used by the Host bridge without switch recursion. */
|
|
13958
14200
|
local() {
|
|
13959
|
-
|
|
14201
|
+
const dispatch = this.localDispatch ?? (async (endpoint, payload, signal) => {
|
|
14202
|
+
try {
|
|
14203
|
+
const request = requestFromCarrier(endpoint, payload, signal);
|
|
14204
|
+
return { ok: true, value: await this.localInvoke(request) };
|
|
14205
|
+
} catch (error) {
|
|
14206
|
+
return { ok: false, error: this.failure(error) };
|
|
14207
|
+
}
|
|
14208
|
+
});
|
|
14209
|
+
const open = this.localOpen ?? (async (endpoint, payload, signal) => {
|
|
14210
|
+
if (this.localStream === void 0) throw new Error("The local Harness Gateway does not support Remote streams.");
|
|
14211
|
+
return this.localStream(requestFromCarrier(endpoint, payload, signal));
|
|
14212
|
+
});
|
|
14213
|
+
return {
|
|
14214
|
+
invoke: this.localInvoke,
|
|
14215
|
+
...this.localStream === void 0 ? {} : { stream: this.localStream },
|
|
14216
|
+
dispatch,
|
|
14217
|
+
open,
|
|
14218
|
+
failure: (error) => this.failure(error),
|
|
14219
|
+
supportsCarrier: this.localDispatch !== void 0 && this.localOpen !== void 0
|
|
14220
|
+
};
|
|
14221
|
+
}
|
|
14222
|
+
supportsCarrier() {
|
|
14223
|
+
return this.localDispatch !== void 0 && this.localOpen !== void 0;
|
|
14224
|
+
}
|
|
14225
|
+
status() {
|
|
14226
|
+
return this.remoteInvoke === void 0 ? { mode: "local" } : { mode: "remote", ...this.target === void 0 ? {} : { target: { ...this.target } } };
|
|
13960
14227
|
}
|
|
13961
14228
|
install() {
|
|
13962
14229
|
if (this.installed) return;
|
|
13963
|
-
this.
|
|
13964
|
-
|
|
13965
|
-
|
|
13966
|
-
|
|
13967
|
-
|
|
13968
|
-
|
|
13969
|
-
|
|
13970
|
-
|
|
14230
|
+
this.runtime.invoke = (request) => this.selectInvoke(request);
|
|
14231
|
+
if (this.originalStream !== void 0) {
|
|
14232
|
+
this.runtime.stream = (request) => this.remoteTarget === void 0 ? this.localStream(request) : this.remoteTarget.open(endpointOf(request), { args: request.args }, request.signal ?? new AbortController().signal);
|
|
14233
|
+
}
|
|
14234
|
+
if (this.originalDispatch !== void 0) {
|
|
14235
|
+
this.runtime.dispatchRpc = (endpoint, payload, signal) => this.remoteTarget === void 0 ? this.localDispatch(endpoint, payload, signal) : this.remoteTarget.dispatch(endpoint, payload, signal);
|
|
14236
|
+
}
|
|
14237
|
+
if (this.originalOpen !== void 0) {
|
|
14238
|
+
this.runtime.openWireStream = (endpoint, payload, signal) => this.remoteTarget === void 0 ? this.localOpen(endpoint, payload, signal) : this.remoteTarget.open(endpoint, payload, signal);
|
|
14239
|
+
}
|
|
13971
14240
|
this.installed = true;
|
|
13972
14241
|
}
|
|
13973
|
-
selectRemote(
|
|
14242
|
+
selectRemote(remote, support = ALL_REMOTE_COMMANDS, target) {
|
|
13974
14243
|
if (!this.installed) throw new Error("The Typert gateway switch is not installed.");
|
|
13975
|
-
this.remoteInvoke = invoke;
|
|
14244
|
+
this.remoteInvoke = typeof remote === "function" ? remote : (request) => remote.invoke(request);
|
|
14245
|
+
this.remoteTarget = typeof remote === "function" ? void 0 : remote;
|
|
13976
14246
|
this.remoteSupport = { ...support };
|
|
14247
|
+
this.target = target === void 0 ? void 0 : { ...target };
|
|
13977
14248
|
}
|
|
13978
14249
|
selectLocal() {
|
|
13979
14250
|
this.remoteInvoke = void 0;
|
|
14251
|
+
this.remoteTarget = void 0;
|
|
13980
14252
|
this.remoteSupport = { execute: false, list: false };
|
|
14253
|
+
this.target = void 0;
|
|
13981
14254
|
}
|
|
13982
14255
|
restore() {
|
|
13983
14256
|
if (!this.installed) return;
|
|
13984
14257
|
this.selectLocal();
|
|
13985
|
-
this.
|
|
14258
|
+
this.runtime.invoke = this.originalInvoke;
|
|
14259
|
+
if (this.originalStream !== void 0) this.runtime.stream = this.originalStream;
|
|
14260
|
+
if (this.originalDispatch !== void 0) this.runtime.dispatchRpc = this.originalDispatch;
|
|
14261
|
+
if (this.originalOpen !== void 0) this.runtime.openWireStream = this.originalOpen;
|
|
13986
14262
|
this.installed = false;
|
|
13987
14263
|
}
|
|
14264
|
+
selectInvoke(request) {
|
|
14265
|
+
if (this.remoteTarget !== void 0) return this.remoteTarget.invoke(request);
|
|
14266
|
+
if (request.namespace !== "commands" || !isRemoteCommandMethod(request.method) || this.remoteInvoke === void 0) {
|
|
14267
|
+
return this.localInvoke(request);
|
|
14268
|
+
}
|
|
14269
|
+
if (this.remoteSupport[request.method]) return this.remoteInvoke(request);
|
|
14270
|
+
if (request.method === "list") return Promise.resolve([]);
|
|
14271
|
+
return this.localInvoke(request);
|
|
14272
|
+
}
|
|
14273
|
+
failure(error) {
|
|
14274
|
+
const normalized = this.runtime.wireStream?.failure(error);
|
|
14275
|
+
if (normalized !== void 0) return normalized;
|
|
14276
|
+
const source = error instanceof Error ? error : new Error("The Harness Gateway rejected the request.");
|
|
14277
|
+
const code = "code" in source && typeof source.code === "string" ? source.code : "internal";
|
|
14278
|
+
const details = "details" in source && isRecord2(source.details) ? source.details : {};
|
|
14279
|
+
return { code, message: source.message, details };
|
|
14280
|
+
}
|
|
13988
14281
|
};
|
|
14282
|
+
function requestFromCarrier(endpoint, payload, signal) {
|
|
14283
|
+
const segments = endpoint.split("/");
|
|
14284
|
+
if (segments.length !== 2 || segments.some((segment) => segment.length === 0)) {
|
|
14285
|
+
throw new Error("The Harness Gateway endpoint is invalid.");
|
|
14286
|
+
}
|
|
14287
|
+
if (!isRecord2(payload) || !isRecord2(payload.args)) {
|
|
14288
|
+
throw new Error("The Harness Gateway payload is invalid.");
|
|
14289
|
+
}
|
|
14290
|
+
return { namespace: segments[0], method: segments[1], args: payload.args, signal };
|
|
14291
|
+
}
|
|
14292
|
+
function endpointOf(request) {
|
|
14293
|
+
return `${request.namespace}/${request.method}`;
|
|
14294
|
+
}
|
|
13989
14295
|
function isRemoteCommandMethod(method) {
|
|
13990
14296
|
return REMOTE_COMMAND_METHODS.includes(method);
|
|
13991
14297
|
}
|
|
14298
|
+
function isRecord2(value) {
|
|
14299
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14300
|
+
}
|
|
13992
14301
|
|
|
13993
14302
|
// src/werift-rtc.ts
|
|
13994
14303
|
import { createSocket } from "node:dgram";
|
|
@@ -15057,7 +15366,7 @@ var ClientModeRuntime = class {
|
|
|
15057
15366
|
this.logger = logger;
|
|
15058
15367
|
this.host = host;
|
|
15059
15368
|
this.rtcFactoryProvider = rtcFactoryProvider;
|
|
15060
|
-
this.proxySwitch = new ApiProxySwitch(apiProxy);
|
|
15369
|
+
this.proxySwitch = apiProxy === void 0 ? void 0 : new ApiProxySwitch(apiProxy);
|
|
15061
15370
|
this.gatewaySwitch = new TypertGatewaySwitch(typertGateway);
|
|
15062
15371
|
}
|
|
15063
15372
|
identity;
|
|
@@ -15070,7 +15379,7 @@ var ClientModeRuntime = class {
|
|
|
15070
15379
|
if (this.closed) throw new Error("client remote-mode runtime is closed");
|
|
15071
15380
|
this.identity = await this.identities.loadOrCreate(this.config.deviceName);
|
|
15072
15381
|
this.server.bindIdentity(this.identity);
|
|
15073
|
-
this.proxySwitch
|
|
15382
|
+
this.proxySwitch?.install();
|
|
15074
15383
|
this.gatewaySwitch.install();
|
|
15075
15384
|
this.logger.info("client remote-mode identity ready", {
|
|
15076
15385
|
deviceId: shortId(this.identity.deviceId),
|
|
@@ -15083,13 +15392,14 @@ var ClientModeRuntime = class {
|
|
|
15083
15392
|
});
|
|
15084
15393
|
}
|
|
15085
15394
|
status() {
|
|
15395
|
+
const targetStatus = this.gatewaySwitch.supportsCarrier() ? this.gatewaySwitch.status() : this.proxySwitch?.status() ?? this.gatewaySwitch.status();
|
|
15086
15396
|
return {
|
|
15087
15397
|
available: this.config.serverUrl !== void 0,
|
|
15088
15398
|
identityReady: this.identity !== void 0,
|
|
15089
15399
|
deviceId: this.identity?.deviceId,
|
|
15090
15400
|
deviceName: this.identity?.name,
|
|
15091
15401
|
serverUrl: this.config.serverUrl,
|
|
15092
|
-
...
|
|
15402
|
+
...targetStatus,
|
|
15093
15403
|
connected: this.connected !== void 0,
|
|
15094
15404
|
transport: this.connected?.client.getStats().mode ?? "Disconnected",
|
|
15095
15405
|
preferredTransports: this.config.forceRelay ? ["relay"] : ["lan", "p2p", "turn", "relay"],
|
|
@@ -15161,7 +15471,7 @@ var ClientModeRuntime = class {
|
|
|
15161
15471
|
const previous = this.connected;
|
|
15162
15472
|
this.connected = void 0;
|
|
15163
15473
|
this.pendingWorkspaceSelection = void 0;
|
|
15164
|
-
this.proxySwitch
|
|
15474
|
+
this.proxySwitch?.selectLocal();
|
|
15165
15475
|
this.gatewaySwitch.selectLocal();
|
|
15166
15476
|
await previous?.client.close().catch(() => void 0);
|
|
15167
15477
|
await this.server.revokeCurrentDevice();
|
|
@@ -15180,7 +15490,7 @@ var ClientModeRuntime = class {
|
|
|
15180
15490
|
}
|
|
15181
15491
|
async setMode(mode, targetDeviceId, signal) {
|
|
15182
15492
|
if (mode === "local") {
|
|
15183
|
-
this.proxySwitch
|
|
15493
|
+
this.proxySwitch?.selectLocal();
|
|
15184
15494
|
this.gatewaySwitch.selectLocal();
|
|
15185
15495
|
const previous2 = this.connected;
|
|
15186
15496
|
this.connected = void 0;
|
|
@@ -15193,18 +15503,31 @@ var ClientModeRuntime = class {
|
|
|
15193
15503
|
throw new ClientModeError("INVALID_MESSAGE", "A targetDeviceId is required for remote mode.");
|
|
15194
15504
|
}
|
|
15195
15505
|
const next = await this.connect(targetDeviceId, signal);
|
|
15506
|
+
try {
|
|
15507
|
+
this.assertRemoteCompatible(next);
|
|
15508
|
+
} catch (error) {
|
|
15509
|
+
await next.client.close().catch(() => void 0);
|
|
15510
|
+
throw error;
|
|
15511
|
+
}
|
|
15196
15512
|
const previous = this.connected;
|
|
15197
15513
|
this.connected = next;
|
|
15198
15514
|
this.pendingWorkspaceSelection = void 0;
|
|
15199
|
-
|
|
15200
|
-
this.proxySwitch.selectRemote(remoteApi, { deviceId: next.target.deviceId, name: next.target.name });
|
|
15201
|
-
this.selectRemoteCommands(next);
|
|
15515
|
+
this.selectRemoteTarget(next);
|
|
15202
15516
|
await previous?.client.close().catch(() => void 0);
|
|
15203
15517
|
this.logger.info("Harness target switched", { mode: "remote", targetDeviceId: shortId(next.target.deviceId) });
|
|
15204
15518
|
return this.status();
|
|
15205
15519
|
}
|
|
15206
15520
|
async listRemoteDirectory(targetDeviceId, path, signal) {
|
|
15207
15521
|
const remote = await this.ensureConnected(targetDeviceId, signal);
|
|
15522
|
+
if (remote.features.remoteGateway) {
|
|
15523
|
+
const value = await new RemoteTypertGateway(remote.client).invoke({
|
|
15524
|
+
namespace: "directoryPicker",
|
|
15525
|
+
method: "list",
|
|
15526
|
+
args: path === void 0 ? {} : { path },
|
|
15527
|
+
...signal === void 0 ? {} : { signal }
|
|
15528
|
+
});
|
|
15529
|
+
return value;
|
|
15530
|
+
}
|
|
15208
15531
|
const api = new RemoteHarnessApiProxy(remote.client).api;
|
|
15209
15532
|
const response = await api.host.listDirectory({
|
|
15210
15533
|
rpcId: `remote-directory-${Date.now()}`,
|
|
@@ -15214,6 +15537,9 @@ var ClientModeRuntime = class {
|
|
|
15214
15537
|
}
|
|
15215
15538
|
async listRemoteWorkspaces(targetDeviceId, signal) {
|
|
15216
15539
|
const remote = await this.ensureConnected(targetDeviceId, signal);
|
|
15540
|
+
if (remote.features.remoteGateway) {
|
|
15541
|
+
return readRemoteWorkspaceBaseline(new RemoteTypertGateway(remote.client), signal);
|
|
15542
|
+
}
|
|
15217
15543
|
const api = new RemoteHarnessApiProxy(remote.client).api;
|
|
15218
15544
|
const response = await api.workspace.list({
|
|
15219
15545
|
rpcId: `remote-workspaces-${Date.now()}`,
|
|
@@ -15225,14 +15551,24 @@ var ClientModeRuntime = class {
|
|
|
15225
15551
|
async openRemoteWorkspace(targetDeviceId, path, signal) {
|
|
15226
15552
|
if (path.trim() === "") throw new ClientModeError("INVALID_MESSAGE", "A remote working directory is required.");
|
|
15227
15553
|
const remote = await this.ensureConnected(targetDeviceId, signal);
|
|
15228
|
-
|
|
15229
|
-
|
|
15230
|
-
|
|
15231
|
-
|
|
15232
|
-
|
|
15233
|
-
|
|
15234
|
-
|
|
15235
|
-
|
|
15554
|
+
this.assertRemoteCompatible(remote);
|
|
15555
|
+
let workspace;
|
|
15556
|
+
if (remote.features.remoteGateway) {
|
|
15557
|
+
workspace = await new RemoteTypertGateway(remote.client).invoke({
|
|
15558
|
+
namespace: "workspace",
|
|
15559
|
+
method: "create",
|
|
15560
|
+
args: { request: { path } },
|
|
15561
|
+
...signal === void 0 ? {} : { signal }
|
|
15562
|
+
});
|
|
15563
|
+
} else {
|
|
15564
|
+
const api = new RemoteHarnessApiProxy(remote.client).api;
|
|
15565
|
+
const response = await api.workspace.create({
|
|
15566
|
+
rpcId: `remote-workspace-${Date.now()}`,
|
|
15567
|
+
payload: { path }
|
|
15568
|
+
});
|
|
15569
|
+
workspace = unwrapNativeResult(response);
|
|
15570
|
+
}
|
|
15571
|
+
this.selectRemoteTarget(remote);
|
|
15236
15572
|
const workspaceId = workspaceRecordId(workspace.workspace);
|
|
15237
15573
|
this.pendingWorkspaceSelection = { targetDeviceId: remote.target.deviceId, workspaceId };
|
|
15238
15574
|
this.logger.info("Remote workspace opened", { targetDeviceId: shortId(remote.target.deviceId) });
|
|
@@ -15248,17 +15584,17 @@ var ClientModeRuntime = class {
|
|
|
15248
15584
|
async close() {
|
|
15249
15585
|
if (this.closed) return;
|
|
15250
15586
|
this.closed = true;
|
|
15251
|
-
this.proxySwitch
|
|
15587
|
+
this.proxySwitch?.selectLocal();
|
|
15252
15588
|
this.gatewaySwitch.selectLocal();
|
|
15253
15589
|
this.pendingWorkspaceSelection = void 0;
|
|
15254
15590
|
await this.connected?.client.close().catch(() => void 0);
|
|
15255
15591
|
this.connected = void 0;
|
|
15256
|
-
this.proxySwitch
|
|
15592
|
+
this.proxySwitch?.restore();
|
|
15257
15593
|
this.gatewaySwitch.restore();
|
|
15258
15594
|
}
|
|
15259
15595
|
async callRemoteFileViewer(endpoint, payload, signal) {
|
|
15260
15596
|
const remote = this.connected;
|
|
15261
|
-
if (remote === void 0 || this.
|
|
15597
|
+
if (remote === void 0 || this.status().mode !== "remote") {
|
|
15262
15598
|
throw new ClientModeError("REMOTE_NOT_CONNECTED", "No Remote Host is selected.", true);
|
|
15263
15599
|
}
|
|
15264
15600
|
if (!remote.features.fileViewer) {
|
|
@@ -15266,11 +15602,25 @@ var ClientModeRuntime = class {
|
|
|
15266
15602
|
}
|
|
15267
15603
|
return remote.client.rpc("fileviewer.call", { endpoint, payload }, signal);
|
|
15268
15604
|
}
|
|
15269
|
-
|
|
15605
|
+
selectRemoteTarget(remote) {
|
|
15606
|
+
const target = { deviceId: remote.target.deviceId, name: remote.target.name };
|
|
15607
|
+
if (this.gatewaySwitch.supportsCarrier()) {
|
|
15608
|
+
this.gatewaySwitch.selectRemote(new RemoteTypertGateway(remote.client), void 0, target);
|
|
15609
|
+
return;
|
|
15610
|
+
}
|
|
15611
|
+
this.proxySwitch.selectRemote(new RemoteHarnessApiProxy(remote.client).api, target);
|
|
15270
15612
|
this.gatewaySwitch.selectRemote((request) => invokeRemoteCommand(remote.client, request), {
|
|
15271
15613
|
execute: true,
|
|
15272
15614
|
list: remote.features.commandList
|
|
15273
|
-
});
|
|
15615
|
+
}, target);
|
|
15616
|
+
}
|
|
15617
|
+
assertRemoteCompatible(remote) {
|
|
15618
|
+
const localRemoteGateway = this.gatewaySwitch.supportsCarrier();
|
|
15619
|
+
if (localRemoteGateway && remote.features.remoteGateway || !localRemoteGateway && this.proxySwitch !== void 0 && remote.features.apiProxy) return;
|
|
15620
|
+
throw new ClientModeError(
|
|
15621
|
+
"HARNESS_VERSION_INCOMPATIBLE",
|
|
15622
|
+
localRemoteGateway ? "The selected Host does not provide the Harness alpha Remote Gateway transport." : "The selected Host does not provide the legacy Harness ApiProxy transport."
|
|
15623
|
+
);
|
|
15274
15624
|
}
|
|
15275
15625
|
async connect(targetDeviceId, signal) {
|
|
15276
15626
|
signal?.throwIfAborted();
|
|
@@ -15337,7 +15687,7 @@ var ClientModeRuntime = class {
|
|
|
15337
15687
|
if (this.connected?.client !== connectedClient) return;
|
|
15338
15688
|
this.connected = void 0;
|
|
15339
15689
|
this.pendingWorkspaceSelection = void 0;
|
|
15340
|
-
this.proxySwitch
|
|
15690
|
+
this.proxySwitch?.selectLocal();
|
|
15341
15691
|
this.gatewaySwitch.selectLocal();
|
|
15342
15692
|
void connectedClient.close().catch(() => void 0);
|
|
15343
15693
|
this.logger.warn("remote Harness transport closed; falling back to local mode", {
|
|
@@ -15350,7 +15700,8 @@ var ClientModeRuntime = class {
|
|
|
15350
15700
|
transport: connectedClient.getStats().mode,
|
|
15351
15701
|
...webrtcDiagnosticsLogFields(connectionDetails?.webRtc?.diagnostics)
|
|
15352
15702
|
});
|
|
15353
|
-
|
|
15703
|
+
const features = await probeRemoteHostFeatures(connectedClient, serverDevice.clientVersion);
|
|
15704
|
+
return { client: connectedClient, target, transport: connectedTransport, features };
|
|
15354
15705
|
} catch (error) {
|
|
15355
15706
|
await client?.close().catch(() => void 0);
|
|
15356
15707
|
throw error;
|
|
@@ -15519,6 +15870,9 @@ function record(value) {
|
|
|
15519
15870
|
}
|
|
15520
15871
|
return value;
|
|
15521
15872
|
}
|
|
15873
|
+
function isRecord3(value) {
|
|
15874
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
15875
|
+
}
|
|
15522
15876
|
function ok(value) {
|
|
15523
15877
|
return { ok: true, value };
|
|
15524
15878
|
}
|
|
@@ -15534,6 +15888,27 @@ async function invokeRemoteCommand(client, request) {
|
|
|
15534
15888
|
}
|
|
15535
15889
|
return unwrapNativeResult(response);
|
|
15536
15890
|
}
|
|
15891
|
+
async function readRemoteWorkspaceBaseline(gateway, signal) {
|
|
15892
|
+
const lifetime = new AbortController();
|
|
15893
|
+
const activeSignal = signal === void 0 ? lifetime.signal : AbortSignal.any([signal, lifetime.signal]);
|
|
15894
|
+
const source = await gateway.open("workspace/follow", { args: {} }, activeSignal);
|
|
15895
|
+
const iterator = source[Symbol.asyncIterator]();
|
|
15896
|
+
try {
|
|
15897
|
+
const first = await iterator.next();
|
|
15898
|
+
if (first.done || !isRecord3(first.value) || first.value.type !== "baseline" || !isRecord3(first.value.value) || !Array.isArray(first.value.value.items)) {
|
|
15899
|
+
throw new ClientModeError("INVALID_MESSAGE", "The remote Host returned an invalid Workspace baseline.");
|
|
15900
|
+
}
|
|
15901
|
+
return first.value.value.items.map((item) => {
|
|
15902
|
+
if (!isRecord3(item) || typeof item.workspaceId !== "string" || typeof item.path !== "string" || typeof item.title !== "string") {
|
|
15903
|
+
throw new ClientModeError("INVALID_MESSAGE", "The remote Host returned an invalid Workspace row.");
|
|
15904
|
+
}
|
|
15905
|
+
return { workspaceId: item.workspaceId, path: item.path, title: item.title };
|
|
15906
|
+
});
|
|
15907
|
+
} finally {
|
|
15908
|
+
lifetime.abort("workspace-baseline-read");
|
|
15909
|
+
await iterator.return?.();
|
|
15910
|
+
}
|
|
15911
|
+
}
|
|
15537
15912
|
function unwrapNativeResult(response) {
|
|
15538
15913
|
const result = response.result;
|
|
15539
15914
|
if (typeof result !== "object" || result === null || !("ok" in result)) {
|
|
@@ -15570,7 +15945,34 @@ function shortId(value) {
|
|
|
15570
15945
|
function remoteHostFeatures(clientVersion) {
|
|
15571
15946
|
return {
|
|
15572
15947
|
commandList: isVersionAtLeast(clientVersion, REMOTE_COMMAND_LIST_MIN_VERSION),
|
|
15573
|
-
fileViewer: isVersionAtLeast(clientVersion, REMOTE_FILE_VIEWER_MIN_VERSION)
|
|
15948
|
+
fileViewer: isVersionAtLeast(clientVersion, REMOTE_FILE_VIEWER_MIN_VERSION),
|
|
15949
|
+
apiProxy: true,
|
|
15950
|
+
remoteGateway: false
|
|
15951
|
+
};
|
|
15952
|
+
}
|
|
15953
|
+
async function probeRemoteHostFeatures(client, clientVersion) {
|
|
15954
|
+
const fallback = remoteHostFeatures(clientVersion);
|
|
15955
|
+
let value;
|
|
15956
|
+
try {
|
|
15957
|
+
value = await client.rpc("harness.transport.describe", {});
|
|
15958
|
+
} catch (error) {
|
|
15959
|
+
if (error instanceof Error && "code" in error && error.code === "METHOD_NOT_FOUND") return fallback;
|
|
15960
|
+
throw error;
|
|
15961
|
+
}
|
|
15962
|
+
if (!isRecord3(value) || !Array.isArray(value.capabilities) || value.capabilities.some((capability) => typeof capability !== "string")) {
|
|
15963
|
+
throw new ClientModeError("INVALID_MESSAGE", "The remote Host returned invalid transport capabilities.");
|
|
15964
|
+
}
|
|
15965
|
+
const capabilities = new Set(value.capabilities);
|
|
15966
|
+
const apiProxy = capabilities.has("harness.api.v1");
|
|
15967
|
+
const remoteGateway = capabilities.has("harness.remote.v1");
|
|
15968
|
+
if (!apiProxy && !remoteGateway) {
|
|
15969
|
+
throw new ClientModeError("FEATURE_NOT_SUPPORTED", "The remote Host exposes no supported Harness transport.");
|
|
15970
|
+
}
|
|
15971
|
+
return {
|
|
15972
|
+
commandList: remoteGateway || apiProxy && fallback.commandList,
|
|
15973
|
+
fileViewer: capabilities.has("fileviewer.read.v1"),
|
|
15974
|
+
apiProxy,
|
|
15975
|
+
remoteGateway
|
|
15574
15976
|
};
|
|
15575
15977
|
}
|
|
15576
15978
|
function isVersionAtLeast(value, minimum) {
|
|
@@ -16051,11 +16453,11 @@ function editableConfig(config) {
|
|
|
16051
16453
|
} : false
|
|
16052
16454
|
};
|
|
16053
16455
|
}
|
|
16054
|
-
function
|
|
16456
|
+
function isRecord4(value) {
|
|
16055
16457
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16056
16458
|
}
|
|
16057
16459
|
function record2(value) {
|
|
16058
|
-
if (!
|
|
16460
|
+
if (!isRecord4(value)) throw new ClientModeError("INVALID_MESSAGE", "The control request payload is invalid.");
|
|
16059
16461
|
return value;
|
|
16060
16462
|
}
|
|
16061
16463
|
function ok2(value) {
|
|
@@ -16109,7 +16511,6 @@ function redact(value, key = "") {
|
|
|
16109
16511
|
|
|
16110
16512
|
// src/service.ts
|
|
16111
16513
|
import { randomUUID } from "node:crypto";
|
|
16112
|
-
import { RpcId } from "@deepseek-ai/dsh-host-apiproxy/api";
|
|
16113
16514
|
|
|
16114
16515
|
// src/connection-controller.ts
|
|
16115
16516
|
var ConnectionController = class {
|
|
@@ -16326,7 +16727,9 @@ function safeErrorCode(error) {
|
|
|
16326
16727
|
|
|
16327
16728
|
// src/rpc-router.ts
|
|
16328
16729
|
var wireRequestSchema = external_exports.object({ method: external_exports.string().min(1), params: external_exports.unknown() }).strict();
|
|
16730
|
+
var emptyParamsSchema = external_exports.object({}).strict();
|
|
16329
16731
|
var apiMethods = /* @__PURE__ */ new Set([
|
|
16732
|
+
"harness.transport.describe",
|
|
16330
16733
|
"harness.api.call",
|
|
16331
16734
|
"harness.api.transfer.open",
|
|
16332
16735
|
"harness.api.transfer.chunk",
|
|
@@ -16336,19 +16739,38 @@ var apiMethods = /* @__PURE__ */ new Set([
|
|
|
16336
16739
|
"harness.api.respond",
|
|
16337
16740
|
"harness.api.stream.open",
|
|
16338
16741
|
"harness.api.stream.close",
|
|
16742
|
+
"harness.remote.call",
|
|
16743
|
+
"harness.remote.transfer.open",
|
|
16744
|
+
"harness.remote.transfer.chunk",
|
|
16745
|
+
"harness.remote.transfer.commit",
|
|
16746
|
+
"harness.remote.transfer.read",
|
|
16747
|
+
"harness.remote.transfer.close",
|
|
16748
|
+
"harness.remote.stream.open",
|
|
16749
|
+
"harness.remote.stream.close",
|
|
16339
16750
|
"fileviewer.call"
|
|
16340
16751
|
]);
|
|
16341
|
-
var HOST_CAPABILITIES = [
|
|
16752
|
+
var HOST_CAPABILITIES = [
|
|
16753
|
+
"harness.api.v1",
|
|
16754
|
+
"harness.api.transfer.v1",
|
|
16755
|
+
"harness.remote.v1",
|
|
16756
|
+
"harness.remote.transfer.v1",
|
|
16757
|
+
"fileviewer.read.v1"
|
|
16758
|
+
];
|
|
16342
16759
|
var RpcRouter = class {
|
|
16343
|
-
constructor(harnessApi, maxPending = 128, logger, fileViewer) {
|
|
16760
|
+
constructor(harnessApi, maxPending = 128, logger, fileViewer, harnessRemote, capabilities = () => HOST_CAPABILITIES) {
|
|
16344
16761
|
this.harnessApi = harnessApi;
|
|
16345
16762
|
this.maxPending = maxPending;
|
|
16346
16763
|
this.logger = logger;
|
|
16347
16764
|
this.fileViewer = fileViewer;
|
|
16765
|
+
this.harnessRemote = harnessRemote;
|
|
16766
|
+
this.capabilities = capabilities;
|
|
16348
16767
|
}
|
|
16349
16768
|
active = 0;
|
|
16350
|
-
closePeerStreams() {
|
|
16351
|
-
|
|
16769
|
+
async closePeerStreams() {
|
|
16770
|
+
await Promise.all([
|
|
16771
|
+
this.harnessApi?.closeAll(),
|
|
16772
|
+
this.harnessRemote?.closeAll()
|
|
16773
|
+
]);
|
|
16352
16774
|
}
|
|
16353
16775
|
async handle(message) {
|
|
16354
16776
|
if (message.type !== "rpc.request") {
|
|
@@ -16387,24 +16809,44 @@ var RpcRouter = class {
|
|
|
16387
16809
|
}
|
|
16388
16810
|
invoke(method, params) {
|
|
16389
16811
|
switch (method) {
|
|
16812
|
+
case "harness.transport.describe": {
|
|
16813
|
+
emptyParamsSchema.parse(params);
|
|
16814
|
+
return { capabilities: [...this.capabilities()] };
|
|
16815
|
+
}
|
|
16390
16816
|
case "harness.api.call":
|
|
16391
|
-
return this.
|
|
16817
|
+
return this.requireApiProxy().call(params);
|
|
16392
16818
|
case "harness.api.transfer.open":
|
|
16393
|
-
return this.
|
|
16819
|
+
return this.requireApiProxy().openTransfer(params);
|
|
16394
16820
|
case "harness.api.transfer.chunk":
|
|
16395
|
-
return this.
|
|
16821
|
+
return this.requireApiProxy().appendTransfer(params);
|
|
16396
16822
|
case "harness.api.transfer.commit":
|
|
16397
|
-
return this.
|
|
16823
|
+
return this.requireApiProxy().commitTransfer(params);
|
|
16398
16824
|
case "harness.api.transfer.read":
|
|
16399
|
-
return this.
|
|
16825
|
+
return this.requireApiProxy().readTransfer(params);
|
|
16400
16826
|
case "harness.api.transfer.close":
|
|
16401
|
-
return this.
|
|
16827
|
+
return this.requireApiProxy().closeTransfer(params);
|
|
16402
16828
|
case "harness.api.respond":
|
|
16403
|
-
return this.
|
|
16829
|
+
return this.requireApiProxy().respond(params);
|
|
16404
16830
|
case "harness.api.stream.open":
|
|
16405
|
-
return this.
|
|
16831
|
+
return this.requireApiProxy().openStream(params);
|
|
16406
16832
|
case "harness.api.stream.close":
|
|
16407
|
-
return this.
|
|
16833
|
+
return this.requireApiProxy().closeStream(params);
|
|
16834
|
+
case "harness.remote.call":
|
|
16835
|
+
return this.requireRemoteGateway().call(params);
|
|
16836
|
+
case "harness.remote.transfer.open":
|
|
16837
|
+
return this.requireRemoteGateway().openTransfer(params);
|
|
16838
|
+
case "harness.remote.transfer.chunk":
|
|
16839
|
+
return this.requireRemoteGateway().appendTransfer(params);
|
|
16840
|
+
case "harness.remote.transfer.commit":
|
|
16841
|
+
return this.requireRemoteGateway().commitTransfer(params);
|
|
16842
|
+
case "harness.remote.transfer.read":
|
|
16843
|
+
return this.requireRemoteGateway().readTransfer(params);
|
|
16844
|
+
case "harness.remote.transfer.close":
|
|
16845
|
+
return this.requireRemoteGateway().closeTransfer(params);
|
|
16846
|
+
case "harness.remote.stream.open":
|
|
16847
|
+
return this.requireRemoteGateway().openStream(params);
|
|
16848
|
+
case "harness.remote.stream.close":
|
|
16849
|
+
return this.requireRemoteGateway().closeStream(params);
|
|
16408
16850
|
case "fileviewer.call": {
|
|
16409
16851
|
if (this.fileViewer === void 0) {
|
|
16410
16852
|
throw new RpcError("FILE_VIEWER_UNAVAILABLE", "The Remote Host does not have DSH File Viewer available.");
|
|
@@ -16415,6 +16857,18 @@ var RpcRouter = class {
|
|
|
16415
16857
|
throw new RpcError("METHOD_NOT_FOUND", "The requested method does not exist.");
|
|
16416
16858
|
}
|
|
16417
16859
|
}
|
|
16860
|
+
requireApiProxy() {
|
|
16861
|
+
if (this.harnessApi === void 0) {
|
|
16862
|
+
throw new RpcError("FEATURE_NOT_SUPPORTED", "This Harness version does not provide the legacy ApiProxy transport.");
|
|
16863
|
+
}
|
|
16864
|
+
return this.harnessApi;
|
|
16865
|
+
}
|
|
16866
|
+
requireRemoteGateway() {
|
|
16867
|
+
if (this.harnessRemote === void 0) {
|
|
16868
|
+
throw new RpcError("FEATURE_NOT_SUPPORTED", "This Harness version does not provide the Remote Gateway transport.");
|
|
16869
|
+
}
|
|
16870
|
+
return this.harnessRemote;
|
|
16871
|
+
}
|
|
16418
16872
|
};
|
|
16419
16873
|
function errorResponse(requestId, error) {
|
|
16420
16874
|
const code = safeErrorCode(error);
|
|
@@ -18069,28 +18523,333 @@ function concatChunks(chunks, totalBytes) {
|
|
|
18069
18523
|
return result;
|
|
18070
18524
|
}
|
|
18071
18525
|
|
|
18526
|
+
// src/harness-remote-bridge.ts
|
|
18527
|
+
var endpointSchema = external_exports.string().min(1).max(128).regex(/^(?:\$events(?:\/result)?|[A-Za-z0-9_$.-]+\/[A-Za-z0-9_$.-]+)$/);
|
|
18528
|
+
var callSchema3 = external_exports.object({ endpoint: endpointSchema, payload: external_exports.unknown() }).strict();
|
|
18529
|
+
var streamOpenSchema2 = external_exports.object({
|
|
18530
|
+
streamId: external_exports.string().min(1).max(128),
|
|
18531
|
+
endpoint: endpointSchema,
|
|
18532
|
+
payload: external_exports.unknown()
|
|
18533
|
+
}).strict();
|
|
18534
|
+
var streamCloseSchema2 = external_exports.object({ streamId: external_exports.string().min(1).max(128) }).strict();
|
|
18535
|
+
var transferOpenSchema2 = external_exports.object({
|
|
18536
|
+
transferId: external_exports.string().uuid(),
|
|
18537
|
+
totalBytes: external_exports.number().int().positive().max(MAX_HARNESS_API_TRANSFER_BYTES),
|
|
18538
|
+
totalChunks: external_exports.number().int().positive()
|
|
18539
|
+
}).strict();
|
|
18540
|
+
var transferChunkSchema2 = external_exports.object({
|
|
18541
|
+
transferId: external_exports.string().uuid(),
|
|
18542
|
+
index: external_exports.number().int().nonnegative(),
|
|
18543
|
+
data: external_exports.string().min(1)
|
|
18544
|
+
}).strict();
|
|
18545
|
+
var transferIdSchema2 = external_exports.object({ transferId: external_exports.string().uuid() }).strict();
|
|
18546
|
+
var transferReadSchema2 = external_exports.object({
|
|
18547
|
+
transferId: external_exports.string().uuid(),
|
|
18548
|
+
index: external_exports.number().int().nonnegative()
|
|
18549
|
+
}).strict();
|
|
18550
|
+
var MAX_ACTIVE_STREAMS = 16;
|
|
18551
|
+
var MAX_ACTIVE_TRANSFERS = 2;
|
|
18552
|
+
var TRANSFER_IDLE_MS = 2 * 6e4;
|
|
18553
|
+
var INLINE_TRANSFER_RESPONSE_BYTES2 = 2 * 1024 * 1024;
|
|
18554
|
+
var HARNESS_REMOTE_ALLOWLIST = [
|
|
18555
|
+
"$events",
|
|
18556
|
+
"$events/result",
|
|
18557
|
+
"agentPresets/list",
|
|
18558
|
+
"agentPresets/read",
|
|
18559
|
+
"agentPresets/select",
|
|
18560
|
+
"commands/execute",
|
|
18561
|
+
"commands/list",
|
|
18562
|
+
"credentials/describe",
|
|
18563
|
+
"credentials/set",
|
|
18564
|
+
"credentials/unset",
|
|
18565
|
+
"directoryPicker/list",
|
|
18566
|
+
"fileReferences/list",
|
|
18567
|
+
"goals/clear",
|
|
18568
|
+
"goals/complete",
|
|
18569
|
+
"goals/create",
|
|
18570
|
+
"goals/edit",
|
|
18571
|
+
"goals/pause",
|
|
18572
|
+
"goals/resume",
|
|
18573
|
+
"llm/discoverModels",
|
|
18574
|
+
"llm/listConfigurableProviders",
|
|
18575
|
+
"llm/listProviders",
|
|
18576
|
+
"messageFeedback/delete",
|
|
18577
|
+
"messageFeedback/list",
|
|
18578
|
+
"messageFeedback/put",
|
|
18579
|
+
"pluginInventory/list",
|
|
18580
|
+
"session/attachment",
|
|
18581
|
+
"session/cancel",
|
|
18582
|
+
"session/control",
|
|
18583
|
+
"session/create",
|
|
18584
|
+
"session/follow",
|
|
18585
|
+
"session/fork",
|
|
18586
|
+
"session/list",
|
|
18587
|
+
"session/modelCatalog",
|
|
18588
|
+
"session/page",
|
|
18589
|
+
"session/prompt",
|
|
18590
|
+
"session/rename",
|
|
18591
|
+
"session/search",
|
|
18592
|
+
"session/selectModel",
|
|
18593
|
+
"session/updateQueue",
|
|
18594
|
+
"sessionReferenceResolver/candidates",
|
|
18595
|
+
"settings/describe",
|
|
18596
|
+
"settings/mutate",
|
|
18597
|
+
"settings/replace",
|
|
18598
|
+
"settings/update",
|
|
18599
|
+
"skills/list",
|
|
18600
|
+
"subagents/interruptByParent",
|
|
18601
|
+
"subagents/list",
|
|
18602
|
+
"subagents/prompt",
|
|
18603
|
+
"workspace/archiveSession",
|
|
18604
|
+
"workspace/create",
|
|
18605
|
+
"workspace/delete",
|
|
18606
|
+
"workspace/follow",
|
|
18607
|
+
"workspace/insertBefore",
|
|
18608
|
+
"workspace/insertSessionBefore",
|
|
18609
|
+
"workspace/rename"
|
|
18610
|
+
];
|
|
18611
|
+
var allowedEndpoints = new Set(HARNESS_REMOTE_ALLOWLIST);
|
|
18612
|
+
var HarnessRemoteBridge = class {
|
|
18613
|
+
constructor(gateway, publish, logger) {
|
|
18614
|
+
this.gateway = gateway;
|
|
18615
|
+
this.publish = publish;
|
|
18616
|
+
this.logger = logger;
|
|
18617
|
+
}
|
|
18618
|
+
streams = /* @__PURE__ */ new Map();
|
|
18619
|
+
incomingTransfers = /* @__PURE__ */ new Map();
|
|
18620
|
+
outgoingTransfers = /* @__PURE__ */ new Map();
|
|
18621
|
+
async call(input) {
|
|
18622
|
+
const params = callSchema3.parse(input);
|
|
18623
|
+
this.assertAllowed(params.endpoint);
|
|
18624
|
+
const startedAt = performance.now();
|
|
18625
|
+
try {
|
|
18626
|
+
const result = await this.gateway.dispatch(params.endpoint, params.payload, AbortSignal.timeout(6e4));
|
|
18627
|
+
this.logger?.debug("harness remote call ok", {
|
|
18628
|
+
endpoint: params.endpoint,
|
|
18629
|
+
durationMs: Math.round(performance.now() - startedAt)
|
|
18630
|
+
});
|
|
18631
|
+
return result;
|
|
18632
|
+
} catch (error) {
|
|
18633
|
+
this.logger?.warn("harness remote call failed", {
|
|
18634
|
+
endpoint: params.endpoint,
|
|
18635
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
18636
|
+
code: this.gateway.failure(error).code
|
|
18637
|
+
});
|
|
18638
|
+
throw error;
|
|
18639
|
+
}
|
|
18640
|
+
}
|
|
18641
|
+
openTransfer(input) {
|
|
18642
|
+
this.pruneTransfers();
|
|
18643
|
+
const params = transferOpenSchema2.parse(input);
|
|
18644
|
+
if (params.totalChunks !== Math.ceil(params.totalBytes / HARNESS_API_TRANSFER_CHUNK_BYTES)) {
|
|
18645
|
+
throw new RpcError("INVALID_MESSAGE", "The Harness Remote transfer chunk count is invalid.");
|
|
18646
|
+
}
|
|
18647
|
+
if (this.incomingTransfers.has(params.transferId) || this.outgoingTransfers.has(params.transferId)) {
|
|
18648
|
+
throw new RpcError("REQUEST_CONFLICT", "The Harness Remote transfer id is already active.");
|
|
18649
|
+
}
|
|
18650
|
+
if (this.incomingTransfers.size >= MAX_ACTIVE_TRANSFERS) {
|
|
18651
|
+
throw new RpcError("RATE_LIMITED", "Too many Harness Remote transfers are active.", void 0, true);
|
|
18652
|
+
}
|
|
18653
|
+
this.incomingTransfers.set(params.transferId, {
|
|
18654
|
+
totalBytes: params.totalBytes,
|
|
18655
|
+
totalChunks: params.totalChunks,
|
|
18656
|
+
chunks: [],
|
|
18657
|
+
receivedBytes: 0,
|
|
18658
|
+
touchedAt: Date.now()
|
|
18659
|
+
});
|
|
18660
|
+
return { opened: true, transferId: params.transferId };
|
|
18661
|
+
}
|
|
18662
|
+
appendTransfer(input) {
|
|
18663
|
+
this.pruneTransfers();
|
|
18664
|
+
const params = transferChunkSchema2.parse(input);
|
|
18665
|
+
const transfer = this.incomingTransfers.get(params.transferId);
|
|
18666
|
+
if (transfer === void 0) throw new RpcError("TRANSFER_NOT_FOUND", "The Harness Remote transfer is not active.");
|
|
18667
|
+
if (params.index !== transfer.chunks.length || params.index >= transfer.totalChunks) {
|
|
18668
|
+
this.incomingTransfers.delete(params.transferId);
|
|
18669
|
+
throw new RpcError("INVALID_MESSAGE", "Harness Remote transfer chunks must arrive exactly once and in order.");
|
|
18670
|
+
}
|
|
18671
|
+
const chunk = decodeCanonicalBase642(params.data);
|
|
18672
|
+
const expectedBytes = Math.min(
|
|
18673
|
+
HARNESS_API_TRANSFER_CHUNK_BYTES,
|
|
18674
|
+
transfer.totalBytes - params.index * HARNESS_API_TRANSFER_CHUNK_BYTES
|
|
18675
|
+
);
|
|
18676
|
+
if (chunk.byteLength !== expectedBytes) {
|
|
18677
|
+
this.incomingTransfers.delete(params.transferId);
|
|
18678
|
+
throw new RpcError("INVALID_MESSAGE", "The Harness Remote transfer chunk size is invalid.");
|
|
18679
|
+
}
|
|
18680
|
+
transfer.chunks.push(chunk);
|
|
18681
|
+
transfer.receivedBytes += chunk.byteLength;
|
|
18682
|
+
transfer.touchedAt = Date.now();
|
|
18683
|
+
return { accepted: true, transferId: params.transferId, index: params.index };
|
|
18684
|
+
}
|
|
18685
|
+
async commitTransfer(input) {
|
|
18686
|
+
this.pruneTransfers();
|
|
18687
|
+
const params = transferIdSchema2.parse(input);
|
|
18688
|
+
const transfer = this.incomingTransfers.get(params.transferId);
|
|
18689
|
+
if (transfer === void 0) throw new RpcError("TRANSFER_NOT_FOUND", "The Harness Remote transfer is not active.");
|
|
18690
|
+
this.incomingTransfers.delete(params.transferId);
|
|
18691
|
+
if (transfer.chunks.length !== transfer.totalChunks || transfer.receivedBytes !== transfer.totalBytes) {
|
|
18692
|
+
throw new RpcError("INVALID_MESSAGE", "The Harness Remote transfer is incomplete.");
|
|
18693
|
+
}
|
|
18694
|
+
let request;
|
|
18695
|
+
try {
|
|
18696
|
+
request = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(concatChunks2(transfer.chunks, transfer.totalBytes)));
|
|
18697
|
+
} catch {
|
|
18698
|
+
throw new RpcError("INVALID_MESSAGE", "The Harness Remote transfer does not contain a valid request.");
|
|
18699
|
+
}
|
|
18700
|
+
const response = await this.call(callSchema3.parse(request));
|
|
18701
|
+
const responseBytes = new TextEncoder().encode(JSON.stringify(response));
|
|
18702
|
+
if (responseBytes.byteLength <= INLINE_TRANSFER_RESPONSE_BYTES2) return { kind: "inline", response };
|
|
18703
|
+
if (responseBytes.byteLength > MAX_HARNESS_API_TRANSFER_BYTES) {
|
|
18704
|
+
throw new RpcError("RESPONSE_TOO_LARGE", "The Harness Remote response exceeds the bounded transfer limit.");
|
|
18705
|
+
}
|
|
18706
|
+
if (this.outgoingTransfers.size >= MAX_ACTIVE_TRANSFERS) {
|
|
18707
|
+
throw new RpcError("RATE_LIMITED", "Too many Harness Remote response transfers are active.", void 0, true);
|
|
18708
|
+
}
|
|
18709
|
+
const totalChunks = Math.ceil(responseBytes.byteLength / HARNESS_API_TRANSFER_CHUNK_BYTES);
|
|
18710
|
+
this.outgoingTransfers.set(params.transferId, {
|
|
18711
|
+
bytes: responseBytes,
|
|
18712
|
+
totalChunks,
|
|
18713
|
+
nextIndex: 0,
|
|
18714
|
+
touchedAt: Date.now()
|
|
18715
|
+
});
|
|
18716
|
+
return { kind: "chunked", transferId: params.transferId, totalBytes: responseBytes.byteLength, totalChunks };
|
|
18717
|
+
}
|
|
18718
|
+
readTransfer(input) {
|
|
18719
|
+
this.pruneTransfers();
|
|
18720
|
+
const params = transferReadSchema2.parse(input);
|
|
18721
|
+
const transfer = this.outgoingTransfers.get(params.transferId);
|
|
18722
|
+
if (transfer === void 0) throw new RpcError("TRANSFER_NOT_FOUND", "The Harness Remote response transfer is not active.");
|
|
18723
|
+
if (params.index !== transfer.nextIndex || params.index >= transfer.totalChunks) {
|
|
18724
|
+
this.outgoingTransfers.delete(params.transferId);
|
|
18725
|
+
throw new RpcError("INVALID_MESSAGE", "Harness Remote response chunks must be read exactly once and in order.");
|
|
18726
|
+
}
|
|
18727
|
+
const start = params.index * HARNESS_API_TRANSFER_CHUNK_BYTES;
|
|
18728
|
+
const end = Math.min(start + HARNESS_API_TRANSFER_CHUNK_BYTES, transfer.bytes.byteLength);
|
|
18729
|
+
transfer.nextIndex += 1;
|
|
18730
|
+
transfer.touchedAt = Date.now();
|
|
18731
|
+
return {
|
|
18732
|
+
transferId: params.transferId,
|
|
18733
|
+
index: params.index,
|
|
18734
|
+
data: Buffer.from(transfer.bytes.subarray(start, end)).toString("base64")
|
|
18735
|
+
};
|
|
18736
|
+
}
|
|
18737
|
+
closeTransfer(input) {
|
|
18738
|
+
const params = transferIdSchema2.parse(input);
|
|
18739
|
+
const closed = this.incomingTransfers.delete(params.transferId) || this.outgoingTransfers.delete(params.transferId);
|
|
18740
|
+
return { closed, transferId: params.transferId };
|
|
18741
|
+
}
|
|
18742
|
+
async openStream(input) {
|
|
18743
|
+
const params = streamOpenSchema2.parse(input);
|
|
18744
|
+
this.assertAllowed(params.endpoint);
|
|
18745
|
+
if (this.streams.has(params.streamId)) throw new RpcError("REQUEST_CONFLICT", "The Harness Remote stream is already open.");
|
|
18746
|
+
if (this.streams.size >= MAX_ACTIVE_STREAMS) {
|
|
18747
|
+
throw new RpcError("RATE_LIMITED", "Too many Harness Remote streams are open.", void 0, true);
|
|
18748
|
+
}
|
|
18749
|
+
const controller = new AbortController();
|
|
18750
|
+
const source = await this.gateway.open(params.endpoint, params.payload, controller.signal);
|
|
18751
|
+
this.streams.set(params.streamId, { controller });
|
|
18752
|
+
void this.pump(params.streamId, source, controller.signal);
|
|
18753
|
+
return { opened: true, streamId: params.streamId };
|
|
18754
|
+
}
|
|
18755
|
+
closeStream(input) {
|
|
18756
|
+
const params = streamCloseSchema2.parse(input);
|
|
18757
|
+
const active = this.streams.get(params.streamId);
|
|
18758
|
+
if (active !== void 0) {
|
|
18759
|
+
this.streams.delete(params.streamId);
|
|
18760
|
+
active.controller.abort("client-closed");
|
|
18761
|
+
}
|
|
18762
|
+
return { closed: active !== void 0, streamId: params.streamId };
|
|
18763
|
+
}
|
|
18764
|
+
async closeAll(reason = "peer-disconnected") {
|
|
18765
|
+
const streams = [...this.streams.entries()];
|
|
18766
|
+
this.streams.clear();
|
|
18767
|
+
this.incomingTransfers.clear();
|
|
18768
|
+
this.outgoingTransfers.clear();
|
|
18769
|
+
for (const [, stream] of streams) stream.controller.abort(reason);
|
|
18770
|
+
}
|
|
18771
|
+
assertAllowed(endpoint) {
|
|
18772
|
+
if (!allowedEndpoints.has(endpoint)) {
|
|
18773
|
+
throw new RpcError("METHOD_NOT_ALLOWED", "The requested Harness Remote endpoint is not allowed.");
|
|
18774
|
+
}
|
|
18775
|
+
if (endpoint === "$events" && !this.gateway.supportsCarrier) {
|
|
18776
|
+
throw new RpcError("FEATURE_NOT_SUPPORTED", "This Harness version does not support Remote event transport.");
|
|
18777
|
+
}
|
|
18778
|
+
}
|
|
18779
|
+
async pump(streamId, source, signal) {
|
|
18780
|
+
let reason = "completed";
|
|
18781
|
+
let failure;
|
|
18782
|
+
try {
|
|
18783
|
+
for await (const value of source) {
|
|
18784
|
+
if (signal.aborted) break;
|
|
18785
|
+
await this.publish("harness.remote.frame", {
|
|
18786
|
+
streamId,
|
|
18787
|
+
hasValue: true,
|
|
18788
|
+
...value === void 0 ? {} : { value }
|
|
18789
|
+
});
|
|
18790
|
+
}
|
|
18791
|
+
if (signal.aborted) reason = "cancelled";
|
|
18792
|
+
} catch (error) {
|
|
18793
|
+
reason = signal.aborted ? "cancelled" : "failed";
|
|
18794
|
+
if (!signal.aborted) failure = this.gateway.failure(error);
|
|
18795
|
+
} finally {
|
|
18796
|
+
this.streams.delete(streamId);
|
|
18797
|
+
await this.publish("harness.remote.stream.closed", {
|
|
18798
|
+
streamId,
|
|
18799
|
+
reason,
|
|
18800
|
+
...failure === void 0 ? {} : { failure }
|
|
18801
|
+
}).catch(() => void 0);
|
|
18802
|
+
}
|
|
18803
|
+
}
|
|
18804
|
+
pruneTransfers() {
|
|
18805
|
+
const cutoff = Date.now() - TRANSFER_IDLE_MS;
|
|
18806
|
+
for (const [id, transfer] of this.incomingTransfers) if (transfer.touchedAt < cutoff) this.incomingTransfers.delete(id);
|
|
18807
|
+
for (const [id, transfer] of this.outgoingTransfers) if (transfer.touchedAt < cutoff) this.outgoingTransfers.delete(id);
|
|
18808
|
+
}
|
|
18809
|
+
};
|
|
18810
|
+
function decodeCanonicalBase642(value) {
|
|
18811
|
+
const bytes = Buffer.from(value, "base64");
|
|
18812
|
+
if (bytes.toString("base64") !== value) throw new RpcError("INVALID_MESSAGE", "The Harness Remote transfer chunk is invalid.");
|
|
18813
|
+
return bytes;
|
|
18814
|
+
}
|
|
18815
|
+
function concatChunks2(chunks, totalBytes) {
|
|
18816
|
+
const result = new Uint8Array(totalBytes);
|
|
18817
|
+
let offset = 0;
|
|
18818
|
+
for (const chunk of chunks) {
|
|
18819
|
+
result.set(chunk, offset);
|
|
18820
|
+
offset += chunk.byteLength;
|
|
18821
|
+
}
|
|
18822
|
+
return result;
|
|
18823
|
+
}
|
|
18824
|
+
|
|
18072
18825
|
// src/service.ts
|
|
18073
18826
|
var HostPluginRuntime = class {
|
|
18074
|
-
constructor(config, identities, apiProxy, logger,
|
|
18827
|
+
constructor(config, identities, apiProxy, logger, localGateway, fileViewerHost) {
|
|
18075
18828
|
this.config = config;
|
|
18076
18829
|
this.identities = identities;
|
|
18077
18830
|
this.apiProxy = apiProxy;
|
|
18078
18831
|
this.logger = logger;
|
|
18832
|
+
this.localGateway = localGateway;
|
|
18079
18833
|
this.fileViewerHost = fileViewerHost;
|
|
18080
18834
|
this.connections = new ConnectionController(this.identities, (_context, send) => {
|
|
18081
|
-
const harnessApi = new HarnessApiBridge(
|
|
18835
|
+
const harnessApi = this.apiProxy === void 0 ? void 0 : new HarnessApiBridge(
|
|
18082
18836
|
this.apiProxy,
|
|
18083
18837
|
(event, data) => send(createEvent(event, data)),
|
|
18084
18838
|
void 0,
|
|
18085
18839
|
this.logger,
|
|
18086
|
-
|
|
18840
|
+
this.localGateway,
|
|
18087
18841
|
this.harnessVersion
|
|
18088
18842
|
);
|
|
18843
|
+
const harnessRemote = this.localGateway?.supportsCarrier === true ? new HarnessRemoteBridge(
|
|
18844
|
+
this.localGateway,
|
|
18845
|
+
(event, data) => send(createEvent(event, data)),
|
|
18846
|
+
this.logger
|
|
18847
|
+
) : void 0;
|
|
18089
18848
|
const fileViewer = new RemoteFileViewerBridge(
|
|
18090
18849
|
() => this.fileViewerHost?.(),
|
|
18091
18850
|
this.logger
|
|
18092
18851
|
);
|
|
18093
|
-
return new RpcRouter(harnessApi, void 0, this.logger, fileViewer);
|
|
18852
|
+
return new RpcRouter(harnessApi, void 0, this.logger, fileViewer, harnessRemote, () => this.hostCapabilities());
|
|
18094
18853
|
}, this.logger);
|
|
18095
18854
|
if (config.serverUrl !== void 0) {
|
|
18096
18855
|
this.serverApi = new HostServerApi(config.serverUrl, new ServerCredentialStore(identities.directory));
|
|
@@ -18231,7 +18990,7 @@ var HostPluginRuntime = class {
|
|
|
18231
18990
|
this.logger,
|
|
18232
18991
|
void 0,
|
|
18233
18992
|
this.config.forceRelay ? void 0 : () => loadNodeRtcFactory({ routeTargets: this.config.serverUrl === void 0 ? [] : [this.config.serverUrl] }),
|
|
18234
|
-
() => this.
|
|
18993
|
+
() => this.hostCapabilities(),
|
|
18235
18994
|
this.harnessVersion
|
|
18236
18995
|
);
|
|
18237
18996
|
}
|
|
@@ -18239,7 +18998,8 @@ var HostPluginRuntime = class {
|
|
|
18239
18998
|
let reportedVersion;
|
|
18240
18999
|
let errorCode2;
|
|
18241
19000
|
try {
|
|
18242
|
-
const response = await this.apiProxy
|
|
19001
|
+
const response = await this.apiProxy?.host.describe({ rpcId: randomUUID(), payload: {} });
|
|
19002
|
+
if (response === void 0) throw new Error("ApiProxy is unavailable");
|
|
18243
19003
|
if (!response.result.ok) {
|
|
18244
19004
|
errorCode2 = response.result.error.code;
|
|
18245
19005
|
} else {
|
|
@@ -18253,6 +19013,15 @@ var HostPluginRuntime = class {
|
|
|
18253
19013
|
this.logger.warn("Harness version is unavailable", errorCode2 === void 0 ? void 0 : { code: errorCode2 });
|
|
18254
19014
|
return void 0;
|
|
18255
19015
|
}
|
|
19016
|
+
hostCapabilities() {
|
|
19017
|
+
const capabilities = [];
|
|
19018
|
+
if (this.apiProxy !== void 0) capabilities.push("harness.api.v1", "harness.api.transfer.v1");
|
|
19019
|
+
if (this.localGateway?.supportsCarrier === true) {
|
|
19020
|
+
capabilities.push("harness.remote.v1", "harness.remote.transfer.v1");
|
|
19021
|
+
}
|
|
19022
|
+
if (this.fileViewerHost?.() !== void 0) capabilities.push("fileviewer.read.v1");
|
|
19023
|
+
return capabilities;
|
|
19024
|
+
}
|
|
18256
19025
|
};
|
|
18257
19026
|
function shortId5(value) {
|
|
18258
19027
|
return value.length <= 12 ? value : `${value.slice(0, 8)}\u2026${value.slice(-4)}`;
|
|
@@ -18335,7 +19104,13 @@ function decodeBase64(value) {
|
|
|
18335
19104
|
var name = "ds-harness-remote";
|
|
18336
19105
|
var legacyLoaderModuleNames = /* @__PURE__ */ new Set(["dsh-remote", "@dsh-remote/plugin"]);
|
|
18337
19106
|
function apply(ctx, input = {}) {
|
|
18338
|
-
ctx.inject(["settings", "
|
|
19107
|
+
ctx.inject(["settings", "connection", "typertGateway"], (runtimeContext) => {
|
|
19108
|
+
const gateway = runtimeContext.get("typertGateway");
|
|
19109
|
+
if (runtimeContext.get("apiProxy") !== void 0 || new TypertGatewaySwitch(gateway).supportsCarrier()) {
|
|
19110
|
+
return activate(runtimeContext, input);
|
|
19111
|
+
}
|
|
19112
|
+
runtimeContext.inject(["apiProxy"], (legacyContext) => activate(legacyContext, input));
|
|
19113
|
+
});
|
|
18339
19114
|
}
|
|
18340
19115
|
async function activate(ctx, input) {
|
|
18341
19116
|
const settings = ctx.get("settings");
|
|
@@ -18380,12 +19155,12 @@ async function activate(ctx, input) {
|
|
|
18380
19155
|
hostIdentities,
|
|
18381
19156
|
apiProxy,
|
|
18382
19157
|
logger,
|
|
18383
|
-
|
|
19158
|
+
localTypertGateway,
|
|
18384
19159
|
() => ctx.get("fileViewerHost")
|
|
18385
19160
|
);
|
|
18386
19161
|
let clientRuntime;
|
|
18387
19162
|
const hostControl = runtime;
|
|
18388
|
-
if (config.serverUrl !== void 0 &&
|
|
19163
|
+
if (config.serverUrl !== void 0 && connection !== void 0) {
|
|
18389
19164
|
const clientIdentities = new IdentityStore({
|
|
18390
19165
|
directory: serverStorageDirectory(defaultIdentityDirectory, config.serverUrl, "client")
|
|
18391
19166
|
});
|
|
@@ -18412,6 +19187,7 @@ async function activate(ctx, input) {
|
|
|
18412
19187
|
logger.warn("client remote mode is unavailable", {
|
|
18413
19188
|
serverConfigured: config.serverUrl !== void 0,
|
|
18414
19189
|
apiProxyAvailable: apiProxy !== void 0,
|
|
19190
|
+
remoteGatewayAvailable: localTypertGateway.supportsCarrier,
|
|
18415
19191
|
connectionAvailable: connection !== void 0
|
|
18416
19192
|
});
|
|
18417
19193
|
}
|
|
@@ -18461,8 +19237,10 @@ export {
|
|
|
18461
19237
|
ConnectionController,
|
|
18462
19238
|
ConnectionRejectedError,
|
|
18463
19239
|
HARNESS_API_ALLOWLIST,
|
|
19240
|
+
HARNESS_REMOTE_ALLOWLIST,
|
|
18464
19241
|
HOST_CAPABILITIES,
|
|
18465
19242
|
HarnessApiBridge,
|
|
19243
|
+
HarnessRemoteBridge,
|
|
18466
19244
|
HostPluginRuntime,
|
|
18467
19245
|
HostServerApi,
|
|
18468
19246
|
HostServerConnection,
|
|
@@ -18471,6 +19249,7 @@ export {
|
|
|
18471
19249
|
PluginControlRuntime,
|
|
18472
19250
|
RemoteFileViewerBridge,
|
|
18473
19251
|
RemoteHarnessApiProxy,
|
|
19252
|
+
RemoteTypertGateway,
|
|
18474
19253
|
RpcError,
|
|
18475
19254
|
RpcRouter,
|
|
18476
19255
|
ServerApiError,
|