ds-harness-remote 0.3.19 → 0.3.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4111,7 +4111,8 @@ var helloPayloadSchema = external_exports.object({
4111
4111
  accessToken: external_exports.string().min(1),
4112
4112
  protocols: external_exports.array(external_exports.number().int()).min(1),
4113
4113
  capabilities: external_exports.array(external_exports.string()),
4114
- clientVersion: external_exports.string().optional()
4114
+ clientVersion: external_exports.string().optional(),
4115
+ harnessVersion: external_exports.string().optional()
4115
4116
  });
4116
4117
  var helloAckPayloadSchema = external_exports.object({
4117
4118
  protocol: external_exports.literal(PROTOCOL_VERSION),
@@ -4359,6 +4360,14 @@ function cryptoRandomId() {
4359
4360
  }
4360
4361
 
4361
4362
  // ../client-core/dist/index.js
4363
+ var RemoteClientError = class extends Error {
4364
+ code;
4365
+ constructor(code, message, options) {
4366
+ super(message, options);
4367
+ this.code = code;
4368
+ this.name = "RemoteClientError";
4369
+ }
4370
+ };
4362
4371
  var RemoteClientCore = class {
4363
4372
  transport;
4364
4373
  timeoutMs;
@@ -4367,6 +4376,7 @@ var RemoteClientCore = class {
4367
4376
  unsubscribeTransport;
4368
4377
  unsubscribeClose;
4369
4378
  closeHandlers = /* @__PURE__ */ new Set();
4379
+ closeNotified = false;
4370
4380
  constructor(transport, timeoutMs = 3e4) {
4371
4381
  this.transport = transport;
4372
4382
  this.timeoutMs = timeoutMs;
@@ -4374,6 +4384,7 @@ var RemoteClientCore = class {
4374
4384
  async connect() {
4375
4385
  if (this.unsubscribeTransport !== void 0)
4376
4386
  return;
4387
+ this.closeNotified = false;
4377
4388
  this.unsubscribeTransport = this.transport.onMessage((data) => this.handleMessage(data));
4378
4389
  this.unsubscribeClose = this.transport.onClose?.(() => this.handleTransportClose());
4379
4390
  try {
@@ -4387,23 +4398,24 @@ var RemoteClientCore = class {
4387
4398
  }
4388
4399
  }
4389
4400
  async rpc(method, params, signal) {
4390
- signal?.throwIfAborted();
4401
+ if (signal?.aborted)
4402
+ throw rpcAbortedError(method, signal.reason);
4391
4403
  const request = createRpcRequest(method, params);
4392
4404
  const result = new Promise((resolve2, reject) => {
4393
4405
  const timer = setTimeout(() => {
4394
- const pending2 = this.pending.get(request.id);
4395
- pending2?.removeAbort?.();
4396
- this.pending.delete(request.id);
4397
- reject(new Error(`RPC ${method} timed out`));
4406
+ this.rejectPending(request.id, new RemoteClientError("RPC_TIMEOUT", `RPC ${method} timed out after ${this.timeoutMs}ms`));
4398
4407
  }, this.timeoutMs);
4399
- const pending = { resolve: resolve2, reject, timer };
4408
+ const pending = {
4409
+ method,
4410
+ resolve: resolve2,
4411
+ reject,
4412
+ timer
4413
+ };
4400
4414
  if (signal !== void 0) {
4401
4415
  const onAbort = () => {
4402
4416
  if (this.pending.get(request.id) !== pending)
4403
4417
  return;
4404
- clearTimeout(timer);
4405
- this.pending.delete(request.id);
4406
- reject(signal.reason instanceof Error ? signal.reason : new Error("RPC was cancelled"));
4418
+ this.rejectPending(request.id, rpcAbortedError(method, signal.reason));
4407
4419
  };
4408
4420
  signal.addEventListener("abort", onAbort, { once: true });
4409
4421
  pending.removeAbort = () => signal.removeEventListener("abort", onAbort);
@@ -4411,15 +4423,12 @@ var RemoteClientCore = class {
4411
4423
  this.pending.set(request.id, pending);
4412
4424
  });
4413
4425
  try {
4414
- await this.transport.send(encodeMessage(request));
4426
+ const send = this.transport.send(encodeMessage(request));
4427
+ void send.catch((error) => {
4428
+ this.rejectPending(request.id, transportSendError(error));
4429
+ });
4415
4430
  } catch (error) {
4416
- const pending = this.pending.get(request.id);
4417
- if (pending !== void 0) {
4418
- clearTimeout(pending.timer);
4419
- pending.removeAbort?.();
4420
- }
4421
- this.pending.delete(request.id);
4422
- throw error;
4431
+ this.rejectPending(request.id, transportSendError(error));
4423
4432
  }
4424
4433
  return result;
4425
4434
  }
@@ -4439,23 +4448,13 @@ var RemoteClientCore = class {
4439
4448
  this.unsubscribeTransport = void 0;
4440
4449
  this.unsubscribeClose?.();
4441
4450
  this.unsubscribeClose = void 0;
4451
+ this.rejectAllPending((pending) => new RemoteClientError("CLIENT_CLOSED", `RPC ${pending.method} terminated because the remote client closed`));
4452
+ this.notifyClose();
4442
4453
  await this.transport.close();
4443
- for (const pending of this.pending.values()) {
4444
- clearTimeout(pending.timer);
4445
- pending.removeAbort?.();
4446
- pending.reject(new Error("remote client closed"));
4447
- }
4448
- this.pending.clear();
4449
4454
  }
4450
4455
  handleTransportClose() {
4451
- for (const pending of this.pending.values()) {
4452
- clearTimeout(pending.timer);
4453
- pending.removeAbort?.();
4454
- pending.reject(new Error("remote transport closed"));
4455
- }
4456
- this.pending.clear();
4457
- for (const handler of this.closeHandlers)
4458
- handler();
4456
+ this.rejectAllPending((pending) => new RemoteClientError("TRANSPORT_CLOSED", `RPC ${pending.method} terminated because the remote transport closed`));
4457
+ this.notifyClose();
4459
4458
  }
4460
4459
  handleMessage(data) {
4461
4460
  const message = decodeMessage(data);
@@ -4470,24 +4469,54 @@ var RemoteClientCore = class {
4470
4469
  }
4471
4470
  }
4472
4471
  handleResponse(message) {
4473
- const pending = this.pending.get(message.payload.requestId);
4472
+ const pending = this.takePending(message.payload.requestId);
4474
4473
  if (pending === void 0)
4475
4474
  return;
4476
- this.pending.delete(message.payload.requestId);
4477
- clearTimeout(pending.timer);
4478
- pending.removeAbort?.();
4479
4475
  pending.resolve(message.payload.result);
4480
4476
  }
4481
4477
  handleError(message) {
4482
- const pending = this.pending.get(message.payload.requestId);
4478
+ const pending = this.takePending(message.payload.requestId);
4483
4479
  if (pending === void 0)
4484
4480
  return;
4485
- this.pending.delete(message.payload.requestId);
4481
+ pending.reject(Object.assign(new Error(message.payload.message), { code: message.payload.code }));
4482
+ }
4483
+ takePending(requestId) {
4484
+ const pending = this.pending.get(requestId);
4485
+ if (pending === void 0)
4486
+ return void 0;
4487
+ this.pending.delete(requestId);
4486
4488
  clearTimeout(pending.timer);
4487
4489
  pending.removeAbort?.();
4488
- pending.reject(Object.assign(new Error(message.payload.message), { code: message.payload.code }));
4490
+ return pending;
4491
+ }
4492
+ rejectPending(requestId, error) {
4493
+ const pending = this.takePending(requestId);
4494
+ if (pending === void 0)
4495
+ return false;
4496
+ pending.reject(error);
4497
+ return true;
4498
+ }
4499
+ rejectAllPending(createError) {
4500
+ for (const requestId of [...this.pending.keys()]) {
4501
+ const pending = this.takePending(requestId);
4502
+ if (pending !== void 0)
4503
+ pending.reject(createError(pending));
4504
+ }
4505
+ }
4506
+ notifyClose() {
4507
+ if (this.closeNotified)
4508
+ return;
4509
+ this.closeNotified = true;
4510
+ for (const handler of this.closeHandlers)
4511
+ handler();
4489
4512
  }
4490
4513
  };
4514
+ function rpcAbortedError(method, reason) {
4515
+ return new RemoteClientError("RPC_ABORTED", `RPC ${method} was aborted`, reason === void 0 ? void 0 : { cause: reason });
4516
+ }
4517
+ function transportSendError(error) {
4518
+ return error instanceof Error ? error : new Error("remote transport send failed", { cause: error });
4519
+ }
4491
4520
 
4492
4521
  // ../webrtc/dist/transport.js
4493
4522
  var BaseTransport = class {
@@ -4627,6 +4656,9 @@ function adaptDataChannel(raw) {
4627
4656
  get binaryType() {
4628
4657
  return raw.binaryType;
4629
4658
  },
4659
+ set binaryType(value) {
4660
+ raw.binaryType = value;
4661
+ },
4630
4662
  set onopen(value) {
4631
4663
  raw.onopen = value;
4632
4664
  },
@@ -4777,7 +4809,6 @@ function isChunk(frame) {
4777
4809
 
4778
4810
  // ../webrtc/dist/rtc-data-channel.js
4779
4811
  var DEFAULT_NEGOTIATE_TIMEOUT_MS = 8e3;
4780
- var DEFAULT_SEND_TIMEOUT_MS = 5e3;
4781
4812
  var RtcDataChannelTransport = class {
4782
4813
  pc;
4783
4814
  role;
@@ -4811,7 +4842,7 @@ var RtcDataChannelTransport = class {
4811
4842
  this.onSignal = options.onSignal;
4812
4843
  this.negotiateTimeoutMs = options.negotiateTimeoutMs ?? DEFAULT_NEGOTIATE_TIMEOUT_MS;
4813
4844
  this.channelLabel = options.channelLabel ?? RTC_DATA_CHANNEL_LABEL;
4814
- this.sendTimeoutMs = options.sendTimeoutMs ?? DEFAULT_SEND_TIMEOUT_MS;
4845
+ this.sendTimeoutMs = options.sendTimeoutMs;
4815
4846
  this.pc = options.factory.create({ iceServers: options.iceServers });
4816
4847
  this.pc.ondatachannel = (event) => this.adoptChannel(event.channel);
4817
4848
  this.pc.onicecandidate = (event) => {
@@ -5104,11 +5135,16 @@ var RtcDataChannelTransport = class {
5104
5135
  return channel;
5105
5136
  }
5106
5137
  armWatchdog(channel) {
5107
- if (this.watchdogTimer !== void 0 || this.closed)
5138
+ if (this.closed || this.sendTimeoutMs === void 0 || this.sendTimeoutMs <= 0)
5108
5139
  return;
5140
+ if (this.watchdogTimer !== void 0)
5141
+ clearTimeout(this.watchdogTimer);
5142
+ this.watchdogTimer = void 0;
5109
5143
  const baseline = channel.bufferedAmount;
5110
- if (baseline <= 0)
5144
+ if (baseline <= 0) {
5145
+ this.lastBufferedAmount = 0;
5111
5146
  return;
5147
+ }
5112
5148
  this.lastBufferedAmount = baseline;
5113
5149
  this.watchdogTimer = setTimeout(() => {
5114
5150
  this.watchdogTimer = void 0;
@@ -5499,7 +5535,10 @@ var AdaptiveTransport = class extends BaseTransport {
5499
5535
  this.bytesReceived += data.byteLength;
5500
5536
  this.emit(data);
5501
5537
  });
5502
- rtc.onClose(() => this.emitClose());
5538
+ rtc.onClose(() => {
5539
+ if (this.rtc === rtc && this.dataMode === "webrtc")
5540
+ this.emitClose();
5541
+ });
5503
5542
  try {
5504
5543
  await rtc.connect();
5505
5544
  } catch (error) {
@@ -12908,7 +12947,7 @@ var RemoteHarnessApiProxy = class {
12908
12947
  if (String(response.rpcId) !== String(request.rpcId) || typeof response.result !== "object" || response.result === null) {
12909
12948
  throw new Error("The remote Host returned an invalid Harness API response.");
12910
12949
  }
12911
- return response;
12950
+ return normalizeLegacyResponse(method, response);
12912
12951
  }
12913
12952
  async respond(message) {
12914
12953
  return this.client.rpc("harness.api.respond", { message });
@@ -12941,8 +12980,22 @@ var RemoteHarnessApiProxy = class {
12941
12980
  }
12942
12981
  }
12943
12982
  };
12983
+ function normalizeLegacyResponse(method, response) {
12984
+ if (method !== "host.describe" || !response.result.ok) return response;
12985
+ const value = response.result.value;
12986
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return response;
12987
+ const description = value;
12988
+ if (typeof description.home === "string" || typeof description.cwd !== "string") return response;
12989
+ return {
12990
+ ...response,
12991
+ result: {
12992
+ ...response.result,
12993
+ value: { ...description, home: description.cwd }
12994
+ }
12995
+ };
12996
+ }
12944
12997
  function isRemoteDisconnect(error) {
12945
- return error instanceof Error && (error.message === "remote transport closed" || error.message === "remote client closed");
12998
+ return error instanceof RemoteClientError && (error.code === "TRANSPORT_CLOSED" || error.code === "CLIENT_CLOSED");
12946
12999
  }
12947
13000
  var AsyncFrameQueue = class {
12948
13001
  values = [];
@@ -13068,7 +13121,7 @@ function normalizeServerUrl(value) {
13068
13121
  }
13069
13122
 
13070
13123
  // src/version.ts
13071
- var PLUGIN_VERSION = "0.3.19";
13124
+ var PLUGIN_VERSION = "0.3.21";
13072
13125
 
13073
13126
  // src/server-api.ts
13074
13127
  var HostServerApi = class {
@@ -13291,8 +13344,7 @@ var HostServerApi = class {
13291
13344
  role: this.role,
13292
13345
  platform: platform(),
13293
13346
  identityKey: identity.publicKey,
13294
- clientVersion: PLUGIN_VERSION,
13295
- ...this.role === "host" ? { harnessVersion: "0.1.0-rc.6" } : {}
13347
+ clientVersion: PLUGIN_VERSION
13296
13348
  };
13297
13349
  }
13298
13350
  saveTokens(identity, tokens, authorization) {
@@ -13711,6 +13763,7 @@ var ClientModeRuntime = class {
13711
13763
  }
13712
13764
  identity;
13713
13765
  connected;
13766
+ pendingWorkspaceSelection;
13714
13767
  proxySwitch;
13715
13768
  gatewaySwitch;
13716
13769
  closed = false;
@@ -13740,6 +13793,7 @@ var ClientModeRuntime = class {
13740
13793
  connected: this.connected !== void 0,
13741
13794
  transport: this.connected?.client.getStats().mode ?? "Disconnected",
13742
13795
  remoteFeatures: this.connected?.features ?? remoteHostFeatures(),
13796
+ ...this.pendingWorkspaceSelection === void 0 ? {} : { workspaceSelection: { ...this.pendingWorkspaceSelection } },
13743
13797
  hostAuthorizationAvailable: this.host !== void 0,
13744
13798
  ...this.host === void 0 ? {} : { host: this.host.hostStatus() }
13745
13799
  };
@@ -13808,6 +13862,7 @@ var ClientModeRuntime = class {
13808
13862
  async clearClientAuthorization() {
13809
13863
  const previous = this.connected;
13810
13864
  this.connected = void 0;
13865
+ this.pendingWorkspaceSelection = void 0;
13811
13866
  this.proxySwitch.selectLocal();
13812
13867
  this.gatewaySwitch.selectLocal();
13813
13868
  await previous?.client.close().catch(() => void 0);
@@ -13831,6 +13886,7 @@ var ClientModeRuntime = class {
13831
13886
  this.gatewaySwitch.selectLocal();
13832
13887
  const previous2 = this.connected;
13833
13888
  this.connected = void 0;
13889
+ this.pendingWorkspaceSelection = void 0;
13834
13890
  await previous2?.client.close().catch(() => void 0);
13835
13891
  this.logger.info("Harness target switched", { mode: "local" });
13836
13892
  return this.status();
@@ -13841,6 +13897,7 @@ var ClientModeRuntime = class {
13841
13897
  const next = await this.connect(targetDeviceId, signal);
13842
13898
  const previous = this.connected;
13843
13899
  this.connected = next;
13900
+ this.pendingWorkspaceSelection = void 0;
13844
13901
  const remoteApi = new RemoteHarnessApiProxy(next.client).api;
13845
13902
  this.proxySwitch.selectRemote(remoteApi, { deviceId: next.target.deviceId, name: next.target.name });
13846
13903
  this.selectRemoteCommands(next);
@@ -13878,14 +13935,24 @@ var ClientModeRuntime = class {
13878
13935
  const workspace = unwrapNativeResult(response);
13879
13936
  this.proxySwitch.selectRemote(api, { deviceId: remote.target.deviceId, name: remote.target.name });
13880
13937
  this.selectRemoteCommands(remote);
13938
+ const workspaceId = workspaceRecordId(workspace.workspace);
13939
+ this.pendingWorkspaceSelection = { targetDeviceId: remote.target.deviceId, workspaceId };
13881
13940
  this.logger.info("Remote workspace opened", { targetDeviceId: shortId(remote.target.deviceId) });
13882
13941
  return { ...this.status(), workspace };
13883
13942
  }
13943
+ consumeWorkspaceSelection(selection) {
13944
+ const pending = this.pendingWorkspaceSelection;
13945
+ if (pending?.targetDeviceId === selection.targetDeviceId && pending.workspaceId === selection.workspaceId) {
13946
+ this.pendingWorkspaceSelection = void 0;
13947
+ }
13948
+ return this.status();
13949
+ }
13884
13950
  async close() {
13885
13951
  if (this.closed) return;
13886
13952
  this.closed = true;
13887
13953
  this.proxySwitch.selectLocal();
13888
13954
  this.gatewaySwitch.selectLocal();
13955
+ this.pendingWorkspaceSelection = void 0;
13889
13956
  await this.connected?.client.close().catch(() => void 0);
13890
13957
  this.connected = void 0;
13891
13958
  this.proxySwitch.restore();
@@ -13958,6 +14025,7 @@ var ClientModeRuntime = class {
13958
14025
  client.onClose(() => {
13959
14026
  if (this.connected?.client !== client) return;
13960
14027
  this.connected = void 0;
14028
+ this.pendingWorkspaceSelection = void 0;
13961
14029
  this.proxySwitch.selectLocal();
13962
14030
  this.gatewaySwitch.selectLocal();
13963
14031
  void client.close().catch(() => void 0);
@@ -14023,6 +14091,16 @@ var ClientModeRuntime = class {
14023
14091
  }
14024
14092
  return ok(await this.openRemoteWorkspace(value.targetDeviceId, value.path, signal));
14025
14093
  }
14094
+ if (endpoint === "workspace.selection.consume") {
14095
+ const value = record(payload);
14096
+ if (typeof value.targetDeviceId !== "string" || typeof value.workspaceId !== "string") {
14097
+ throw new ClientModeError("INVALID_MESSAGE", "A Host and Workspace are required.");
14098
+ }
14099
+ return ok(this.consumeWorkspaceSelection({
14100
+ targetDeviceId: value.targetDeviceId,
14101
+ workspaceId: value.workspaceId
14102
+ }));
14103
+ }
14026
14104
  if (endpoint === "fileviewer.stat" || endpoint === "fileviewer.readRange" || endpoint === "fileviewer.list") {
14027
14105
  const method = endpoint === "fileviewer.stat" ? "stat" : endpoint === "fileviewer.readRange" ? "readRange" : "list";
14028
14106
  return ok(await this.callRemoteFileViewer(method, payload, signal));
@@ -14133,6 +14211,12 @@ function unwrapNativeResult(response) {
14133
14211
  }
14134
14212
  return result.value;
14135
14213
  }
14214
+ function workspaceRecordId(value) {
14215
+ if (typeof value !== "object" || value === null || !("workspaceId" in value) || typeof value.workspaceId !== "string" || value.workspaceId.length === 0) {
14216
+ throw new ClientModeError("INVALID_MESSAGE", "The remote Host returned an invalid Workspace.");
14217
+ }
14218
+ return value.workspaceId;
14219
+ }
14136
14220
  function fail(error) {
14137
14221
  const source = error instanceof Error ? error : void 0;
14138
14222
  const remoteCode = source !== void 0 && "code" in source && typeof source.code === "string" ? source.code : source instanceof ClientModeError ? source.code : void 0;
@@ -14680,6 +14764,10 @@ function redact(value, key = "") {
14680
14764
  return Object.fromEntries(Object.entries(value).map(([childKey, child]) => [childKey, redact(child, childKey)]));
14681
14765
  }
14682
14766
 
14767
+ // src/service.ts
14768
+ import { randomUUID } from "node:crypto";
14769
+ import { RpcId } from "@deepseek-ai/dsh-host-apiproxy/api";
14770
+
14683
14771
  // src/connection-controller.ts
14684
14772
  var ConnectionController = class {
14685
14773
  constructor(identities, createRouter, logger) {
@@ -14848,6 +14936,36 @@ function shortId2(value) {
14848
14936
  return value.length <= 12 ? value : `${value.slice(0, 8)}\u2026${value.slice(-4)}`;
14849
14937
  }
14850
14938
 
14939
+ // src/harness-version.ts
14940
+ import { readFile as readFile3 } from "node:fs/promises";
14941
+ import { dirname as dirname3, isAbsolute, join as join3 } from "node:path";
14942
+ var LEGACY_PLACEHOLDER_VERSION = "0.0.1";
14943
+ function normalizeHarnessVersion(value) {
14944
+ if (typeof value !== "string") return void 0;
14945
+ const version = value.trim();
14946
+ if (version.length === 0 || version.length > 64 || /[\u0000-\u001f]/u.test(version)) return void 0;
14947
+ return version;
14948
+ }
14949
+ function selectHarnessVersion(reportedVersion, distributionVersion) {
14950
+ if (reportedVersion !== void 0 && reportedVersion !== LEGACY_PLACEHOLDER_VERSION) return reportedVersion;
14951
+ return distributionVersion;
14952
+ }
14953
+ async function readHarnessDistributionVersion(entrypoint = process.argv[1]) {
14954
+ if (entrypoint === void 0 || !isAbsolute(entrypoint)) return void 0;
14955
+ let directory = dirname3(entrypoint);
14956
+ for (let depth = 0; depth < 8; depth += 1) {
14957
+ try {
14958
+ const manifest = JSON.parse(await readFile3(join3(directory, "package.json"), "utf8"));
14959
+ if (manifest.name === "@deepseek-ai/dsh") return normalizeHarnessVersion(manifest.version);
14960
+ } catch {
14961
+ }
14962
+ const parent = dirname3(directory);
14963
+ if (parent === directory) break;
14964
+ directory = parent;
14965
+ }
14966
+ return void 0;
14967
+ }
14968
+
14851
14969
  // src/rpc-router.ts
14852
14970
  var wireRequestSchema = external_exports.object({ method: external_exports.string().min(1), params: external_exports.unknown() }).strict();
14853
14971
  var apiMethods = /* @__PURE__ */ new Set([
@@ -15067,7 +15185,7 @@ function isOversizedListing(value) {
15067
15185
  // src/server-connection.ts
15068
15186
  var DEFAULT_WEBRTC_NEGOTIATE_TIMEOUT_MS = 8e3;
15069
15187
  var HostServerConnection = class {
15070
- constructor(config, identity, identities, api, connections, logger, createWebSocket = (url) => new WebSocket(url), rtcFactoryProvider, hostCapabilities = () => ["harness.api.v1"]) {
15188
+ constructor(config, identity, identities, api, connections, logger, createWebSocket = (url) => new WebSocket(url), rtcFactoryProvider, hostCapabilities = () => ["harness.api.v1"], harnessVersion) {
15071
15189
  this.config = config;
15072
15190
  this.identity = identity;
15073
15191
  this.identities = identities;
@@ -15077,6 +15195,7 @@ var HostServerConnection = class {
15077
15195
  this.createWebSocket = createWebSocket;
15078
15196
  this.rtcFactoryProvider = rtcFactoryProvider;
15079
15197
  this.hostCapabilities = hostCapabilities;
15198
+ this.harnessVersion = harnessVersion;
15080
15199
  }
15081
15200
  socket;
15082
15201
  running;
@@ -15200,6 +15319,7 @@ var HostServerConnection = class {
15200
15319
  accessToken: credentials.accessToken,
15201
15320
  protocols: [PROTOCOL_VERSION],
15202
15321
  clientVersion: PLUGIN_VERSION,
15322
+ ...this.harnessVersion === void 0 ? {} : { harnessVersion: this.harnessVersion },
15203
15323
  capabilities: this.rtcFactoryProvider === void 0 || this.config.forceRelay ? ["transport.relay", ...this.hostCapabilities()] : ["transport.p2p", "transport.turn", "transport.relay", ...this.hostCapabilities()]
15204
15324
  });
15205
15325
  };
@@ -15752,13 +15872,13 @@ function closeCode(code) {
15752
15872
  // src/remote-directory-browser.ts
15753
15873
  import { readdir, stat as stat3 } from "node:fs/promises";
15754
15874
  import { homedir as homedir2, platform as platform2 } from "node:os";
15755
- import { basename, dirname as dirname3, isAbsolute, parse, resolve } from "node:path";
15875
+ import { basename, dirname as dirname4, isAbsolute as isAbsolute2, parse, resolve } from "node:path";
15756
15876
  var MAX_ENTRIES = 500;
15757
15877
  async function listRemoteDirectory(path, signal) {
15758
15878
  signal?.throwIfAborted();
15759
15879
  const home = resolve(homedir2());
15760
15880
  const target = path === void 0 || path.trim() === "" ? home : resolve(path);
15761
- if (!isAbsolute(target)) throw new Error("The remote directory path must be absolute.");
15881
+ if (!isAbsolute2(target)) throw new Error("The remote directory path must be absolute.");
15762
15882
  const rows = await readdir(target, { withFileTypes: true });
15763
15883
  const directories = [];
15764
15884
  for (const row of rows) {
@@ -15785,7 +15905,7 @@ function crumbs(path) {
15785
15905
  let current = path;
15786
15906
  while (current !== root) {
15787
15907
  segments.unshift(basename(current));
15788
- current = dirname3(current);
15908
+ current = dirname4(current);
15789
15909
  }
15790
15910
  for (const segment of segments) {
15791
15911
  current = resolve(current, segment);
@@ -15870,7 +15990,7 @@ var HARNESS_API_ALLOWLIST = [
15870
15990
  var NATIVE_CALL_TIMEOUT_MS = 3e4;
15871
15991
  var SESSION_HISTORY_PAGE_SIZES = [50, 30, 20, 12, 6, 3, 1];
15872
15992
  var HarnessApiBridge = class {
15873
- constructor(api, publish, maxStreams = 3, logger, typertGateway) {
15993
+ constructor(api, publish, maxStreams = 8, logger, typertGateway) {
15874
15994
  this.api = api;
15875
15995
  this.publish = publish;
15876
15996
  this.maxStreams = maxStreams;
@@ -16149,11 +16269,12 @@ var HostPluginRuntime = class {
16149
16269
  constructor(config, identities, apiProxy, logger, typertGateway, fileViewerHost) {
16150
16270
  this.config = config;
16151
16271
  this.identities = identities;
16272
+ this.apiProxy = apiProxy;
16152
16273
  this.logger = logger;
16153
16274
  this.fileViewerHost = fileViewerHost;
16154
16275
  this.connections = new ConnectionController(this.identities, (_context, send) => {
16155
16276
  const harnessApi = new HarnessApiBridge(
16156
- apiProxy,
16277
+ this.apiProxy,
16157
16278
  (event, data) => send(createEvent(event, data)),
16158
16279
  void 0,
16159
16280
  this.logger,
@@ -16173,6 +16294,7 @@ var HostPluginRuntime = class {
16173
16294
  identity;
16174
16295
  serverApi;
16175
16296
  serverConnection;
16297
+ harnessVersion;
16176
16298
  closed = false;
16177
16299
  async start() {
16178
16300
  if (this.closed) throw new Error("remote runtime is closed");
@@ -16183,6 +16305,7 @@ var HostPluginRuntime = class {
16183
16305
  server: this.config.serverUrl ?? "not configured"
16184
16306
  });
16185
16307
  if (this.serverApi !== void 0) {
16308
+ this.harnessVersion = await this.readHarnessVersion();
16186
16309
  this.serverApi.bindIdentity(this.identity);
16187
16310
  this.serverConnection = this.createServerConnection(this.identity);
16188
16311
  this.serverConnection.start();
@@ -16301,9 +16424,28 @@ var HostPluginRuntime = class {
16301
16424
  this.logger,
16302
16425
  void 0,
16303
16426
  this.config.forceRelay ? void 0 : loadWeriftFactory,
16304
- () => this.fileViewerHost?.() === void 0 ? ["harness.api.v1"] : ["harness.api.v1", "fileviewer.read.v1"]
16427
+ () => this.fileViewerHost?.() === void 0 ? ["harness.api.v1"] : ["harness.api.v1", "fileviewer.read.v1"],
16428
+ this.harnessVersion
16305
16429
  );
16306
16430
  }
16431
+ async readHarnessVersion() {
16432
+ let reportedVersion;
16433
+ let errorCode2;
16434
+ try {
16435
+ const response = await this.apiProxy.host.describe({ rpcId: RpcId(randomUUID()), payload: {} });
16436
+ if (!response.result.ok) {
16437
+ errorCode2 = response.result.error.code;
16438
+ } else {
16439
+ reportedVersion = normalizeHarnessVersion(response.result.value.version);
16440
+ }
16441
+ } catch {
16442
+ }
16443
+ const distributionVersion = reportedVersion === void 0 || reportedVersion === "0.0.1" ? await readHarnessDistributionVersion() : void 0;
16444
+ const version = selectHarnessVersion(reportedVersion, distributionVersion);
16445
+ if (version !== void 0) return version;
16446
+ this.logger.warn("Harness version is unavailable", errorCode2 === void 0 ? void 0 : { code: errorCode2 });
16447
+ return void 0;
16448
+ }
16307
16449
  };
16308
16450
  function shortId5(value) {
16309
16451
  return value.length <= 12 ? value : `${value.slice(0, 8)}\u2026${value.slice(-4)}`;