ds-harness-remote 0.3.15 → 0.3.17
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 +29 -10
- package/dist/client-runtime.d.ts +5 -1
- package/dist/client-runtime.d.ts.map +1 -1
- package/dist/client-runtime.js +83 -7
- package/dist/client-runtime.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.github.js +227 -9
- package/dist/client.js +227 -9
- package/dist/client.js.map +1 -1
- package/dist/file-viewer-bridge.d.ts +30 -0
- package/dist/file-viewer-bridge.d.ts.map +1 -0
- package/dist/file-viewer-bridge.js +128 -0
- package/dist/file-viewer-bridge.js.map +1 -0
- package/dist/file-viewer-contract.d.ts +5 -0
- package/dist/file-viewer-contract.d.ts.map +1 -0
- package/dist/file-viewer-contract.js +4 -0
- package/dist/file-viewer-contract.js.map +1 -0
- package/dist/harness-api-bridge.d.ts +3 -1
- package/dist/harness-api-bridge.d.ts.map +1 -1
- package/dist/harness-api-bridge.js +24 -8
- package/dist/harness-api-bridge.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +656 -268
- package/dist/index.js.map +4 -4
- package/dist/remote-file-content-provider.d.ts +28 -0
- package/dist/remote-file-content-provider.d.ts.map +1 -0
- package/dist/remote-file-content-provider.js +69 -0
- package/dist/remote-file-content-provider.js.map +1 -0
- package/dist/rpc-router.d.ts +4 -2
- package/dist/rpc-router.d.ts.map +1 -1
- package/dist/rpc-router.js +11 -2
- package/dist/rpc-router.js.map +1 -1
- package/dist/server-connection.d.ts +2 -1
- package/dist/server-connection.d.ts.map +1 -1
- package/dist/server-connection.js +9 -6
- package/dist/server-connection.js.map +1 -1
- package/dist/service.d.ts +3 -1
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +9 -3
- package/dist/service.js.map +1 -1
- package/dist/typert-gateway-switch.d.ts +1 -1
- package/dist/typert-gateway-switch.d.ts.map +1 -1
- package/dist/typert-gateway-switch.js +7 -2
- package/dist/typert-gateway-switch.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4083,7 +4083,8 @@ var rpcMethods = [
|
|
|
4083
4083
|
"harness.api.call",
|
|
4084
4084
|
"harness.api.respond",
|
|
4085
4085
|
"harness.api.stream.open",
|
|
4086
|
-
"harness.api.stream.close"
|
|
4086
|
+
"harness.api.stream.close",
|
|
4087
|
+
"fileviewer.call"
|
|
4087
4088
|
];
|
|
4088
4089
|
var rpcMethodSchema = external_exports.enum(rpcMethods);
|
|
4089
4090
|
var messageTypeSchema = external_exports.enum(messageTypes);
|
|
@@ -4803,6 +4804,7 @@ var RtcDataChannelTransport = class {
|
|
|
4803
4804
|
bytesSent = 0;
|
|
4804
4805
|
bytesReceived = 0;
|
|
4805
4806
|
selected;
|
|
4807
|
+
selectedMode;
|
|
4806
4808
|
lastBufferedAmount = 0;
|
|
4807
4809
|
constructor(options) {
|
|
4808
4810
|
this.role = options.role;
|
|
@@ -4879,10 +4881,27 @@ var RtcDataChannelTransport = class {
|
|
|
4879
4881
|
selectedTransport() {
|
|
4880
4882
|
return this.selected;
|
|
4881
4883
|
}
|
|
4884
|
+
selectedPathMode() {
|
|
4885
|
+
return this.selectedMode;
|
|
4886
|
+
}
|
|
4887
|
+
async connectionDetails() {
|
|
4888
|
+
let statsDetails = {};
|
|
4889
|
+
try {
|
|
4890
|
+
statsDetails = inspectSelectedPath(await this.pc.getStats());
|
|
4891
|
+
} catch {
|
|
4892
|
+
}
|
|
4893
|
+
return {
|
|
4894
|
+
...statsDetails,
|
|
4895
|
+
mode: statsDetails.mode ?? this.selectedMode,
|
|
4896
|
+
connectionState: this.pc.connectionState,
|
|
4897
|
+
iceConnectionState: this.pc.iceConnectionState,
|
|
4898
|
+
dataChannelState: this.channel?.readyState
|
|
4899
|
+
};
|
|
4900
|
+
}
|
|
4882
4901
|
getStats() {
|
|
4883
4902
|
const connected = this.channel?.readyState === "open";
|
|
4884
4903
|
return {
|
|
4885
|
-
mode: !connected ? "Disconnected" : this.selected === "turn" ? "TURN" : "P2P",
|
|
4904
|
+
mode: !connected ? "Disconnected" : this.selectedMode ?? (this.selected === "turn" ? "TURN" : "P2P"),
|
|
4886
4905
|
connected,
|
|
4887
4906
|
bytesSent: this.bytesSent,
|
|
4888
4907
|
bytesReceived: this.bytesReceived
|
|
@@ -5043,9 +5062,12 @@ var RtcDataChannelTransport = class {
|
|
|
5043
5062
|
}
|
|
5044
5063
|
async resolveSelectedTransport() {
|
|
5045
5064
|
try {
|
|
5046
|
-
|
|
5065
|
+
const selected = inspectSelectedPath(await this.pc.getStats());
|
|
5066
|
+
this.selected = selected?.transport;
|
|
5067
|
+
this.selectedMode = selected?.mode;
|
|
5047
5068
|
} catch {
|
|
5048
5069
|
this.selected = void 0;
|
|
5070
|
+
this.selectedMode = void 0;
|
|
5049
5071
|
}
|
|
5050
5072
|
}
|
|
5051
5073
|
checkConnectionState() {
|
|
@@ -5119,28 +5141,73 @@ var RtcConnectError = class extends Error {
|
|
|
5119
5141
|
this.code = code;
|
|
5120
5142
|
}
|
|
5121
5143
|
};
|
|
5122
|
-
function
|
|
5123
|
-
const
|
|
5144
|
+
function inspectSelectedPath(stats) {
|
|
5145
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
5146
|
+
const candidatePairs = /* @__PURE__ */ new Map();
|
|
5124
5147
|
const selectedPairs = [];
|
|
5125
|
-
|
|
5148
|
+
const selectedPairIds = /* @__PURE__ */ new Set();
|
|
5149
|
+
for (const [reportId, entry] of stats) {
|
|
5126
5150
|
if (entry.type === "local-candidate" || entry.type === "remote-candidate") {
|
|
5127
5151
|
if (typeof entry.candidateType === "string" && entry.id !== void 0) {
|
|
5128
|
-
|
|
5152
|
+
candidates.set(String(entry.id), entry);
|
|
5129
5153
|
}
|
|
5130
|
-
} else if (entry.type === "candidate-pair"
|
|
5154
|
+
} else if (entry.type === "candidate-pair") {
|
|
5155
|
+
candidatePairs.set(String(entry.id ?? reportId), entry);
|
|
5131
5156
|
if (entry.selected === true || entry.nominated === true)
|
|
5132
5157
|
selectedPairs.push(entry);
|
|
5133
|
-
}
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5158
|
+
} else if (entry.type === "transport" && typeof entry.selectedCandidatePairId === "string") {
|
|
5159
|
+
selectedPairIds.add(entry.selectedCandidatePairId);
|
|
5160
|
+
}
|
|
5161
|
+
}
|
|
5162
|
+
const pairs = [
|
|
5163
|
+
...[...selectedPairIds].map((id) => candidatePairs.get(id)).filter((pair) => pair !== void 0),
|
|
5164
|
+
...selectedPairs
|
|
5165
|
+
];
|
|
5166
|
+
for (const pair of pairs) {
|
|
5167
|
+
const local = candidates.get(String(pair.localCandidateId));
|
|
5168
|
+
const remote = candidates.get(String(pair.remoteCandidateId));
|
|
5169
|
+
const localType = local?.candidateType;
|
|
5170
|
+
const remoteType = remote?.candidateType;
|
|
5171
|
+
let selected;
|
|
5172
|
+
if (localType === "relay" || remoteType === "relay")
|
|
5173
|
+
selected = { transport: "turn", mode: "TURN" };
|
|
5174
|
+
if (local === void 0 && remote === void 0)
|
|
5175
|
+
continue;
|
|
5176
|
+
selected ??= {
|
|
5177
|
+
transport: "p2p",
|
|
5178
|
+
mode: localType === "host" && remoteType === "host" ? "LAN" : "P2P"
|
|
5179
|
+
};
|
|
5180
|
+
const currentRoundTripTime = numberStat(pair, "currentRoundTripTime");
|
|
5181
|
+
return {
|
|
5182
|
+
...selected,
|
|
5183
|
+
localCandidateType: stringStat(local, "candidateType"),
|
|
5184
|
+
remoteCandidateType: stringStat(remote, "candidateType"),
|
|
5185
|
+
localAddress: candidateAddress(local),
|
|
5186
|
+
remoteAddress: candidateAddress(remote),
|
|
5187
|
+
protocol: stringStat(local, "protocol") ?? stringStat(remote, "protocol"),
|
|
5188
|
+
relayProtocol: stringStat(local, "relayProtocol") ?? stringStat(remote, "relayProtocol"),
|
|
5189
|
+
currentRoundTripTimeMs: currentRoundTripTime === void 0 ? void 0 : Math.round(currentRoundTripTime * 1e3),
|
|
5190
|
+
availableOutgoingBitrate: numberStat(pair, "availableOutgoingBitrate"),
|
|
5191
|
+
bytesSent: numberStat(pair, "bytesSent"),
|
|
5192
|
+
bytesReceived: numberStat(pair, "bytesReceived")
|
|
5193
|
+
};
|
|
5142
5194
|
}
|
|
5143
|
-
return
|
|
5195
|
+
return {};
|
|
5196
|
+
}
|
|
5197
|
+
function candidateAddress(candidate) {
|
|
5198
|
+
const address = stringStat(candidate, "address") ?? stringStat(candidate, "ip");
|
|
5199
|
+
if (address === void 0)
|
|
5200
|
+
return void 0;
|
|
5201
|
+
const port = numberStat(candidate, "port");
|
|
5202
|
+
return port === void 0 ? address : `${address}:${port}`;
|
|
5203
|
+
}
|
|
5204
|
+
function stringStat(entry, key) {
|
|
5205
|
+
const value = entry?.[key];
|
|
5206
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
5207
|
+
}
|
|
5208
|
+
function numberStat(entry, key) {
|
|
5209
|
+
const value = entry?.[key];
|
|
5210
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
5144
5211
|
}
|
|
5145
5212
|
function requireSdp(description) {
|
|
5146
5213
|
if (typeof description.sdp !== "string" || description.sdp.length === 0) {
|
|
@@ -5159,7 +5226,7 @@ function asError(error) {
|
|
|
5159
5226
|
|
|
5160
5227
|
// ../webrtc/dist/adaptive-transport.js
|
|
5161
5228
|
var DEFAULT_CAPABILITIES = ["transport.p2p", "transport.turn", "transport.relay", "harness.api.v1"];
|
|
5162
|
-
var DEFAULT_PREFERRED_TRANSPORTS = ["p2p", "turn", "relay"];
|
|
5229
|
+
var DEFAULT_PREFERRED_TRANSPORTS = ["lan", "p2p", "turn", "relay"];
|
|
5163
5230
|
var AdaptiveTransport = class extends BaseTransport {
|
|
5164
5231
|
url;
|
|
5165
5232
|
options;
|
|
@@ -5177,6 +5244,7 @@ var AdaptiveTransport = class extends BaseTransport {
|
|
|
5177
5244
|
handshakeTimer;
|
|
5178
5245
|
webrtcEnabled = true;
|
|
5179
5246
|
serverNegotiateTimeoutMs;
|
|
5247
|
+
connectedAt;
|
|
5180
5248
|
constructor(url, options) {
|
|
5181
5249
|
super();
|
|
5182
5250
|
this.url = url;
|
|
@@ -5205,10 +5273,10 @@ var AdaptiveTransport = class extends BaseTransport {
|
|
|
5205
5273
|
});
|
|
5206
5274
|
};
|
|
5207
5275
|
socket.onerror = () => this.failConnection(new Error(`AdaptiveTransport failed to connect to ${this.url}`));
|
|
5208
|
-
socket.onclose = () => {
|
|
5276
|
+
socket.onclose = (event) => {
|
|
5209
5277
|
const pending = this.readyReject !== void 0;
|
|
5210
5278
|
if (pending)
|
|
5211
|
-
this.failConnection(
|
|
5279
|
+
this.failConnection(adaptiveControlCloseError(event));
|
|
5212
5280
|
this.socket = void 0;
|
|
5213
5281
|
this.connectionId = void 0;
|
|
5214
5282
|
this.emitClose();
|
|
@@ -5246,6 +5314,16 @@ var AdaptiveTransport = class extends BaseTransport {
|
|
|
5246
5314
|
remoteDeviceId: this.options.targetDeviceId
|
|
5247
5315
|
};
|
|
5248
5316
|
}
|
|
5317
|
+
async connectionDetails() {
|
|
5318
|
+
return {
|
|
5319
|
+
...this.connectionId === void 0 ? {} : { connectionId: this.connectionId },
|
|
5320
|
+
...this.connectedAt === void 0 ? {} : { connectedAt: this.connectedAt },
|
|
5321
|
+
controlChannelUrl: this.url,
|
|
5322
|
+
controlChannelState: socketState(this.socket?.readyState),
|
|
5323
|
+
preferredTransports: this.options.forceRelay === true ? ["relay"] : [...this.options.preferredTransports ?? DEFAULT_PREFERRED_TRANSPORTS],
|
|
5324
|
+
...this.rtc === void 0 ? {} : { webRtc: await this.rtc.connectionDetails() }
|
|
5325
|
+
};
|
|
5326
|
+
}
|
|
5249
5327
|
async sendHandshake(step, data) {
|
|
5250
5328
|
if (this.socket?.readyState !== WebSocket.OPEN || this.connectionId === void 0) {
|
|
5251
5329
|
throw new Error("adaptive transport has not been authorized");
|
|
@@ -5276,10 +5354,9 @@ var AdaptiveTransport = class extends BaseTransport {
|
|
|
5276
5354
|
let mode = "Disconnected";
|
|
5277
5355
|
if (this.selected === "relay")
|
|
5278
5356
|
mode = relayConnected ? "Relay" : "Disconnected";
|
|
5279
|
-
else if (this.selected === "turn")
|
|
5280
|
-
mode = webrtcConnected ?
|
|
5281
|
-
|
|
5282
|
-
mode = webrtcConnected ? "P2P" : "Disconnected";
|
|
5357
|
+
else if (this.selected === "turn" || this.selected === "p2p") {
|
|
5358
|
+
mode = webrtcConnected ? this.rtc.getStats().mode : "Disconnected";
|
|
5359
|
+
}
|
|
5283
5360
|
return { mode, connected, bytesSent: this.bytesSent, bytesReceived: this.bytesReceived };
|
|
5284
5361
|
}
|
|
5285
5362
|
async handleSocketMessage(raw) {
|
|
@@ -5365,14 +5442,19 @@ var AdaptiveTransport = class extends BaseTransport {
|
|
|
5365
5442
|
if (this.connectionId === void 0)
|
|
5366
5443
|
return;
|
|
5367
5444
|
const preferred = this.options.preferredTransports ?? [...DEFAULT_PREFERRED_TRANSPORTS];
|
|
5368
|
-
const wantWebRtc = !this.options.forceRelay && this.webrtcEnabled && (preferred.includes("p2p") || preferred.includes("turn"));
|
|
5445
|
+
const wantWebRtc = !this.options.forceRelay && this.webrtcEnabled && (preferred.includes("lan") || preferred.includes("p2p") || preferred.includes("turn"));
|
|
5369
5446
|
let selected = "relay";
|
|
5370
5447
|
if (wantWebRtc) {
|
|
5371
5448
|
try {
|
|
5372
5449
|
const rtcSelected = await this.tryWebRtc();
|
|
5373
5450
|
this.dataMode = "webrtc";
|
|
5374
5451
|
selected = rtcSelected;
|
|
5375
|
-
} catch {
|
|
5452
|
+
} catch (error) {
|
|
5453
|
+
const reason = error instanceof Error ? error : new Error("WebRTC negotiation failed.");
|
|
5454
|
+
try {
|
|
5455
|
+
this.options.onWebRtcFallback?.(reason);
|
|
5456
|
+
} catch {
|
|
5457
|
+
}
|
|
5376
5458
|
await this.rtc?.close();
|
|
5377
5459
|
this.rtc = void 0;
|
|
5378
5460
|
this.dataMode = "relay";
|
|
@@ -5457,6 +5539,7 @@ var AdaptiveTransport = class extends BaseTransport {
|
|
|
5457
5539
|
}
|
|
5458
5540
|
finishConnection() {
|
|
5459
5541
|
this.clearHandshake();
|
|
5542
|
+
this.connectedAt ??= Date.now();
|
|
5460
5543
|
this.readyResolve?.();
|
|
5461
5544
|
this.readyResolve = void 0;
|
|
5462
5545
|
this.readyReject = void 0;
|
|
@@ -5479,9 +5562,23 @@ var AdaptiveTransport = class extends BaseTransport {
|
|
|
5479
5562
|
this.handshakeTimer = void 0;
|
|
5480
5563
|
}
|
|
5481
5564
|
};
|
|
5565
|
+
function adaptiveControlCloseError(event) {
|
|
5566
|
+
const reason = event.reason.trim().slice(0, 300);
|
|
5567
|
+
const detail = reason.length > 0 ? `${reason} (WebSocket ${event.code})` : `WebSocket closed with code ${event.code}`;
|
|
5568
|
+
return new Error(`Adaptive control channel closed before it was ready: ${detail}`);
|
|
5569
|
+
}
|
|
5482
5570
|
function isObject(value) {
|
|
5483
5571
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5484
5572
|
}
|
|
5573
|
+
function socketState(readyState) {
|
|
5574
|
+
if (readyState === 0)
|
|
5575
|
+
return "connecting";
|
|
5576
|
+
if (readyState === 1)
|
|
5577
|
+
return "open";
|
|
5578
|
+
if (readyState === 2)
|
|
5579
|
+
return "closing";
|
|
5580
|
+
return "closed";
|
|
5581
|
+
}
|
|
5485
5582
|
|
|
5486
5583
|
// src/api-proxy-switch.ts
|
|
5487
5584
|
var SWITCHED_DOMAINS = [
|
|
@@ -12968,7 +13065,7 @@ function normalizeServerUrl(value) {
|
|
|
12968
13065
|
}
|
|
12969
13066
|
|
|
12970
13067
|
// src/version.ts
|
|
12971
|
-
var PLUGIN_VERSION = "0.3.
|
|
13068
|
+
var PLUGIN_VERSION = "0.3.17";
|
|
12972
13069
|
|
|
12973
13070
|
// src/server-api.ts
|
|
12974
13071
|
var HostServerApi = class {
|
|
@@ -13353,6 +13450,7 @@ function requireRecord(value, name2) {
|
|
|
13353
13450
|
}
|
|
13354
13451
|
|
|
13355
13452
|
// src/typert-gateway-switch.ts
|
|
13453
|
+
var REMOTE_COMMAND_METHODS = ["execute", "list"];
|
|
13356
13454
|
var TypertGatewaySwitch = class {
|
|
13357
13455
|
constructor(gateway) {
|
|
13358
13456
|
this.gateway = gateway;
|
|
@@ -13369,7 +13467,7 @@ var TypertGatewaySwitch = class {
|
|
|
13369
13467
|
}
|
|
13370
13468
|
install() {
|
|
13371
13469
|
if (this.installed) return;
|
|
13372
|
-
this.gateway.invoke = (request) => request.namespace === "commands" && request.method
|
|
13470
|
+
this.gateway.invoke = (request) => request.namespace === "commands" && isRemoteCommandMethod(request.method) ? (this.remoteInvoke ?? this.localInvoke)(request) : this.localInvoke(request);
|
|
13373
13471
|
this.installed = true;
|
|
13374
13472
|
}
|
|
13375
13473
|
selectRemote(invoke) {
|
|
@@ -13386,15 +13484,212 @@ var TypertGatewaySwitch = class {
|
|
|
13386
13484
|
this.installed = false;
|
|
13387
13485
|
}
|
|
13388
13486
|
};
|
|
13487
|
+
function isRemoteCommandMethod(method) {
|
|
13488
|
+
return REMOTE_COMMAND_METHODS.includes(method);
|
|
13489
|
+
}
|
|
13490
|
+
|
|
13491
|
+
// src/werift-rtc.ts
|
|
13492
|
+
import { networkInterfaces } from "node:os";
|
|
13493
|
+
var cachedFactory;
|
|
13494
|
+
async function loadWeriftFactory() {
|
|
13495
|
+
if (cachedFactory !== void 0) return cachedFactory;
|
|
13496
|
+
try {
|
|
13497
|
+
const werift = await import("werift");
|
|
13498
|
+
cachedFactory = buildWeriftFactory(werift);
|
|
13499
|
+
return cachedFactory;
|
|
13500
|
+
} catch {
|
|
13501
|
+
return void 0;
|
|
13502
|
+
}
|
|
13503
|
+
}
|
|
13504
|
+
function buildWeriftFactory(werift) {
|
|
13505
|
+
return {
|
|
13506
|
+
create(configuration) {
|
|
13507
|
+
const hostIpv4 = detectHostIpv4();
|
|
13508
|
+
const raw = new werift.RTCPeerConnection({
|
|
13509
|
+
iceServers: configuration.iceServers ?? [],
|
|
13510
|
+
iceUseIpv6: false,
|
|
13511
|
+
iceUseLinkLocalAddress: false,
|
|
13512
|
+
...hostIpv4 === void 0 ? {} : { iceInterfaceAddresses: { udp4: hostIpv4 } }
|
|
13513
|
+
});
|
|
13514
|
+
let onIceCandidate = null;
|
|
13515
|
+
let onDataChannel = null;
|
|
13516
|
+
raw.onicecandidate = (event) => {
|
|
13517
|
+
if (onIceCandidate === null) return;
|
|
13518
|
+
onIceCandidate({ candidate: event.candidate === void 0 ? null : event.candidate.toJSON() });
|
|
13519
|
+
};
|
|
13520
|
+
raw.ondatachannel = (event) => {
|
|
13521
|
+
if (onDataChannel === null) return;
|
|
13522
|
+
onDataChannel({ channel: adaptDataChannel2(event.channel) });
|
|
13523
|
+
};
|
|
13524
|
+
const pc = {
|
|
13525
|
+
get connectionState() {
|
|
13526
|
+
return raw.connectionState;
|
|
13527
|
+
},
|
|
13528
|
+
get iceConnectionState() {
|
|
13529
|
+
return raw.iceConnectionState;
|
|
13530
|
+
},
|
|
13531
|
+
get iceGatheringState() {
|
|
13532
|
+
return raw.iceGatheringState;
|
|
13533
|
+
},
|
|
13534
|
+
get signalingState() {
|
|
13535
|
+
return raw.signalingState;
|
|
13536
|
+
},
|
|
13537
|
+
set onconnectionstatechange(value) {
|
|
13538
|
+
raw.onconnectionstatechange = value;
|
|
13539
|
+
},
|
|
13540
|
+
get onconnectionstatechange() {
|
|
13541
|
+
return raw.onconnectionstatechange;
|
|
13542
|
+
},
|
|
13543
|
+
set oniceconnectionstatechange(value) {
|
|
13544
|
+
raw.oniceconnectionstatechange = value;
|
|
13545
|
+
},
|
|
13546
|
+
get oniceconnectionstatechange() {
|
|
13547
|
+
return raw.oniceconnectionstatechange;
|
|
13548
|
+
},
|
|
13549
|
+
set onicegatheringstatechange(value) {
|
|
13550
|
+
raw.onicegatheringstatechange = value;
|
|
13551
|
+
},
|
|
13552
|
+
get onicegatheringstatechange() {
|
|
13553
|
+
return raw.onicegatheringstatechange;
|
|
13554
|
+
},
|
|
13555
|
+
set onicecandidate(value) {
|
|
13556
|
+
onIceCandidate = value;
|
|
13557
|
+
},
|
|
13558
|
+
get onicecandidate() {
|
|
13559
|
+
return onIceCandidate;
|
|
13560
|
+
},
|
|
13561
|
+
set ondatachannel(value) {
|
|
13562
|
+
onDataChannel = value;
|
|
13563
|
+
},
|
|
13564
|
+
get ondatachannel() {
|
|
13565
|
+
return onDataChannel;
|
|
13566
|
+
},
|
|
13567
|
+
createDataChannel(label, options) {
|
|
13568
|
+
return adaptDataChannel2(raw.createDataChannel(label, { ordered: options?.ordered ?? true }));
|
|
13569
|
+
},
|
|
13570
|
+
createOffer() {
|
|
13571
|
+
return raw.createOffer();
|
|
13572
|
+
},
|
|
13573
|
+
createAnswer() {
|
|
13574
|
+
return raw.createAnswer();
|
|
13575
|
+
},
|
|
13576
|
+
setLocalDescription(description) {
|
|
13577
|
+
return raw.setLocalDescription(description).then(() => void 0);
|
|
13578
|
+
},
|
|
13579
|
+
setRemoteDescription(description) {
|
|
13580
|
+
return raw.setRemoteDescription(description);
|
|
13581
|
+
},
|
|
13582
|
+
addIceCandidate(candidate) {
|
|
13583
|
+
return raw.addIceCandidate(candidate);
|
|
13584
|
+
},
|
|
13585
|
+
getStats() {
|
|
13586
|
+
return raw.getStats();
|
|
13587
|
+
},
|
|
13588
|
+
close() {
|
|
13589
|
+
void raw.close().catch(() => void 0);
|
|
13590
|
+
}
|
|
13591
|
+
};
|
|
13592
|
+
return pc;
|
|
13593
|
+
}
|
|
13594
|
+
};
|
|
13595
|
+
}
|
|
13596
|
+
function adaptDataChannel2(raw) {
|
|
13597
|
+
return {
|
|
13598
|
+
get label() {
|
|
13599
|
+
return raw.label;
|
|
13600
|
+
},
|
|
13601
|
+
get ordered() {
|
|
13602
|
+
return raw.ordered;
|
|
13603
|
+
},
|
|
13604
|
+
get readyState() {
|
|
13605
|
+
return raw.readyState;
|
|
13606
|
+
},
|
|
13607
|
+
get bufferedAmount() {
|
|
13608
|
+
return raw.bufferedAmount;
|
|
13609
|
+
},
|
|
13610
|
+
binaryType: "arraybuffer",
|
|
13611
|
+
set onopen(value) {
|
|
13612
|
+
raw.onopen = value;
|
|
13613
|
+
},
|
|
13614
|
+
get onopen() {
|
|
13615
|
+
return raw.onopen;
|
|
13616
|
+
},
|
|
13617
|
+
set onmessage(value) {
|
|
13618
|
+
raw.onmessage = value === null ? null : (event) => value({ data: toArrayBuffer2(event.data) });
|
|
13619
|
+
},
|
|
13620
|
+
get onmessage() {
|
|
13621
|
+
return null;
|
|
13622
|
+
},
|
|
13623
|
+
set onclose(value) {
|
|
13624
|
+
raw.onclose = value;
|
|
13625
|
+
},
|
|
13626
|
+
get onclose() {
|
|
13627
|
+
return raw.onclose;
|
|
13628
|
+
},
|
|
13629
|
+
set onerror(value) {
|
|
13630
|
+
raw.onerror = value;
|
|
13631
|
+
},
|
|
13632
|
+
get onerror() {
|
|
13633
|
+
return raw.onerror;
|
|
13634
|
+
},
|
|
13635
|
+
onbufferedamountlow: null,
|
|
13636
|
+
send(data) {
|
|
13637
|
+
const bytes = typeof data === "string" ? Buffer.byteLength(data) : data.byteLength;
|
|
13638
|
+
try {
|
|
13639
|
+
raw.send(typeof data === "string" ? data : Buffer.from(data));
|
|
13640
|
+
} catch (error) {
|
|
13641
|
+
console.error("[werift-send-error] bytes=" + bytes, error instanceof Error ? error.message : error);
|
|
13642
|
+
throw error;
|
|
13643
|
+
}
|
|
13644
|
+
},
|
|
13645
|
+
close() {
|
|
13646
|
+
raw.close();
|
|
13647
|
+
}
|
|
13648
|
+
};
|
|
13649
|
+
}
|
|
13650
|
+
function toArrayBuffer2(data) {
|
|
13651
|
+
if (typeof data === "string") return data;
|
|
13652
|
+
return data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
|
|
13653
|
+
}
|
|
13654
|
+
function detectHostIpv4() {
|
|
13655
|
+
const preferred = [];
|
|
13656
|
+
const fallback = [];
|
|
13657
|
+
for (const [name2, addresses] of Object.entries(networkInterfaces())) {
|
|
13658
|
+
if (isVirtualInterface(name2)) continue;
|
|
13659
|
+
for (const address of addresses ?? []) {
|
|
13660
|
+
if (address.internal || address.family !== "IPv4") continue;
|
|
13661
|
+
const ip = address.address;
|
|
13662
|
+
if (ip.startsWith("127.") || ip.startsWith("169.254.") || isCgnat(ip)) continue;
|
|
13663
|
+
if (isPrivate(ip)) preferred.push(ip);
|
|
13664
|
+
else fallback.push(ip);
|
|
13665
|
+
}
|
|
13666
|
+
}
|
|
13667
|
+
return preferred[0] ?? fallback[0];
|
|
13668
|
+
}
|
|
13669
|
+
function isVirtualInterface(name2) {
|
|
13670
|
+
return /^(utun|ppp|bridge|awdl|llw|gif|stf|anpi|ap\d|en[1-9]\d*$)/i.test(name2);
|
|
13671
|
+
}
|
|
13672
|
+
function isCgnat(ip) {
|
|
13673
|
+
const parts = ip.split(".").map(Number);
|
|
13674
|
+
return parts.length === 4 && parts[0] === 100 && parts[1] >= 64 && parts[1] <= 127;
|
|
13675
|
+
}
|
|
13676
|
+
function isPrivate(ip) {
|
|
13677
|
+
const parts = ip.split(".").map(Number);
|
|
13678
|
+
if (parts.length !== 4) return false;
|
|
13679
|
+
if (parts[0] === 10) return true;
|
|
13680
|
+
if (parts[0] === 192 && parts[1] === 168) return true;
|
|
13681
|
+
return parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31;
|
|
13682
|
+
}
|
|
13389
13683
|
|
|
13390
13684
|
// src/client-runtime.ts
|
|
13391
13685
|
var ClientModeRuntime = class {
|
|
13392
|
-
constructor(config, identities, server, apiProxy, typertGateway, logger, host) {
|
|
13686
|
+
constructor(config, identities, server, apiProxy, typertGateway, logger, host, rtcFactoryProvider = loadWeriftFactory) {
|
|
13393
13687
|
this.config = config;
|
|
13394
13688
|
this.identities = identities;
|
|
13395
13689
|
this.server = server;
|
|
13396
13690
|
this.logger = logger;
|
|
13397
13691
|
this.host = host;
|
|
13692
|
+
this.rtcFactoryProvider = rtcFactoryProvider;
|
|
13398
13693
|
this.proxySwitch = new ApiProxySwitch(apiProxy);
|
|
13399
13694
|
this.gatewaySwitch = new TypertGatewaySwitch(typertGateway);
|
|
13400
13695
|
}
|
|
@@ -13432,6 +13727,28 @@ var ClientModeRuntime = class {
|
|
|
13432
13727
|
...this.host === void 0 ? {} : { host: this.host.hostStatus() }
|
|
13433
13728
|
};
|
|
13434
13729
|
}
|
|
13730
|
+
async detailedStatus() {
|
|
13731
|
+
const connected = this.connected;
|
|
13732
|
+
if (connected === void 0 || this.identity === void 0) return this.status();
|
|
13733
|
+
const details = await connected.transport.connectionDetails();
|
|
13734
|
+
if (this.connected !== connected) return this.status();
|
|
13735
|
+
return {
|
|
13736
|
+
...this.status(),
|
|
13737
|
+
network: {
|
|
13738
|
+
...details,
|
|
13739
|
+
local: {
|
|
13740
|
+
deviceId: this.identity.deviceId,
|
|
13741
|
+
name: this.identity.name,
|
|
13742
|
+
platform: process.platform
|
|
13743
|
+
},
|
|
13744
|
+
remote: {
|
|
13745
|
+
deviceId: connected.target.deviceId,
|
|
13746
|
+
name: connected.target.name,
|
|
13747
|
+
platform: connected.target.platform
|
|
13748
|
+
}
|
|
13749
|
+
}
|
|
13750
|
+
};
|
|
13751
|
+
}
|
|
13435
13752
|
async devices() {
|
|
13436
13753
|
this.requireIdentity();
|
|
13437
13754
|
const serverDevices = await this.server.listDevices();
|
|
@@ -13557,6 +13874,13 @@ var ClientModeRuntime = class {
|
|
|
13557
13874
|
this.proxySwitch.restore();
|
|
13558
13875
|
this.gatewaySwitch.restore();
|
|
13559
13876
|
}
|
|
13877
|
+
async callRemoteFileViewer(endpoint, payload, signal) {
|
|
13878
|
+
const remote = this.connected;
|
|
13879
|
+
if (remote === void 0 || this.proxySwitch.status().mode !== "remote") {
|
|
13880
|
+
throw new ClientModeError("REMOTE_NOT_CONNECTED", "No Remote Host is selected.", true);
|
|
13881
|
+
}
|
|
13882
|
+
return remote.client.rpc("fileviewer.call", { endpoint, payload }, signal);
|
|
13883
|
+
}
|
|
13560
13884
|
async connect(targetDeviceId, signal) {
|
|
13561
13885
|
signal?.throwIfAborted();
|
|
13562
13886
|
const identity = this.requireIdentity();
|
|
@@ -13568,19 +13892,43 @@ var ClientModeRuntime = class {
|
|
|
13568
13892
|
const presence = await this.server.presenceFor(targetDeviceId);
|
|
13569
13893
|
if (!presence.online) throw new ClientModeError("HOST_OFFLINE", "The selected Host is offline.", true);
|
|
13570
13894
|
const credentials = await this.server.authenticate(identity);
|
|
13571
|
-
const
|
|
13572
|
-
|
|
13573
|
-
|
|
13574
|
-
|
|
13575
|
-
|
|
13576
|
-
|
|
13577
|
-
|
|
13578
|
-
|
|
13579
|
-
|
|
13580
|
-
|
|
13895
|
+
const rtcFactory = this.config.forceRelay ? void 0 : await this.rtcFactoryProvider().catch(() => void 0);
|
|
13896
|
+
let webRtcFallback = false;
|
|
13897
|
+
const createTransport = (relayOnly) => new AdaptiveTransport(
|
|
13898
|
+
websocketUrl(this.server.baseUrl),
|
|
13899
|
+
{
|
|
13900
|
+
role: "client",
|
|
13901
|
+
deviceId: identity.deviceId,
|
|
13902
|
+
accessToken: credentials.accessToken,
|
|
13903
|
+
targetDeviceId,
|
|
13904
|
+
forceRelay: this.config.forceRelay || relayOnly,
|
|
13905
|
+
preferredTransports: this.config.forceRelay || relayOnly ? ["relay"] : ["lan", "p2p", "turn", "relay"],
|
|
13906
|
+
...rtcFactory === void 0 || relayOnly ? {} : { rtcFactory },
|
|
13907
|
+
fetchIceServers: async (connectionId) => this.server.turnCredentials(connectionId),
|
|
13908
|
+
onWebRtcFallback: (error) => {
|
|
13909
|
+
webRtcFallback = true;
|
|
13910
|
+
this.logger.warn("remote Harness WebRTC failed; using relay", {
|
|
13911
|
+
targetDeviceId: shortId(target.deviceId),
|
|
13912
|
+
reason: diagnosticReason(error)
|
|
13913
|
+
});
|
|
13914
|
+
}
|
|
13915
|
+
}
|
|
13916
|
+
);
|
|
13917
|
+
let transport = createTransport(false);
|
|
13918
|
+
let client = new RemoteClientCore(new ClientSecureTransport(transport, identity, target), 6e4);
|
|
13581
13919
|
try {
|
|
13582
13920
|
await client.connect();
|
|
13583
13921
|
signal?.throwIfAborted();
|
|
13922
|
+
if (webRtcFallback) {
|
|
13923
|
+
await client.close();
|
|
13924
|
+
transport = createTransport(true);
|
|
13925
|
+
client = new RemoteClientCore(new ClientSecureTransport(transport, identity, target), 6e4);
|
|
13926
|
+
await client.connect();
|
|
13927
|
+
signal?.throwIfAborted();
|
|
13928
|
+
this.logger.info("remote Harness relay fallback re-established", {
|
|
13929
|
+
targetDeviceId: shortId(target.deviceId)
|
|
13930
|
+
});
|
|
13931
|
+
}
|
|
13584
13932
|
client.onClose(() => {
|
|
13585
13933
|
if (this.connected?.client !== client) return;
|
|
13586
13934
|
this.connected = void 0;
|
|
@@ -13591,7 +13939,11 @@ var ClientModeRuntime = class {
|
|
|
13591
13939
|
targetDeviceId: shortId(target.deviceId)
|
|
13592
13940
|
});
|
|
13593
13941
|
});
|
|
13594
|
-
|
|
13942
|
+
this.logger.info("remote Harness transport ready", {
|
|
13943
|
+
targetDeviceId: shortId(target.deviceId),
|
|
13944
|
+
transport: client.getStats().mode
|
|
13945
|
+
});
|
|
13946
|
+
return { client, target, transport };
|
|
13595
13947
|
} catch (error) {
|
|
13596
13948
|
await client.close().catch(() => void 0);
|
|
13597
13949
|
throw error;
|
|
@@ -13607,7 +13959,7 @@ var ClientModeRuntime = class {
|
|
|
13607
13959
|
}
|
|
13608
13960
|
async handleControl(endpoint, payload, signal) {
|
|
13609
13961
|
try {
|
|
13610
|
-
if (endpoint === "status") return ok(this.
|
|
13962
|
+
if (endpoint === "status") return ok(await this.detailedStatus());
|
|
13611
13963
|
if (endpoint === "devices") return ok(await this.devices());
|
|
13612
13964
|
if (endpoint === "client.account.login") {
|
|
13613
13965
|
const value = record(payload);
|
|
@@ -13645,6 +13997,10 @@ var ClientModeRuntime = class {
|
|
|
13645
13997
|
}
|
|
13646
13998
|
return ok(await this.openRemoteWorkspace(value.targetDeviceId, value.path, signal));
|
|
13647
13999
|
}
|
|
14000
|
+
if (endpoint === "fileviewer.stat" || endpoint === "fileviewer.readRange" || endpoint === "fileviewer.list") {
|
|
14001
|
+
const method = endpoint === "fileviewer.stat" ? "stat" : endpoint === "fileviewer.readRange" ? "readRange" : "list";
|
|
14002
|
+
return ok(await this.callRemoteFileViewer(method, payload, signal));
|
|
14003
|
+
}
|
|
13648
14004
|
if (endpoint === "host.account.login") {
|
|
13649
14005
|
if (this.host === void 0) throw new ClientModeError("METHOD_NOT_ALLOWED", "This plugin is not running as a Host.");
|
|
13650
14006
|
const value = record(payload);
|
|
@@ -13767,6 +14123,11 @@ function fail(error) {
|
|
|
13767
14123
|
function shortId(value) {
|
|
13768
14124
|
return value.length <= 12 ? value : `${value.slice(0, 8)}\u2026${value.slice(-4)}`;
|
|
13769
14125
|
}
|
|
14126
|
+
function diagnosticReason(error) {
|
|
14127
|
+
const code = "code" in error && typeof error.code === "string" ? error.code : void 0;
|
|
14128
|
+
const message = error.message.replace(/[\r\n\t]+/g, " ").slice(0, 240);
|
|
14129
|
+
return code === void 0 ? message : `${code}: ${message}`;
|
|
14130
|
+
}
|
|
13770
14131
|
|
|
13771
14132
|
// src/control-runtime.ts
|
|
13772
14133
|
import { hostname as hostname2 } from "node:os";
|
|
@@ -14387,7 +14748,7 @@ var ConnectionController = class {
|
|
|
14387
14748
|
} catch (error) {
|
|
14388
14749
|
this.logger?.warn("peer message handling failed; disconnecting", {
|
|
14389
14750
|
peerDeviceId: shortId2(connection.channel.peerDeviceId),
|
|
14390
|
-
reason:
|
|
14751
|
+
reason: diagnosticReason2(error)
|
|
14391
14752
|
});
|
|
14392
14753
|
await this.disconnect(connection);
|
|
14393
14754
|
}
|
|
@@ -14405,7 +14766,7 @@ var ConnectionController = class {
|
|
|
14405
14766
|
this.logger?.warn("peer send failed; disconnecting", {
|
|
14406
14767
|
peerDeviceId: shortId2(connection.channel.peerDeviceId),
|
|
14407
14768
|
messageType: message.type,
|
|
14408
|
-
reason:
|
|
14769
|
+
reason: diagnosticReason2(error)
|
|
14409
14770
|
});
|
|
14410
14771
|
await this.disconnect(connection);
|
|
14411
14772
|
}
|
|
@@ -14435,7 +14796,7 @@ var ConnectionRejectedError = class extends Error {
|
|
|
14435
14796
|
this.code = code;
|
|
14436
14797
|
}
|
|
14437
14798
|
};
|
|
14438
|
-
function
|
|
14799
|
+
function diagnosticReason2(error) {
|
|
14439
14800
|
const message = error instanceof Error ? error.message : String(error);
|
|
14440
14801
|
return message.replace(/[\r\n]+/g, " ").slice(0, 160) || "Unknown peer connection failure.";
|
|
14441
14802
|
}
|
|
@@ -14449,14 +14810,16 @@ var apiMethods = /* @__PURE__ */ new Set([
|
|
|
14449
14810
|
"harness.api.call",
|
|
14450
14811
|
"harness.api.respond",
|
|
14451
14812
|
"harness.api.stream.open",
|
|
14452
|
-
"harness.api.stream.close"
|
|
14813
|
+
"harness.api.stream.close",
|
|
14814
|
+
"fileviewer.call"
|
|
14453
14815
|
]);
|
|
14454
|
-
var HOST_CAPABILITIES = ["harness.api.v1"];
|
|
14816
|
+
var HOST_CAPABILITIES = ["harness.api.v1", "fileviewer.read.v1"];
|
|
14455
14817
|
var RpcRouter = class {
|
|
14456
|
-
constructor(harnessApi, maxPending = 128, logger) {
|
|
14818
|
+
constructor(harnessApi, maxPending = 128, logger, fileViewer) {
|
|
14457
14819
|
this.harnessApi = harnessApi;
|
|
14458
14820
|
this.maxPending = maxPending;
|
|
14459
14821
|
this.logger = logger;
|
|
14822
|
+
this.fileViewer = fileViewer;
|
|
14460
14823
|
}
|
|
14461
14824
|
active = 0;
|
|
14462
14825
|
closePeerStreams() {
|
|
@@ -14491,7 +14854,7 @@ var RpcRouter = class {
|
|
|
14491
14854
|
durationMs: Math.round(performance.now() - startedAt),
|
|
14492
14855
|
code: response.payload.code,
|
|
14493
14856
|
retryable: response.payload.retryable,
|
|
14494
|
-
reason:
|
|
14857
|
+
reason: diagnosticReason3(error)
|
|
14495
14858
|
});
|
|
14496
14859
|
return response;
|
|
14497
14860
|
} finally {
|
|
@@ -14508,6 +14871,12 @@ var RpcRouter = class {
|
|
|
14508
14871
|
return this.harnessApi.openStream(params);
|
|
14509
14872
|
case "harness.api.stream.close":
|
|
14510
14873
|
return this.harnessApi.closeStream(params);
|
|
14874
|
+
case "fileviewer.call": {
|
|
14875
|
+
if (this.fileViewer === void 0) {
|
|
14876
|
+
throw new RpcError("FILE_VIEWER_UNAVAILABLE", "The Remote Host does not have DSH File Viewer available.");
|
|
14877
|
+
}
|
|
14878
|
+
return this.fileViewer.call(params);
|
|
14879
|
+
}
|
|
14511
14880
|
default:
|
|
14512
14881
|
throw new RpcError("METHOD_NOT_FOUND", "The requested method does not exist.");
|
|
14513
14882
|
}
|
|
@@ -14526,15 +14895,135 @@ function errorResponse(requestId, error) {
|
|
|
14526
14895
|
if (error instanceof external_exports.ZodError) return createRpcError(requestId, "INVALID_MESSAGE", "The RPC parameters are invalid.");
|
|
14527
14896
|
return createRpcError(requestId, "INTERNAL_ERROR", "The Host could not complete the request.");
|
|
14528
14897
|
}
|
|
14529
|
-
function
|
|
14898
|
+
function diagnosticReason3(error) {
|
|
14530
14899
|
const message = error instanceof Error ? error.message : String(error);
|
|
14531
14900
|
return message.replace(/[\r\n]+/g, " ").slice(0, 160) || "Unknown Host request failure.";
|
|
14532
14901
|
}
|
|
14533
14902
|
|
|
14903
|
+
// src/file-viewer-contract.ts
|
|
14904
|
+
var REMOTE_FILE_CHUNK_BYTES = 512 * 1024;
|
|
14905
|
+
var MAX_REMOTE_FILE_LOCATOR_CHARS = 4096;
|
|
14906
|
+
var MAX_REMOTE_DIRECTORY_ENTRIES = 1e3;
|
|
14907
|
+
|
|
14908
|
+
// src/file-viewer-bridge.ts
|
|
14909
|
+
var locatorSchema = external_exports.string().min(1).max(MAX_REMOTE_FILE_LOCATOR_CHARS);
|
|
14910
|
+
var callSchema = external_exports.discriminatedUnion("endpoint", [
|
|
14911
|
+
external_exports.object({
|
|
14912
|
+
endpoint: external_exports.literal("stat"),
|
|
14913
|
+
payload: external_exports.object({ path: locatorSchema }).strict()
|
|
14914
|
+
}).strict(),
|
|
14915
|
+
external_exports.object({
|
|
14916
|
+
endpoint: external_exports.literal("readRange"),
|
|
14917
|
+
payload: external_exports.object({
|
|
14918
|
+
path: locatorSchema,
|
|
14919
|
+
offset: external_exports.number().int().nonnegative().safe(),
|
|
14920
|
+
length: external_exports.number().int().positive().max(REMOTE_FILE_CHUNK_BYTES)
|
|
14921
|
+
}).strict()
|
|
14922
|
+
}).strict(),
|
|
14923
|
+
external_exports.object({
|
|
14924
|
+
endpoint: external_exports.literal("list"),
|
|
14925
|
+
payload: external_exports.object({ path: locatorSchema }).strict()
|
|
14926
|
+
}).strict()
|
|
14927
|
+
]);
|
|
14928
|
+
var statResultSchema = external_exports.object({
|
|
14929
|
+
path: locatorSchema,
|
|
14930
|
+
name: external_exports.string().min(1).max(1024),
|
|
14931
|
+
ext: external_exports.string().max(128),
|
|
14932
|
+
mime: external_exports.string().min(1).max(256),
|
|
14933
|
+
size: external_exports.number().int().nonnegative().safe(),
|
|
14934
|
+
mtimeMs: external_exports.number().nonnegative().optional(),
|
|
14935
|
+
isDirectory: external_exports.boolean(),
|
|
14936
|
+
exists: external_exports.boolean()
|
|
14937
|
+
}).strict();
|
|
14938
|
+
var rangeResultSchema = external_exports.object({
|
|
14939
|
+
data: external_exports.string().max(Math.ceil(REMOTE_FILE_CHUNK_BYTES / 3) * 4 + 4),
|
|
14940
|
+
offset: external_exports.number().int().nonnegative().safe(),
|
|
14941
|
+
size: external_exports.number().int().nonnegative().safe(),
|
|
14942
|
+
eof: external_exports.boolean()
|
|
14943
|
+
}).strict();
|
|
14944
|
+
var directoryEntrySchema = external_exports.object({
|
|
14945
|
+
name: external_exports.string().min(1).max(1024),
|
|
14946
|
+
path: locatorSchema,
|
|
14947
|
+
isDirectory: external_exports.boolean(),
|
|
14948
|
+
size: external_exports.number().int().nonnegative().safe().optional(),
|
|
14949
|
+
mtimeMs: external_exports.number().nonnegative().optional()
|
|
14950
|
+
}).strict();
|
|
14951
|
+
var listResultSchema = external_exports.object({
|
|
14952
|
+
path: locatorSchema,
|
|
14953
|
+
entries: external_exports.array(directoryEntrySchema).max(MAX_REMOTE_DIRECTORY_ENTRIES)
|
|
14954
|
+
}).strict();
|
|
14955
|
+
var RemoteFileViewerBridge = class {
|
|
14956
|
+
constructor(service, logger) {
|
|
14957
|
+
this.service = service;
|
|
14958
|
+
this.logger = logger;
|
|
14959
|
+
}
|
|
14960
|
+
async call(input) {
|
|
14961
|
+
const params = callSchema.parse(input);
|
|
14962
|
+
const service = this.service();
|
|
14963
|
+
if (service === void 0) {
|
|
14964
|
+
throw new RpcError("FILE_VIEWER_UNAVAILABLE", "The Remote Host does not have DSH File Viewer available.");
|
|
14965
|
+
}
|
|
14966
|
+
const startedAt = performance.now();
|
|
14967
|
+
const signal = AbortSignal.timeout(3e4);
|
|
14968
|
+
let raw;
|
|
14969
|
+
try {
|
|
14970
|
+
raw = await service.handle(params.endpoint, params.payload, signal);
|
|
14971
|
+
} catch {
|
|
14972
|
+
this.logger?.warn("remote file viewer service failed", { endpoint: params.endpoint });
|
|
14973
|
+
throw new RpcError("FILE_VIEWER_ERROR", "The Remote File Viewer could not complete the request.");
|
|
14974
|
+
}
|
|
14975
|
+
const result = parseHostResult(raw);
|
|
14976
|
+
if (!result.ok) throw hostFailure(result);
|
|
14977
|
+
try {
|
|
14978
|
+
const value = params.endpoint === "stat" ? statResultSchema.parse(result.value) : params.endpoint === "readRange" ? rangeResultSchema.parse(result.value) : listResultSchema.parse(result.value);
|
|
14979
|
+
this.logger?.debug("remote file viewer call ok", {
|
|
14980
|
+
endpoint: params.endpoint,
|
|
14981
|
+
durationMs: Math.round(performance.now() - startedAt)
|
|
14982
|
+
});
|
|
14983
|
+
return value;
|
|
14984
|
+
} catch (error) {
|
|
14985
|
+
if (error instanceof external_exports.ZodError && params.endpoint === "list" && isOversizedListing(result.value)) {
|
|
14986
|
+
throw new RpcError(
|
|
14987
|
+
"RESPONSE_TOO_LARGE",
|
|
14988
|
+
`The remote directory contains more than ${MAX_REMOTE_DIRECTORY_ENTRIES} entries.`,
|
|
14989
|
+
{ maxEntries: MAX_REMOTE_DIRECTORY_ENTRIES },
|
|
14990
|
+
true
|
|
14991
|
+
);
|
|
14992
|
+
}
|
|
14993
|
+
throw error;
|
|
14994
|
+
}
|
|
14995
|
+
}
|
|
14996
|
+
};
|
|
14997
|
+
function parseHostResult(input) {
|
|
14998
|
+
if (typeof input !== "object" || input === null || !("ok" in input) || typeof input.ok !== "boolean") {
|
|
14999
|
+
throw new RpcError("FILE_VIEWER_INVALID_RESPONSE", "The Remote Host File Viewer returned an invalid response.");
|
|
15000
|
+
}
|
|
15001
|
+
return input;
|
|
15002
|
+
}
|
|
15003
|
+
function hostFailure(result) {
|
|
15004
|
+
const message = result.error?.message ?? "";
|
|
15005
|
+
if (/access denied/i.test(message)) {
|
|
15006
|
+
return new RpcError("ACCESS_DENIED", "The Remote File Viewer denied access to this locator.");
|
|
15007
|
+
}
|
|
15008
|
+
if (/does not exist|not exist|not found/i.test(message)) {
|
|
15009
|
+
return new RpcError("NOT_FOUND", "The requested remote file does not exist.");
|
|
15010
|
+
}
|
|
15011
|
+
if (result.error?.code === "cancelled") {
|
|
15012
|
+
return new RpcError("CANCELLED", "The Remote File Viewer request was cancelled.", void 0, true);
|
|
15013
|
+
}
|
|
15014
|
+
if (result.error?.code === "bad-request") {
|
|
15015
|
+
return new RpcError("INVALID_MESSAGE", "The Remote File Viewer request is invalid.");
|
|
15016
|
+
}
|
|
15017
|
+
return new RpcError("FILE_VIEWER_ERROR", "The Remote File Viewer could not complete the request.");
|
|
15018
|
+
}
|
|
15019
|
+
function isOversizedListing(value) {
|
|
15020
|
+
return typeof value === "object" && value !== null && "entries" in value && Array.isArray(value.entries) && value.entries.length > MAX_REMOTE_DIRECTORY_ENTRIES;
|
|
15021
|
+
}
|
|
15022
|
+
|
|
14534
15023
|
// src/server-connection.ts
|
|
14535
15024
|
var DEFAULT_WEBRTC_NEGOTIATE_TIMEOUT_MS = 8e3;
|
|
14536
15025
|
var HostServerConnection = class {
|
|
14537
|
-
constructor(config, identity, identities, api, connections, logger, createWebSocket = (url) => new WebSocket(url), rtcFactoryProvider) {
|
|
15026
|
+
constructor(config, identity, identities, api, connections, logger, createWebSocket = (url) => new WebSocket(url), rtcFactoryProvider, hostCapabilities = () => ["harness.api.v1"]) {
|
|
14538
15027
|
this.config = config;
|
|
14539
15028
|
this.identity = identity;
|
|
14540
15029
|
this.identities = identities;
|
|
@@ -14543,6 +15032,7 @@ var HostServerConnection = class {
|
|
|
14543
15032
|
this.logger = logger;
|
|
14544
15033
|
this.createWebSocket = createWebSocket;
|
|
14545
15034
|
this.rtcFactoryProvider = rtcFactoryProvider;
|
|
15035
|
+
this.hostCapabilities = hostCapabilities;
|
|
14546
15036
|
}
|
|
14547
15037
|
socket;
|
|
14548
15038
|
running;
|
|
@@ -14666,7 +15156,7 @@ var HostServerConnection = class {
|
|
|
14666
15156
|
accessToken: credentials.accessToken,
|
|
14667
15157
|
protocols: [PROTOCOL_VERSION],
|
|
14668
15158
|
clientVersion: PLUGIN_VERSION,
|
|
14669
|
-
capabilities: this.rtcFactoryProvider === void 0 || this.config.forceRelay ? ["transport.relay",
|
|
15159
|
+
capabilities: this.rtcFactoryProvider === void 0 || this.config.forceRelay ? ["transport.relay", ...this.hostCapabilities()] : ["transport.p2p", "transport.turn", "transport.relay", ...this.hostCapabilities()]
|
|
14670
15160
|
});
|
|
14671
15161
|
};
|
|
14672
15162
|
socket.onmessage = (event) => {
|
|
@@ -14692,7 +15182,7 @@ var HostServerConnection = class {
|
|
|
14692
15182
|
this.terminalError = code;
|
|
14693
15183
|
this.logger.error("server control frame failed", {
|
|
14694
15184
|
code,
|
|
14695
|
-
reason:
|
|
15185
|
+
reason: diagnosticReason4(error)
|
|
14696
15186
|
});
|
|
14697
15187
|
socket.close(4008, "invalid control frame");
|
|
14698
15188
|
});
|
|
@@ -14841,7 +15331,7 @@ var HostServerConnection = class {
|
|
|
14841
15331
|
if (!tunnel.noise.complete) throw new ControlConnectionError("SECURE_CHANNEL_FAILED", "Noise IK handshake did not complete.");
|
|
14842
15332
|
const viaWebRtc = tunnel.rtc !== void 0 && (tunnel.transport === "p2p" || tunnel.transport === "turn");
|
|
14843
15333
|
if (!viaWebRtc && tunnel.transport === "negotiating") tunnel.transport = "relay";
|
|
14844
|
-
const mode = viaWebRtc ? tunnel.transport === "turn" ? "TURN" : "P2P" : "Relay";
|
|
15334
|
+
const mode = viaWebRtc ? tunnel.transportMode ?? (tunnel.transport === "turn" ? "TURN" : "P2P") : "Relay";
|
|
14845
15335
|
const transmit = viaWebRtc ? (ciphertext) => tunnel.rtc.send(ciphertext) : (ciphertext) => this.sendRelay(tunnel, ciphertext);
|
|
14846
15336
|
const channel = new ServerNoiseChannel(tunnel, transmit, () => {
|
|
14847
15337
|
if (this.tunnels.get(tunnel.connectionId) === tunnel) this.tunnels.delete(tunnel.connectionId);
|
|
@@ -14951,19 +15441,20 @@ var HostServerConnection = class {
|
|
|
14951
15441
|
handleRtcOpened(tunnel, selected) {
|
|
14952
15442
|
if (this.tunnels.get(tunnel.connectionId) !== tunnel || tunnel.rtc === void 0) return;
|
|
14953
15443
|
tunnel.transport = selected;
|
|
15444
|
+
tunnel.transportMode = tunnel.rtc.selectedPathMode();
|
|
14954
15445
|
this.sendTransportSelected(tunnel, selected);
|
|
14955
15446
|
this.logger.info("webrtc data channel ready", {
|
|
14956
15447
|
connectionId: shortId3(tunnel.connectionId),
|
|
14957
15448
|
peerDeviceId: shortId3(tunnel.peer.deviceId),
|
|
14958
|
-
transport: selected
|
|
15449
|
+
transport: tunnel.transportMode ?? selected
|
|
14959
15450
|
});
|
|
14960
15451
|
}
|
|
14961
15452
|
async handleRtcFailed(tunnel, rtc, error) {
|
|
14962
15453
|
if (this.tunnels.get(tunnel.connectionId) !== tunnel || tunnel.rtc !== rtc) return;
|
|
14963
|
-
if (tunnel.
|
|
15454
|
+
if (tunnel.transport === "p2p" || tunnel.transport === "turn") {
|
|
14964
15455
|
this.logger.warn("webrtc data channel failed; disconnecting peer", {
|
|
14965
15456
|
connectionId: shortId3(tunnel.connectionId),
|
|
14966
|
-
reason:
|
|
15457
|
+
reason: diagnosticReason4(error)
|
|
14967
15458
|
});
|
|
14968
15459
|
await this.dropTunnel(tunnel.connectionId, "CONNECTION_FAILED");
|
|
14969
15460
|
return;
|
|
@@ -14973,7 +15464,7 @@ var HostServerConnection = class {
|
|
|
14973
15464
|
await rtc.close();
|
|
14974
15465
|
this.logger.warn("webrtc negotiation failed; falling back to relay", {
|
|
14975
15466
|
connectionId: shortId3(tunnel.connectionId),
|
|
14976
|
-
reason:
|
|
15467
|
+
reason: diagnosticReason4(error)
|
|
14977
15468
|
});
|
|
14978
15469
|
}
|
|
14979
15470
|
sendTransportSelected(tunnel, transport) {
|
|
@@ -15017,7 +15508,7 @@ var HostServerConnection = class {
|
|
|
15017
15508
|
} catch (error) {
|
|
15018
15509
|
this.logger.warn("remote connection RTC cleanup failed", {
|
|
15019
15510
|
connectionId: shortId3(connectionId),
|
|
15020
|
-
reason:
|
|
15511
|
+
reason: diagnosticReason4(error)
|
|
15021
15512
|
});
|
|
15022
15513
|
}
|
|
15023
15514
|
if (tunnel.channel !== void 0) {
|
|
@@ -15028,7 +15519,7 @@ var HostServerConnection = class {
|
|
|
15028
15519
|
await tunnel.channel.close(code).catch(() => void 0);
|
|
15029
15520
|
this.logger.warn("remote connection channel cleanup failed", {
|
|
15030
15521
|
connectionId: shortId3(connectionId),
|
|
15031
|
-
reason:
|
|
15522
|
+
reason: diagnosticReason4(error)
|
|
15032
15523
|
});
|
|
15033
15524
|
}
|
|
15034
15525
|
} else {
|
|
@@ -15196,7 +15687,7 @@ function shortId3(value) {
|
|
|
15196
15687
|
function asError2(error) {
|
|
15197
15688
|
return error instanceof Error ? error : new Error("Unknown Server connection error.");
|
|
15198
15689
|
}
|
|
15199
|
-
function
|
|
15690
|
+
function diagnosticReason4(error) {
|
|
15200
15691
|
const message = asError2(error).message.replace(/[\r\n]+/g, " ").slice(0, 160);
|
|
15201
15692
|
return message || "Unknown Server connection error.";
|
|
15202
15693
|
}
|
|
@@ -15260,7 +15751,7 @@ function crumbs(path) {
|
|
|
15260
15751
|
}
|
|
15261
15752
|
|
|
15262
15753
|
// src/harness-api-bridge.ts
|
|
15263
|
-
var
|
|
15754
|
+
var callSchema2 = external_exports.object({
|
|
15264
15755
|
method: external_exports.string().min(1).max(80),
|
|
15265
15756
|
rpcId: external_exports.string().min(1).max(128),
|
|
15266
15757
|
payload: external_exports.unknown()
|
|
@@ -15285,9 +15776,12 @@ var streamOpenSchema = external_exports.object({
|
|
|
15285
15776
|
}).strict()
|
|
15286
15777
|
}).strict();
|
|
15287
15778
|
var streamCloseSchema = external_exports.object({ streamId: external_exports.string().min(1).max(128) }).strict();
|
|
15288
|
-
var
|
|
15779
|
+
var commandExecuteSchema = external_exports.object({
|
|
15289
15780
|
agentId: external_exports.string().min(1).max(128),
|
|
15290
|
-
line: external_exports.string().
|
|
15781
|
+
line: external_exports.string().min(1).max(2048)
|
|
15782
|
+
}).strict();
|
|
15783
|
+
var commandListSchema = external_exports.object({
|
|
15784
|
+
agentId: external_exports.string().min(1).max(128)
|
|
15291
15785
|
}).strict();
|
|
15292
15786
|
var HARNESS_API_ALLOWLIST = [
|
|
15293
15787
|
"session.list",
|
|
@@ -15325,6 +15819,7 @@ var HARNESS_API_ALLOWLIST = [
|
|
|
15325
15819
|
"goal.complete",
|
|
15326
15820
|
"goal.clear",
|
|
15327
15821
|
"commands.execute",
|
|
15822
|
+
"commands.list",
|
|
15328
15823
|
"llm.providers",
|
|
15329
15824
|
"llm.models"
|
|
15330
15825
|
];
|
|
@@ -15348,7 +15843,7 @@ var HarnessApiBridge = class {
|
|
|
15348
15843
|
host;
|
|
15349
15844
|
answer;
|
|
15350
15845
|
async call(input) {
|
|
15351
|
-
const params =
|
|
15846
|
+
const params = callSchema2.parse(input);
|
|
15352
15847
|
const method = this.methods.get(params.method);
|
|
15353
15848
|
if (method === void 0) throw deniedMethod(params.method);
|
|
15354
15849
|
const startedAt = performance.now();
|
|
@@ -15382,7 +15877,7 @@ var HarnessApiBridge = class {
|
|
|
15382
15877
|
method: params.method,
|
|
15383
15878
|
durationMs,
|
|
15384
15879
|
timedOut: signal.aborted,
|
|
15385
|
-
reason:
|
|
15880
|
+
reason: diagnosticReason5(error)
|
|
15386
15881
|
});
|
|
15387
15882
|
throw error;
|
|
15388
15883
|
}
|
|
@@ -15481,14 +15976,24 @@ function createMethodMap(api, typertGateway) {
|
|
|
15481
15976
|
const domains = api;
|
|
15482
15977
|
const methods = /* @__PURE__ */ new Map();
|
|
15483
15978
|
for (const method of HARNESS_API_ALLOWLIST) {
|
|
15484
|
-
if (method === "commands.execute") {
|
|
15979
|
+
if (method === "commands.execute" || method === "commands.list") {
|
|
15485
15980
|
if (typertGateway === void 0) continue;
|
|
15981
|
+
const [namespace, commandMethod] = method.split(".");
|
|
15486
15982
|
const implementation2 = async (request, signal) => {
|
|
15487
|
-
|
|
15488
|
-
|
|
15983
|
+
if (commandMethod === "execute") {
|
|
15984
|
+
const args2 = commandExecuteSchema.parse(request.payload);
|
|
15985
|
+
const value2 = await typertGateway.invoke({
|
|
15986
|
+
namespace,
|
|
15987
|
+
method: "execute",
|
|
15988
|
+
args: args2,
|
|
15989
|
+
...signal === void 0 ? {} : { signal }
|
|
15990
|
+
});
|
|
15991
|
+
return { rpcId: request.rpcId, result: { ok: true, value: value2 } };
|
|
15992
|
+
}
|
|
15993
|
+
const args = commandListSchema.parse(request.payload);
|
|
15489
15994
|
const value = await typertGateway.invoke({
|
|
15490
15995
|
namespace,
|
|
15491
|
-
method:
|
|
15996
|
+
method: "list",
|
|
15492
15997
|
args,
|
|
15493
15998
|
...signal === void 0 ? {} : { signal }
|
|
15494
15999
|
});
|
|
@@ -15516,7 +16021,7 @@ function domainProperty(wireDomain) {
|
|
|
15516
16021
|
function deniedMethod(method) {
|
|
15517
16022
|
return new RpcError("METHOD_NOT_ALLOWED", `Harness API method ${JSON.stringify(method)} is not available in remote mode.`);
|
|
15518
16023
|
}
|
|
15519
|
-
function
|
|
16024
|
+
function diagnosticReason5(error) {
|
|
15520
16025
|
const message = error instanceof Error ? error.message : String(error);
|
|
15521
16026
|
return message.replace(/[\r\n]+/g, " ").slice(0, 160) || "Unknown Harness API failure.";
|
|
15522
16027
|
}
|
|
@@ -15595,205 +16100,13 @@ function shortId4(value) {
|
|
|
15595
16100
|
return value.length <= 12 ? value : `${value.slice(0, 8)}\u2026${value.slice(-4)}`;
|
|
15596
16101
|
}
|
|
15597
16102
|
|
|
15598
|
-
// src/werift-rtc.ts
|
|
15599
|
-
import { networkInterfaces } from "node:os";
|
|
15600
|
-
var cachedFactory;
|
|
15601
|
-
async function loadWeriftFactory() {
|
|
15602
|
-
if (cachedFactory !== void 0) return cachedFactory;
|
|
15603
|
-
try {
|
|
15604
|
-
const werift = await import("werift");
|
|
15605
|
-
cachedFactory = buildWeriftFactory(werift);
|
|
15606
|
-
return cachedFactory;
|
|
15607
|
-
} catch {
|
|
15608
|
-
return void 0;
|
|
15609
|
-
}
|
|
15610
|
-
}
|
|
15611
|
-
function buildWeriftFactory(werift) {
|
|
15612
|
-
return {
|
|
15613
|
-
create(configuration) {
|
|
15614
|
-
const hostIpv4 = detectHostIpv4();
|
|
15615
|
-
const raw = new werift.RTCPeerConnection({
|
|
15616
|
-
iceServers: configuration.iceServers ?? [],
|
|
15617
|
-
iceUseIpv6: false,
|
|
15618
|
-
iceUseLinkLocalAddress: false,
|
|
15619
|
-
...hostIpv4 === void 0 ? {} : { iceInterfaceAddresses: { udp4: hostIpv4 } }
|
|
15620
|
-
});
|
|
15621
|
-
let onIceCandidate = null;
|
|
15622
|
-
let onDataChannel = null;
|
|
15623
|
-
raw.onicecandidate = (event) => {
|
|
15624
|
-
if (onIceCandidate === null) return;
|
|
15625
|
-
onIceCandidate({ candidate: event.candidate === void 0 ? null : event.candidate.toJSON() });
|
|
15626
|
-
};
|
|
15627
|
-
raw.ondatachannel = (event) => {
|
|
15628
|
-
if (onDataChannel === null) return;
|
|
15629
|
-
onDataChannel({ channel: adaptDataChannel2(event.channel) });
|
|
15630
|
-
};
|
|
15631
|
-
const pc = {
|
|
15632
|
-
get connectionState() {
|
|
15633
|
-
return raw.connectionState;
|
|
15634
|
-
},
|
|
15635
|
-
get iceConnectionState() {
|
|
15636
|
-
return raw.iceConnectionState;
|
|
15637
|
-
},
|
|
15638
|
-
get iceGatheringState() {
|
|
15639
|
-
return raw.iceGatheringState;
|
|
15640
|
-
},
|
|
15641
|
-
get signalingState() {
|
|
15642
|
-
return raw.signalingState;
|
|
15643
|
-
},
|
|
15644
|
-
set onconnectionstatechange(value) {
|
|
15645
|
-
raw.onconnectionstatechange = value;
|
|
15646
|
-
},
|
|
15647
|
-
get onconnectionstatechange() {
|
|
15648
|
-
return raw.onconnectionstatechange;
|
|
15649
|
-
},
|
|
15650
|
-
set oniceconnectionstatechange(value) {
|
|
15651
|
-
raw.oniceconnectionstatechange = value;
|
|
15652
|
-
},
|
|
15653
|
-
get oniceconnectionstatechange() {
|
|
15654
|
-
return raw.oniceconnectionstatechange;
|
|
15655
|
-
},
|
|
15656
|
-
set onicegatheringstatechange(value) {
|
|
15657
|
-
raw.onicegatheringstatechange = value;
|
|
15658
|
-
},
|
|
15659
|
-
get onicegatheringstatechange() {
|
|
15660
|
-
return raw.onicegatheringstatechange;
|
|
15661
|
-
},
|
|
15662
|
-
set onicecandidate(value) {
|
|
15663
|
-
onIceCandidate = value;
|
|
15664
|
-
},
|
|
15665
|
-
get onicecandidate() {
|
|
15666
|
-
return onIceCandidate;
|
|
15667
|
-
},
|
|
15668
|
-
set ondatachannel(value) {
|
|
15669
|
-
onDataChannel = value;
|
|
15670
|
-
},
|
|
15671
|
-
get ondatachannel() {
|
|
15672
|
-
return onDataChannel;
|
|
15673
|
-
},
|
|
15674
|
-
createDataChannel(label, options) {
|
|
15675
|
-
return adaptDataChannel2(raw.createDataChannel(label, { ordered: options?.ordered ?? true }));
|
|
15676
|
-
},
|
|
15677
|
-
createOffer() {
|
|
15678
|
-
return raw.createOffer();
|
|
15679
|
-
},
|
|
15680
|
-
createAnswer() {
|
|
15681
|
-
return raw.createAnswer();
|
|
15682
|
-
},
|
|
15683
|
-
setLocalDescription(description) {
|
|
15684
|
-
return raw.setLocalDescription(description).then(() => void 0);
|
|
15685
|
-
},
|
|
15686
|
-
setRemoteDescription(description) {
|
|
15687
|
-
return raw.setRemoteDescription(description);
|
|
15688
|
-
},
|
|
15689
|
-
addIceCandidate(candidate) {
|
|
15690
|
-
return raw.addIceCandidate(candidate);
|
|
15691
|
-
},
|
|
15692
|
-
getStats() {
|
|
15693
|
-
return raw.getStats();
|
|
15694
|
-
},
|
|
15695
|
-
close() {
|
|
15696
|
-
void raw.close().catch(() => void 0);
|
|
15697
|
-
}
|
|
15698
|
-
};
|
|
15699
|
-
return pc;
|
|
15700
|
-
}
|
|
15701
|
-
};
|
|
15702
|
-
}
|
|
15703
|
-
function adaptDataChannel2(raw) {
|
|
15704
|
-
return {
|
|
15705
|
-
get label() {
|
|
15706
|
-
return raw.label;
|
|
15707
|
-
},
|
|
15708
|
-
get ordered() {
|
|
15709
|
-
return raw.ordered;
|
|
15710
|
-
},
|
|
15711
|
-
get readyState() {
|
|
15712
|
-
return raw.readyState;
|
|
15713
|
-
},
|
|
15714
|
-
get bufferedAmount() {
|
|
15715
|
-
return raw.bufferedAmount;
|
|
15716
|
-
},
|
|
15717
|
-
binaryType: "arraybuffer",
|
|
15718
|
-
set onopen(value) {
|
|
15719
|
-
raw.onopen = value;
|
|
15720
|
-
},
|
|
15721
|
-
get onopen() {
|
|
15722
|
-
return raw.onopen;
|
|
15723
|
-
},
|
|
15724
|
-
set onmessage(value) {
|
|
15725
|
-
raw.onmessage = value === null ? null : (event) => value({ data: toArrayBuffer2(event.data) });
|
|
15726
|
-
},
|
|
15727
|
-
get onmessage() {
|
|
15728
|
-
return null;
|
|
15729
|
-
},
|
|
15730
|
-
set onclose(value) {
|
|
15731
|
-
raw.onclose = value;
|
|
15732
|
-
},
|
|
15733
|
-
get onclose() {
|
|
15734
|
-
return raw.onclose;
|
|
15735
|
-
},
|
|
15736
|
-
set onerror(value) {
|
|
15737
|
-
raw.onerror = value;
|
|
15738
|
-
},
|
|
15739
|
-
get onerror() {
|
|
15740
|
-
return raw.onerror;
|
|
15741
|
-
},
|
|
15742
|
-
onbufferedamountlow: null,
|
|
15743
|
-
send(data) {
|
|
15744
|
-
const bytes = typeof data === "string" ? Buffer.byteLength(data) : data.byteLength;
|
|
15745
|
-
try {
|
|
15746
|
-
raw.send(typeof data === "string" ? data : Buffer.from(data));
|
|
15747
|
-
} catch (error) {
|
|
15748
|
-
console.error("[werift-send-error] bytes=" + bytes, error instanceof Error ? error.message : error);
|
|
15749
|
-
throw error;
|
|
15750
|
-
}
|
|
15751
|
-
},
|
|
15752
|
-
close() {
|
|
15753
|
-
raw.close();
|
|
15754
|
-
}
|
|
15755
|
-
};
|
|
15756
|
-
}
|
|
15757
|
-
function toArrayBuffer2(data) {
|
|
15758
|
-
if (typeof data === "string") return data;
|
|
15759
|
-
return data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
|
|
15760
|
-
}
|
|
15761
|
-
function detectHostIpv4() {
|
|
15762
|
-
const preferred = [];
|
|
15763
|
-
const fallback = [];
|
|
15764
|
-
for (const [name2, addresses] of Object.entries(networkInterfaces())) {
|
|
15765
|
-
if (isVirtualInterface(name2)) continue;
|
|
15766
|
-
for (const address of addresses ?? []) {
|
|
15767
|
-
if (address.internal || address.family !== "IPv4") continue;
|
|
15768
|
-
const ip = address.address;
|
|
15769
|
-
if (ip.startsWith("127.") || ip.startsWith("169.254.") || isCgnat(ip)) continue;
|
|
15770
|
-
if (isPrivate(ip)) preferred.push(ip);
|
|
15771
|
-
else fallback.push(ip);
|
|
15772
|
-
}
|
|
15773
|
-
}
|
|
15774
|
-
return preferred[0] ?? fallback[0];
|
|
15775
|
-
}
|
|
15776
|
-
function isVirtualInterface(name2) {
|
|
15777
|
-
return /^(utun|ppp|bridge|awdl|llw|gif|stf|anpi|ap\d|en[1-9]\d*$)/i.test(name2);
|
|
15778
|
-
}
|
|
15779
|
-
function isCgnat(ip) {
|
|
15780
|
-
const parts = ip.split(".").map(Number);
|
|
15781
|
-
return parts.length === 4 && parts[0] === 100 && parts[1] >= 64 && parts[1] <= 127;
|
|
15782
|
-
}
|
|
15783
|
-
function isPrivate(ip) {
|
|
15784
|
-
const parts = ip.split(".").map(Number);
|
|
15785
|
-
if (parts.length !== 4) return false;
|
|
15786
|
-
if (parts[0] === 10) return true;
|
|
15787
|
-
if (parts[0] === 192 && parts[1] === 168) return true;
|
|
15788
|
-
return parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31;
|
|
15789
|
-
}
|
|
15790
|
-
|
|
15791
16103
|
// src/service.ts
|
|
15792
16104
|
var HostPluginRuntime = class {
|
|
15793
|
-
constructor(config, identities, apiProxy, logger, typertGateway) {
|
|
16105
|
+
constructor(config, identities, apiProxy, logger, typertGateway, fileViewerHost) {
|
|
15794
16106
|
this.config = config;
|
|
15795
16107
|
this.identities = identities;
|
|
15796
16108
|
this.logger = logger;
|
|
16109
|
+
this.fileViewerHost = fileViewerHost;
|
|
15797
16110
|
this.connections = new ConnectionController(this.identities, (_context, send) => {
|
|
15798
16111
|
const harnessApi = new HarnessApiBridge(
|
|
15799
16112
|
apiProxy,
|
|
@@ -15802,7 +16115,11 @@ var HostPluginRuntime = class {
|
|
|
15802
16115
|
this.logger,
|
|
15803
16116
|
typertGateway?.()
|
|
15804
16117
|
);
|
|
15805
|
-
|
|
16118
|
+
const fileViewer = new RemoteFileViewerBridge(
|
|
16119
|
+
() => this.fileViewerHost?.(),
|
|
16120
|
+
this.logger
|
|
16121
|
+
);
|
|
16122
|
+
return new RpcRouter(harnessApi, void 0, this.logger, fileViewer);
|
|
15806
16123
|
}, this.logger);
|
|
15807
16124
|
if (config.serverUrl !== void 0) {
|
|
15808
16125
|
this.serverApi = new HostServerApi(config.serverUrl, new ServerCredentialStore(identities.directory));
|
|
@@ -15939,7 +16256,8 @@ var HostPluginRuntime = class {
|
|
|
15939
16256
|
this.connections,
|
|
15940
16257
|
this.logger,
|
|
15941
16258
|
void 0,
|
|
15942
|
-
this.config.forceRelay ? void 0 : loadWeriftFactory
|
|
16259
|
+
this.config.forceRelay ? void 0 : loadWeriftFactory,
|
|
16260
|
+
() => this.fileViewerHost?.() === void 0 ? ["harness.api.v1"] : ["harness.api.v1", "fileviewer.read.v1"]
|
|
15943
16261
|
);
|
|
15944
16262
|
}
|
|
15945
16263
|
};
|
|
@@ -15947,6 +16265,67 @@ function shortId5(value) {
|
|
|
15947
16265
|
return value.length <= 12 ? value : `${value.slice(0, 8)}\u2026${value.slice(-4)}`;
|
|
15948
16266
|
}
|
|
15949
16267
|
|
|
16268
|
+
// src/remote-file-content-provider.ts
|
|
16269
|
+
function createRemoteFileContentProvider(call) {
|
|
16270
|
+
return {
|
|
16271
|
+
id: "dsh-remote-files",
|
|
16272
|
+
priority: 1e4,
|
|
16273
|
+
supports: () => true,
|
|
16274
|
+
async stat(locator, signal) {
|
|
16275
|
+
const value = await call("fileviewer.stat", { path: locator }, signal);
|
|
16276
|
+
if (!value.exists) return void 0;
|
|
16277
|
+
return {
|
|
16278
|
+
name: value.name,
|
|
16279
|
+
size: value.isDirectory ? 0 : value.size,
|
|
16280
|
+
mime: value.mime,
|
|
16281
|
+
mtimeMs: value.mtimeMs,
|
|
16282
|
+
isDirectory: value.isDirectory
|
|
16283
|
+
};
|
|
16284
|
+
},
|
|
16285
|
+
async read(locator, request) {
|
|
16286
|
+
if (!Number.isInteger(request.offset) || request.offset < 0) throw new Error("A non-negative integer offset is required.");
|
|
16287
|
+
if (!Number.isInteger(request.length) || request.length <= 0) throw new Error("A positive integer length is required.");
|
|
16288
|
+
const chunks = [];
|
|
16289
|
+
let received = 0;
|
|
16290
|
+
while (received < request.length) {
|
|
16291
|
+
request.signal.throwIfAborted();
|
|
16292
|
+
const length = Math.min(REMOTE_FILE_CHUNK_BYTES, request.length - received);
|
|
16293
|
+
const offset = request.offset + received;
|
|
16294
|
+
const range = await call("fileviewer.readRange", { path: locator, offset, length }, request.signal);
|
|
16295
|
+
if (range.offset !== offset) throw new Error("The Remote Host returned a mismatched file range.");
|
|
16296
|
+
const bytes = decodeBase64(range.data);
|
|
16297
|
+
if (bytes.byteLength > length) throw new Error("The Remote Host returned more file bytes than requested.");
|
|
16298
|
+
chunks.push(bytes);
|
|
16299
|
+
received += bytes.byteLength;
|
|
16300
|
+
if (range.eof || bytes.byteLength === 0) break;
|
|
16301
|
+
}
|
|
16302
|
+
const merged = new Uint8Array(received);
|
|
16303
|
+
let cursor = 0;
|
|
16304
|
+
for (const chunk of chunks) {
|
|
16305
|
+
merged.set(chunk, cursor);
|
|
16306
|
+
cursor += chunk.byteLength;
|
|
16307
|
+
}
|
|
16308
|
+
return merged;
|
|
16309
|
+
},
|
|
16310
|
+
async list(locator, signal) {
|
|
16311
|
+
const value = await call("fileviewer.list", { path: locator }, signal);
|
|
16312
|
+
return value.entries.map((entry) => ({
|
|
16313
|
+
locator: entry.path,
|
|
16314
|
+
name: entry.name,
|
|
16315
|
+
size: entry.isDirectory ? 0 : entry.size ?? 0,
|
|
16316
|
+
mtimeMs: entry.mtimeMs,
|
|
16317
|
+
isDirectory: entry.isDirectory
|
|
16318
|
+
}));
|
|
16319
|
+
}
|
|
16320
|
+
};
|
|
16321
|
+
}
|
|
16322
|
+
function decodeBase64(value) {
|
|
16323
|
+
const binary = atob(value);
|
|
16324
|
+
const bytes = new Uint8Array(binary.length);
|
|
16325
|
+
for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index);
|
|
16326
|
+
return bytes;
|
|
16327
|
+
}
|
|
16328
|
+
|
|
15950
16329
|
// src/index.ts
|
|
15951
16330
|
var name = "dsh-remote";
|
|
15952
16331
|
function apply(ctx, input = {}) {
|
|
@@ -15989,7 +16368,14 @@ async function activate(ctx, input) {
|
|
|
15989
16368
|
const connection = ctx.get("connection");
|
|
15990
16369
|
const nativeTypertGateway = ctx.get("typertGateway");
|
|
15991
16370
|
const localTypertGateway = new TypertGatewaySwitch(nativeTypertGateway).local();
|
|
15992
|
-
const runtime = new HostPluginRuntime(
|
|
16371
|
+
const runtime = new HostPluginRuntime(
|
|
16372
|
+
config,
|
|
16373
|
+
hostIdentities,
|
|
16374
|
+
apiProxy,
|
|
16375
|
+
logger,
|
|
16376
|
+
() => localTypertGateway,
|
|
16377
|
+
() => ctx.get("fileViewerHost")
|
|
16378
|
+
);
|
|
15993
16379
|
let clientRuntime;
|
|
15994
16380
|
const hostControl = runtime;
|
|
15995
16381
|
if (config.serverUrl !== void 0 && apiProxy !== void 0 && connection !== void 0) {
|
|
@@ -16053,6 +16439,7 @@ export {
|
|
|
16053
16439
|
IdentityInvalidError,
|
|
16054
16440
|
IdentityStore,
|
|
16055
16441
|
PluginControlRuntime,
|
|
16442
|
+
RemoteFileViewerBridge,
|
|
16056
16443
|
RemoteHarnessApiProxy,
|
|
16057
16444
|
RpcError,
|
|
16058
16445
|
RpcRouter,
|
|
@@ -16061,6 +16448,7 @@ export {
|
|
|
16061
16448
|
ServerCredentialsInvalidError,
|
|
16062
16449
|
TypertGatewaySwitch,
|
|
16063
16450
|
apply,
|
|
16451
|
+
createRemoteFileContentProvider,
|
|
16064
16452
|
fingerprint,
|
|
16065
16453
|
name,
|
|
16066
16454
|
resolveConfig,
|