ds-harness-remote 0.3.35 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +26 -24
  2. package/dist/client-runtime.d.ts +9 -4
  3. package/dist/client-runtime.d.ts.map +1 -1
  4. package/dist/client-runtime.js +137 -35
  5. package/dist/client-runtime.js.map +1 -1
  6. package/dist/client.d.ts.map +1 -1
  7. package/dist/client.github.js +79 -32
  8. package/dist/client.js +79 -32
  9. package/dist/client.js.map +1 -1
  10. package/dist/harness-api-bridge.d.ts +2 -9
  11. package/dist/harness-api-bridge.d.ts.map +1 -1
  12. package/dist/harness-api-bridge.js.map +1 -1
  13. package/dist/harness-remote-bridge.d.ts +46 -0
  14. package/dist/harness-remote-bridge.d.ts.map +1 -0
  15. package/dist/harness-remote-bridge.js +321 -0
  16. package/dist/harness-remote-bridge.js.map +1 -0
  17. package/dist/index.d.ts +4 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +870 -81
  20. package/dist/index.js.map +4 -4
  21. package/dist/remote-file-content-provider.d.ts +3 -1
  22. package/dist/remote-file-content-provider.d.ts.map +1 -1
  23. package/dist/remote-file-content-provider.js +10 -1
  24. package/dist/remote-file-content-provider.js.map +1 -1
  25. package/dist/remote-typert-gateway.d.ts +13 -0
  26. package/dist/remote-typert-gateway.d.ts.map +1 -0
  27. package/dist/remote-typert-gateway.js +242 -0
  28. package/dist/remote-typert-gateway.js.map +1 -0
  29. package/dist/rpc-router.d.ts +7 -2
  30. package/dist/rpc-router.d.ts.map +1 -1
  31. package/dist/rpc-router.js +61 -12
  32. package/dist/rpc-router.js.map +1 -1
  33. package/dist/server-api.d.ts +1 -1
  34. package/dist/server-api.d.ts.map +1 -1
  35. package/dist/server-api.js +14 -3
  36. package/dist/server-api.js.map +1 -1
  37. package/dist/service.d.ts +5 -3
  38. package/dist/service.d.ts.map +1 -1
  39. package/dist/service.js +26 -8
  40. package/dist/service.js.map +1 -1
  41. package/dist/typert-gateway-contract.d.ts +48 -0
  42. package/dist/typert-gateway-contract.d.ts.map +1 -0
  43. package/dist/typert-gateway-contract.js +2 -0
  44. package/dist/typert-gateway-contract.js.map +1 -0
  45. package/dist/typert-gateway-switch.d.ts +28 -7
  46. package/dist/typert-gateway-switch.d.ts.map +1 -1
  47. package/dist/typert-gateway-switch.js +114 -23
  48. package/dist/typert-gateway-switch.js.map +1 -1
  49. package/dist/version.d.ts +1 -1
  50. package/dist/version.d.ts.map +1 -1
  51. package/dist/version.js +1 -1
  52. package/dist/version.js.map +1 -1
  53. package/package.json +13 -8
package/dist/index.js CHANGED
@@ -4082,6 +4082,7 @@ var controlFrameTypes = [
4082
4082
  "error"
4083
4083
  ];
4084
4084
  var rpcMethods = [
4085
+ "harness.transport.describe",
4085
4086
  "harness.api.call",
4086
4087
  "harness.api.transfer.open",
4087
4088
  "harness.api.transfer.chunk",
@@ -4091,6 +4092,14 @@ var rpcMethods = [
4091
4092
  "harness.api.respond",
4092
4093
  "harness.api.stream.open",
4093
4094
  "harness.api.stream.close",
4095
+ "harness.remote.call",
4096
+ "harness.remote.transfer.open",
4097
+ "harness.remote.transfer.chunk",
4098
+ "harness.remote.transfer.commit",
4099
+ "harness.remote.transfer.read",
4100
+ "harness.remote.transfer.close",
4101
+ "harness.remote.stream.open",
4102
+ "harness.remote.stream.close",
4094
4103
  "fileviewer.call"
4095
4104
  ];
4096
4105
  function normalizeSdpMLineIndex(value) {
@@ -13460,6 +13469,224 @@ function base64ToBytes(value) {
13460
13469
  return bytes;
13461
13470
  }
13462
13471
 
13472
+ // src/remote-typert-gateway.ts
13473
+ var DIRECT_REMOTE_CALL_BYTES = 2 * 1024 * 1024;
13474
+ var RemoteTypertGateway = class {
13475
+ constructor(client) {
13476
+ this.client = client;
13477
+ }
13478
+ async invoke(request) {
13479
+ const result = await this.dispatch(
13480
+ `${request.namespace}/${request.method}`,
13481
+ { args: request.args },
13482
+ request.signal ?? new AbortController().signal
13483
+ );
13484
+ if (result.ok) return result.value;
13485
+ throw remoteFailure(result.error);
13486
+ }
13487
+ async dispatch(endpoint, payload, signal) {
13488
+ const request = { endpoint, payload };
13489
+ const encoded = new TextEncoder().encode(JSON.stringify(request));
13490
+ let response;
13491
+ if (encoded.byteLength > DIRECT_REMOTE_CALL_BYTES) {
13492
+ response = await this.callTransferred(encoded, signal);
13493
+ } else {
13494
+ try {
13495
+ response = await this.client.rpc("harness.remote.call", request, signal);
13496
+ } catch (error) {
13497
+ if (!hasErrorCode(error, "RESPONSE_TOO_LARGE")) throw error;
13498
+ response = await this.callTransferred(encoded, signal);
13499
+ }
13500
+ }
13501
+ return parseRpcResult(response);
13502
+ }
13503
+ async open(endpoint, payload, signal) {
13504
+ const streamId = uuidV7();
13505
+ const queue = new AsyncValueQueue();
13506
+ const unsubscribe = this.client.onEvent((event) => routeStreamEvent2(event, streamId, queue));
13507
+ const unsubscribeClose = this.client.onClose(() => {
13508
+ queue.fail(new RemoteClientError("TRANSPORT_CLOSED", "The remote Harness stream transport closed."));
13509
+ });
13510
+ const onAbort = () => queue.close();
13511
+ signal.addEventListener("abort", onAbort, { once: true });
13512
+ try {
13513
+ await this.client.rpc("harness.remote.stream.open", { streamId, endpoint, payload }, signal);
13514
+ } catch (error) {
13515
+ signal.removeEventListener("abort", onAbort);
13516
+ unsubscribe();
13517
+ unsubscribeClose();
13518
+ throw error;
13519
+ }
13520
+ return this.iterate(streamId, queue, unsubscribe, unsubscribeClose, signal, onAbort);
13521
+ }
13522
+ async *iterate(streamId, queue, unsubscribe, unsubscribeClose, signal, onAbort) {
13523
+ try {
13524
+ yield* queue;
13525
+ } finally {
13526
+ signal.removeEventListener("abort", onAbort);
13527
+ unsubscribe();
13528
+ unsubscribeClose();
13529
+ queue.close();
13530
+ await this.client.rpc("harness.remote.stream.close", { streamId }).catch(() => void 0);
13531
+ }
13532
+ }
13533
+ async callTransferred(encoded, signal) {
13534
+ if (encoded.byteLength > MAX_HARNESS_API_TRANSFER_BYTES) {
13535
+ throw new Error("The Harness Remote request exceeds the transfer limit.");
13536
+ }
13537
+ const transferId = uuidV7();
13538
+ const totalChunks = Math.ceil(encoded.byteLength / HARNESS_API_TRANSFER_CHUNK_BYTES);
13539
+ let opened = false;
13540
+ try {
13541
+ await this.client.rpc("harness.remote.transfer.open", {
13542
+ transferId,
13543
+ totalBytes: encoded.byteLength,
13544
+ totalChunks
13545
+ }, signal);
13546
+ opened = true;
13547
+ for (let index = 0; index < totalChunks; index += 1) {
13548
+ const start = index * HARNESS_API_TRANSFER_CHUNK_BYTES;
13549
+ const chunk = encoded.subarray(start, Math.min(start + HARNESS_API_TRANSFER_CHUNK_BYTES, encoded.byteLength));
13550
+ await this.client.rpc("harness.remote.transfer.chunk", {
13551
+ transferId,
13552
+ index,
13553
+ data: bytesToBase642(chunk)
13554
+ }, signal);
13555
+ }
13556
+ const committed = await this.client.rpc(
13557
+ "harness.remote.transfer.commit",
13558
+ { transferId },
13559
+ signal
13560
+ );
13561
+ if (committed.kind === "inline") return committed.response;
13562
+ if (committed.transferId !== transferId || committed.totalBytes <= 0 || committed.totalBytes > MAX_HARNESS_API_TRANSFER_BYTES || committed.totalChunks !== Math.ceil(committed.totalBytes / HARNESS_API_TRANSFER_CHUNK_BYTES)) {
13563
+ throw new Error("The remote Host returned an invalid Harness Remote transfer descriptor.");
13564
+ }
13565
+ const responseBytes = new Uint8Array(committed.totalBytes);
13566
+ let offset = 0;
13567
+ for (let index = 0; index < committed.totalChunks; index += 1) {
13568
+ const result = await this.client.rpc(
13569
+ "harness.remote.transfer.read",
13570
+ { transferId, index },
13571
+ signal
13572
+ );
13573
+ if (result.transferId !== transferId || result.index !== index) {
13574
+ throw new Error("The remote Host returned an out-of-order Harness Remote transfer chunk.");
13575
+ }
13576
+ const chunk = base64ToBytes2(result.data);
13577
+ const expectedBytes = Math.min(HARNESS_API_TRANSFER_CHUNK_BYTES, committed.totalBytes - offset);
13578
+ if (chunk.byteLength !== expectedBytes) {
13579
+ throw new Error("The remote Host returned an invalid Harness Remote transfer chunk.");
13580
+ }
13581
+ responseBytes.set(chunk, offset);
13582
+ offset += chunk.byteLength;
13583
+ }
13584
+ return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(responseBytes));
13585
+ } finally {
13586
+ if (opened) await this.client.rpc("harness.remote.transfer.close", { transferId }).catch(() => void 0);
13587
+ }
13588
+ }
13589
+ };
13590
+ var AsyncValueQueue = class {
13591
+ values = [];
13592
+ waiters = [];
13593
+ closed = false;
13594
+ error;
13595
+ push(value) {
13596
+ if (this.closed) return;
13597
+ const waiter = this.waiters.shift();
13598
+ if (waiter === void 0) this.values.push(value);
13599
+ else waiter({ done: false, value });
13600
+ }
13601
+ close() {
13602
+ if (this.closed) return;
13603
+ this.closed = true;
13604
+ for (const waiter of this.waiters.splice(0)) waiter({ done: true, value: void 0 });
13605
+ }
13606
+ fail(error) {
13607
+ if (this.closed) return;
13608
+ this.error = error;
13609
+ this.close();
13610
+ }
13611
+ async *[Symbol.asyncIterator]() {
13612
+ while (true) {
13613
+ if (this.values.length > 0) {
13614
+ yield this.values.shift();
13615
+ continue;
13616
+ }
13617
+ if (this.closed) {
13618
+ if (this.error !== void 0) throw this.error;
13619
+ return;
13620
+ }
13621
+ const next = await new Promise((resolve2) => this.waiters.push(resolve2));
13622
+ if (next.done) {
13623
+ if (this.error !== void 0) throw this.error;
13624
+ return;
13625
+ }
13626
+ yield next.value;
13627
+ }
13628
+ }
13629
+ };
13630
+ function routeStreamEvent2(event, streamId, queue) {
13631
+ if (event.event === "harness.remote.frame") {
13632
+ const data2 = event.data;
13633
+ if (data2.streamId === streamId && data2.hasValue === true) queue.push(data2.value);
13634
+ return;
13635
+ }
13636
+ if (event.event !== "harness.remote.stream.closed") return;
13637
+ const data = event.data;
13638
+ if (data.streamId !== streamId) return;
13639
+ if (data.reason === "failed") {
13640
+ const failure = data.failure;
13641
+ queue.fail(remoteFailure({
13642
+ code: typeof failure?.code === "string" ? failure.code : "internal",
13643
+ message: typeof failure?.message === "string" ? failure.message : "The remote Harness stream failed.",
13644
+ details: isRecord(failure?.details) ? failure.details : {}
13645
+ }));
13646
+ } else {
13647
+ queue.close();
13648
+ }
13649
+ }
13650
+ function parseRpcResult(value) {
13651
+ if (!isRecord(value)) throw new Error("The remote Host returned an invalid Gateway result.");
13652
+ if (value.ok === true) return Object.hasOwn(value, "value") ? { ok: true, value: value.value } : { ok: true };
13653
+ if (value.ok !== false || !isRecord(value.error) || typeof value.error.code !== "string" || typeof value.error.message !== "string" || !isRecord(value.error.details)) {
13654
+ throw new Error("The remote Host returned an invalid Gateway failure.");
13655
+ }
13656
+ return {
13657
+ ok: false,
13658
+ error: { code: value.error.code, message: value.error.message, details: value.error.details }
13659
+ };
13660
+ }
13661
+ function remoteFailure(failure) {
13662
+ return Object.assign(new Error(failure.message), { code: failure.code, details: failure.details });
13663
+ }
13664
+ function bytesToBase642(bytes) {
13665
+ let binary = "";
13666
+ for (let offset = 0; offset < bytes.byteLength; offset += 32768) {
13667
+ binary += String.fromCharCode(...bytes.subarray(offset, Math.min(offset + 32768, bytes.byteLength)));
13668
+ }
13669
+ return btoa(binary);
13670
+ }
13671
+ function base64ToBytes2(value) {
13672
+ let binary;
13673
+ try {
13674
+ binary = atob(value);
13675
+ } catch {
13676
+ throw new Error("The remote Host returned malformed Harness Remote transfer data.");
13677
+ }
13678
+ const bytes = new Uint8Array(binary.length);
13679
+ for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index);
13680
+ if (bytesToBase642(bytes) !== value) throw new Error("The remote Host returned non-canonical Harness Remote transfer data.");
13681
+ return bytes;
13682
+ }
13683
+ function isRecord(value) {
13684
+ return typeof value === "object" && value !== null && !Array.isArray(value);
13685
+ }
13686
+ function hasErrorCode(error, code) {
13687
+ return error instanceof Error && "code" in error && error.code === code;
13688
+ }
13689
+
13463
13690
  // src/server-api.ts
13464
13691
  import { platform } from "node:os";
13465
13692
 
@@ -13543,7 +13770,7 @@ function normalizeServerUrl(value) {
13543
13770
  }
13544
13771
 
13545
13772
  // src/version.ts
13546
- var PLUGIN_VERSION = "0.3.35";
13773
+ var PLUGIN_VERSION = "0.4.0";
13547
13774
 
13548
13775
  // src/server-api.ts
13549
13776
  var HostServerApi = class {
@@ -13620,7 +13847,8 @@ var HostServerApi = class {
13620
13847
  }
13621
13848
  return { qrId: value.qrId, scanUrl: value.scanUrl, expiresIn: value.expiresIn };
13622
13849
  }
13623
- async pollOAuthQrLogin(identity, qrId) {
13850
+ async pollOAuthQrLogin(identity, qrId, recoverIdentity) {
13851
+ this.bindIdentity(identity);
13624
13852
  const value = requireRecord(await this.publicRequest(
13625
13853
  `/api/v1/auth/oauth/qr/${encodeURIComponent(qrId)}`,
13626
13854
  { method: "GET" }
@@ -13637,11 +13865,19 @@ var HostServerApi = class {
13637
13865
  if (typeof account.account !== "string" || account.account.length === 0 || typeof account.isAdmin !== "boolean") {
13638
13866
  throw new ServerApiError("INVALID_MESSAGE", "The Server returned an invalid account profile.", false);
13639
13867
  }
13640
- await this.register(identity, {
13868
+ const authorization = {
13641
13869
  accountToken: value.token,
13642
13870
  account: account.account,
13643
13871
  authorizationMethod: "account"
13644
- });
13872
+ };
13873
+ try {
13874
+ await this.register(identity, authorization);
13875
+ } catch (error) {
13876
+ if (!(error instanceof ServerApiError) || error.code !== "DEVICE_REVOKED" || recoverIdentity === void 0) throw error;
13877
+ const nextIdentity = await recoverIdentity();
13878
+ this.bindIdentity(nextIdentity);
13879
+ await this.register(nextIdentity, authorization);
13880
+ }
13645
13881
  return {
13646
13882
  status: "complete",
13647
13883
  authorization: { method: "account", account: account.account, isAdmin: account.isAdmin }
@@ -13935,51 +14171,133 @@ function requireRecord(value, name2) {
13935
14171
  var REMOTE_COMMAND_METHODS = ["execute", "list"];
13936
14172
  var ALL_REMOTE_COMMANDS = { execute: true, list: true };
13937
14173
  var TypertGatewaySwitch = class {
13938
- constructor(gateway) {
13939
- this.gateway = gateway;
13940
- this.originalInvoke = gateway.invoke;
13941
- this.localInvoke = this.originalInvoke.bind(gateway);
13942
- }
14174
+ runtime;
13943
14175
  originalInvoke;
13944
14176
  localInvoke;
14177
+ originalStream;
14178
+ localStream;
14179
+ originalDispatch;
14180
+ localDispatch;
14181
+ originalOpen;
14182
+ localOpen;
13945
14183
  remoteInvoke;
14184
+ remoteTarget;
13946
14185
  remoteSupport = { execute: false, list: false };
14186
+ target;
13947
14187
  installed = false;
13948
- /** A facade that always invokes the original local gateway. */
14188
+ constructor(gateway) {
14189
+ this.runtime = gateway;
14190
+ this.originalInvoke = gateway.invoke;
14191
+ this.localInvoke = this.originalInvoke.bind(gateway);
14192
+ this.originalStream = gateway.stream;
14193
+ this.localStream = gateway.stream?.bind(gateway);
14194
+ this.originalDispatch = this.runtime.dispatchRpc;
14195
+ this.localDispatch = this.runtime.dispatchRpc?.bind(gateway);
14196
+ this.originalOpen = this.runtime.openWireStream;
14197
+ this.localOpen = this.runtime.openWireStream?.bind(gateway) ?? gateway.wireStream?.open.bind(gateway.wireStream);
14198
+ }
14199
+ /** Original local dispatcher, used by the Host bridge without switch recursion. */
13949
14200
  local() {
13950
- return { invoke: this.localInvoke };
14201
+ const dispatch = this.localDispatch ?? (async (endpoint, payload, signal) => {
14202
+ try {
14203
+ const request = requestFromCarrier(endpoint, payload, signal);
14204
+ return { ok: true, value: await this.localInvoke(request) };
14205
+ } catch (error) {
14206
+ return { ok: false, error: this.failure(error) };
14207
+ }
14208
+ });
14209
+ const open = this.localOpen ?? (async (endpoint, payload, signal) => {
14210
+ if (this.localStream === void 0) throw new Error("The local Harness Gateway does not support Remote streams.");
14211
+ return this.localStream(requestFromCarrier(endpoint, payload, signal));
14212
+ });
14213
+ return {
14214
+ invoke: this.localInvoke,
14215
+ ...this.localStream === void 0 ? {} : { stream: this.localStream },
14216
+ dispatch,
14217
+ open,
14218
+ failure: (error) => this.failure(error),
14219
+ supportsCarrier: this.localDispatch !== void 0 && this.localOpen !== void 0
14220
+ };
14221
+ }
14222
+ supportsCarrier() {
14223
+ return this.localDispatch !== void 0 && this.localOpen !== void 0;
14224
+ }
14225
+ status() {
14226
+ return this.remoteInvoke === void 0 ? { mode: "local" } : { mode: "remote", ...this.target === void 0 ? {} : { target: { ...this.target } } };
13951
14227
  }
13952
14228
  install() {
13953
14229
  if (this.installed) return;
13954
- this.gateway.invoke = (request) => {
13955
- if (request.namespace !== "commands" || !isRemoteCommandMethod(request.method) || this.remoteInvoke === void 0) {
13956
- return this.localInvoke(request);
13957
- }
13958
- if (this.remoteSupport[request.method]) return this.remoteInvoke(request);
13959
- if (request.method === "list") return Promise.resolve([]);
13960
- return this.localInvoke(request);
13961
- };
14230
+ this.runtime.invoke = (request) => this.selectInvoke(request);
14231
+ if (this.originalStream !== void 0) {
14232
+ this.runtime.stream = (request) => this.remoteTarget === void 0 ? this.localStream(request) : this.remoteTarget.open(endpointOf(request), { args: request.args }, request.signal ?? new AbortController().signal);
14233
+ }
14234
+ if (this.originalDispatch !== void 0) {
14235
+ this.runtime.dispatchRpc = (endpoint, payload, signal) => this.remoteTarget === void 0 ? this.localDispatch(endpoint, payload, signal) : this.remoteTarget.dispatch(endpoint, payload, signal);
14236
+ }
14237
+ if (this.originalOpen !== void 0) {
14238
+ this.runtime.openWireStream = (endpoint, payload, signal) => this.remoteTarget === void 0 ? this.localOpen(endpoint, payload, signal) : this.remoteTarget.open(endpoint, payload, signal);
14239
+ }
13962
14240
  this.installed = true;
13963
14241
  }
13964
- selectRemote(invoke, support = ALL_REMOTE_COMMANDS) {
14242
+ selectRemote(remote, support = ALL_REMOTE_COMMANDS, target) {
13965
14243
  if (!this.installed) throw new Error("The Typert gateway switch is not installed.");
13966
- this.remoteInvoke = invoke;
14244
+ this.remoteInvoke = typeof remote === "function" ? remote : (request) => remote.invoke(request);
14245
+ this.remoteTarget = typeof remote === "function" ? void 0 : remote;
13967
14246
  this.remoteSupport = { ...support };
14247
+ this.target = target === void 0 ? void 0 : { ...target };
13968
14248
  }
13969
14249
  selectLocal() {
13970
14250
  this.remoteInvoke = void 0;
14251
+ this.remoteTarget = void 0;
13971
14252
  this.remoteSupport = { execute: false, list: false };
14253
+ this.target = void 0;
13972
14254
  }
13973
14255
  restore() {
13974
14256
  if (!this.installed) return;
13975
14257
  this.selectLocal();
13976
- this.gateway.invoke = this.originalInvoke;
14258
+ this.runtime.invoke = this.originalInvoke;
14259
+ if (this.originalStream !== void 0) this.runtime.stream = this.originalStream;
14260
+ if (this.originalDispatch !== void 0) this.runtime.dispatchRpc = this.originalDispatch;
14261
+ if (this.originalOpen !== void 0) this.runtime.openWireStream = this.originalOpen;
13977
14262
  this.installed = false;
13978
14263
  }
14264
+ selectInvoke(request) {
14265
+ if (this.remoteTarget !== void 0) return this.remoteTarget.invoke(request);
14266
+ if (request.namespace !== "commands" || !isRemoteCommandMethod(request.method) || this.remoteInvoke === void 0) {
14267
+ return this.localInvoke(request);
14268
+ }
14269
+ if (this.remoteSupport[request.method]) return this.remoteInvoke(request);
14270
+ if (request.method === "list") return Promise.resolve([]);
14271
+ return this.localInvoke(request);
14272
+ }
14273
+ failure(error) {
14274
+ const normalized = this.runtime.wireStream?.failure(error);
14275
+ if (normalized !== void 0) return normalized;
14276
+ const source = error instanceof Error ? error : new Error("The Harness Gateway rejected the request.");
14277
+ const code = "code" in source && typeof source.code === "string" ? source.code : "internal";
14278
+ const details = "details" in source && isRecord2(source.details) ? source.details : {};
14279
+ return { code, message: source.message, details };
14280
+ }
13979
14281
  };
14282
+ function requestFromCarrier(endpoint, payload, signal) {
14283
+ const segments = endpoint.split("/");
14284
+ if (segments.length !== 2 || segments.some((segment) => segment.length === 0)) {
14285
+ throw new Error("The Harness Gateway endpoint is invalid.");
14286
+ }
14287
+ if (!isRecord2(payload) || !isRecord2(payload.args)) {
14288
+ throw new Error("The Harness Gateway payload is invalid.");
14289
+ }
14290
+ return { namespace: segments[0], method: segments[1], args: payload.args, signal };
14291
+ }
14292
+ function endpointOf(request) {
14293
+ return `${request.namespace}/${request.method}`;
14294
+ }
13980
14295
  function isRemoteCommandMethod(method) {
13981
14296
  return REMOTE_COMMAND_METHODS.includes(method);
13982
14297
  }
14298
+ function isRecord2(value) {
14299
+ return typeof value === "object" && value !== null && !Array.isArray(value);
14300
+ }
13983
14301
 
13984
14302
  // src/werift-rtc.ts
13985
14303
  import { createSocket } from "node:dgram";
@@ -15048,7 +15366,7 @@ var ClientModeRuntime = class {
15048
15366
  this.logger = logger;
15049
15367
  this.host = host;
15050
15368
  this.rtcFactoryProvider = rtcFactoryProvider;
15051
- this.proxySwitch = new ApiProxySwitch(apiProxy);
15369
+ this.proxySwitch = apiProxy === void 0 ? void 0 : new ApiProxySwitch(apiProxy);
15052
15370
  this.gatewaySwitch = new TypertGatewaySwitch(typertGateway);
15053
15371
  }
15054
15372
  identity;
@@ -15061,7 +15379,7 @@ var ClientModeRuntime = class {
15061
15379
  if (this.closed) throw new Error("client remote-mode runtime is closed");
15062
15380
  this.identity = await this.identities.loadOrCreate(this.config.deviceName);
15063
15381
  this.server.bindIdentity(this.identity);
15064
- this.proxySwitch.install();
15382
+ this.proxySwitch?.install();
15065
15383
  this.gatewaySwitch.install();
15066
15384
  this.logger.info("client remote-mode identity ready", {
15067
15385
  deviceId: shortId(this.identity.deviceId),
@@ -15074,13 +15392,14 @@ var ClientModeRuntime = class {
15074
15392
  });
15075
15393
  }
15076
15394
  status() {
15395
+ const targetStatus = this.gatewaySwitch.supportsCarrier() ? this.gatewaySwitch.status() : this.proxySwitch?.status() ?? this.gatewaySwitch.status();
15077
15396
  return {
15078
15397
  available: this.config.serverUrl !== void 0,
15079
15398
  identityReady: this.identity !== void 0,
15080
15399
  deviceId: this.identity?.deviceId,
15081
15400
  deviceName: this.identity?.name,
15082
15401
  serverUrl: this.config.serverUrl,
15083
- ...this.proxySwitch.status(),
15402
+ ...targetStatus,
15084
15403
  connected: this.connected !== void 0,
15085
15404
  transport: this.connected?.client.getStats().mode ?? "Disconnected",
15086
15405
  preferredTransports: this.config.forceRelay ? ["relay"] : ["lan", "p2p", "turn", "relay"],
@@ -15139,23 +15458,20 @@ var ClientModeRuntime = class {
15139
15458
  return this.server.startOAuthQrLogin(provider);
15140
15459
  }
15141
15460
  async pollClientOAuthQrLogin(qrId) {
15142
- try {
15143
- const result = await this.server.pollOAuthQrLogin(this.requireIdentity(), qrId);
15144
- if (result.status === "complete") this.logger.info("Client account authorized with QR login");
15145
- return result;
15146
- } catch (error) {
15147
- if (!(error instanceof ServerApiError) || error.code !== "DEVICE_REVOKED") throw error;
15461
+ const result = await this.server.pollOAuthQrLogin(this.requireIdentity(), qrId, async () => {
15148
15462
  this.identity = await this.identities.reset(this.config.deviceName);
15149
15463
  this.server.bindIdentity(this.identity);
15150
- this.logger.info("Rotated revoked Client identity before QR retry");
15151
- return { status: "expired" };
15152
- }
15464
+ this.logger.info("Rotated revoked Client identity before QR authorization retry");
15465
+ return this.identity;
15466
+ });
15467
+ if (result.status === "complete") this.logger.info("Client account authorized with QR login");
15468
+ return result;
15153
15469
  }
15154
15470
  async clearClientAuthorization() {
15155
15471
  const previous = this.connected;
15156
15472
  this.connected = void 0;
15157
15473
  this.pendingWorkspaceSelection = void 0;
15158
- this.proxySwitch.selectLocal();
15474
+ this.proxySwitch?.selectLocal();
15159
15475
  this.gatewaySwitch.selectLocal();
15160
15476
  await previous?.client.close().catch(() => void 0);
15161
15477
  await this.server.revokeCurrentDevice();
@@ -15174,7 +15490,7 @@ var ClientModeRuntime = class {
15174
15490
  }
15175
15491
  async setMode(mode, targetDeviceId, signal) {
15176
15492
  if (mode === "local") {
15177
- this.proxySwitch.selectLocal();
15493
+ this.proxySwitch?.selectLocal();
15178
15494
  this.gatewaySwitch.selectLocal();
15179
15495
  const previous2 = this.connected;
15180
15496
  this.connected = void 0;
@@ -15187,18 +15503,31 @@ var ClientModeRuntime = class {
15187
15503
  throw new ClientModeError("INVALID_MESSAGE", "A targetDeviceId is required for remote mode.");
15188
15504
  }
15189
15505
  const next = await this.connect(targetDeviceId, signal);
15506
+ try {
15507
+ this.assertRemoteCompatible(next);
15508
+ } catch (error) {
15509
+ await next.client.close().catch(() => void 0);
15510
+ throw error;
15511
+ }
15190
15512
  const previous = this.connected;
15191
15513
  this.connected = next;
15192
15514
  this.pendingWorkspaceSelection = void 0;
15193
- const remoteApi = new RemoteHarnessApiProxy(next.client).api;
15194
- this.proxySwitch.selectRemote(remoteApi, { deviceId: next.target.deviceId, name: next.target.name });
15195
- this.selectRemoteCommands(next);
15515
+ this.selectRemoteTarget(next);
15196
15516
  await previous?.client.close().catch(() => void 0);
15197
15517
  this.logger.info("Harness target switched", { mode: "remote", targetDeviceId: shortId(next.target.deviceId) });
15198
15518
  return this.status();
15199
15519
  }
15200
15520
  async listRemoteDirectory(targetDeviceId, path, signal) {
15201
15521
  const remote = await this.ensureConnected(targetDeviceId, signal);
15522
+ if (remote.features.remoteGateway) {
15523
+ const value = await new RemoteTypertGateway(remote.client).invoke({
15524
+ namespace: "directoryPicker",
15525
+ method: "list",
15526
+ args: path === void 0 ? {} : { path },
15527
+ ...signal === void 0 ? {} : { signal }
15528
+ });
15529
+ return value;
15530
+ }
15202
15531
  const api = new RemoteHarnessApiProxy(remote.client).api;
15203
15532
  const response = await api.host.listDirectory({
15204
15533
  rpcId: `remote-directory-${Date.now()}`,
@@ -15208,6 +15537,9 @@ var ClientModeRuntime = class {
15208
15537
  }
15209
15538
  async listRemoteWorkspaces(targetDeviceId, signal) {
15210
15539
  const remote = await this.ensureConnected(targetDeviceId, signal);
15540
+ if (remote.features.remoteGateway) {
15541
+ return readRemoteWorkspaceBaseline(new RemoteTypertGateway(remote.client), signal);
15542
+ }
15211
15543
  const api = new RemoteHarnessApiProxy(remote.client).api;
15212
15544
  const response = await api.workspace.list({
15213
15545
  rpcId: `remote-workspaces-${Date.now()}`,
@@ -15219,14 +15551,24 @@ var ClientModeRuntime = class {
15219
15551
  async openRemoteWorkspace(targetDeviceId, path, signal) {
15220
15552
  if (path.trim() === "") throw new ClientModeError("INVALID_MESSAGE", "A remote working directory is required.");
15221
15553
  const remote = await this.ensureConnected(targetDeviceId, signal);
15222
- const api = new RemoteHarnessApiProxy(remote.client).api;
15223
- const response = await api.workspace.create({
15224
- rpcId: `remote-workspace-${Date.now()}`,
15225
- payload: { path }
15226
- });
15227
- const workspace = unwrapNativeResult(response);
15228
- this.proxySwitch.selectRemote(api, { deviceId: remote.target.deviceId, name: remote.target.name });
15229
- this.selectRemoteCommands(remote);
15554
+ this.assertRemoteCompatible(remote);
15555
+ let workspace;
15556
+ if (remote.features.remoteGateway) {
15557
+ workspace = await new RemoteTypertGateway(remote.client).invoke({
15558
+ namespace: "workspace",
15559
+ method: "create",
15560
+ args: { request: { path } },
15561
+ ...signal === void 0 ? {} : { signal }
15562
+ });
15563
+ } else {
15564
+ const api = new RemoteHarnessApiProxy(remote.client).api;
15565
+ const response = await api.workspace.create({
15566
+ rpcId: `remote-workspace-${Date.now()}`,
15567
+ payload: { path }
15568
+ });
15569
+ workspace = unwrapNativeResult(response);
15570
+ }
15571
+ this.selectRemoteTarget(remote);
15230
15572
  const workspaceId = workspaceRecordId(workspace.workspace);
15231
15573
  this.pendingWorkspaceSelection = { targetDeviceId: remote.target.deviceId, workspaceId };
15232
15574
  this.logger.info("Remote workspace opened", { targetDeviceId: shortId(remote.target.deviceId) });
@@ -15242,17 +15584,17 @@ var ClientModeRuntime = class {
15242
15584
  async close() {
15243
15585
  if (this.closed) return;
15244
15586
  this.closed = true;
15245
- this.proxySwitch.selectLocal();
15587
+ this.proxySwitch?.selectLocal();
15246
15588
  this.gatewaySwitch.selectLocal();
15247
15589
  this.pendingWorkspaceSelection = void 0;
15248
15590
  await this.connected?.client.close().catch(() => void 0);
15249
15591
  this.connected = void 0;
15250
- this.proxySwitch.restore();
15592
+ this.proxySwitch?.restore();
15251
15593
  this.gatewaySwitch.restore();
15252
15594
  }
15253
15595
  async callRemoteFileViewer(endpoint, payload, signal) {
15254
15596
  const remote = this.connected;
15255
- if (remote === void 0 || this.proxySwitch.status().mode !== "remote") {
15597
+ if (remote === void 0 || this.status().mode !== "remote") {
15256
15598
  throw new ClientModeError("REMOTE_NOT_CONNECTED", "No Remote Host is selected.", true);
15257
15599
  }
15258
15600
  if (!remote.features.fileViewer) {
@@ -15260,11 +15602,25 @@ var ClientModeRuntime = class {
15260
15602
  }
15261
15603
  return remote.client.rpc("fileviewer.call", { endpoint, payload }, signal);
15262
15604
  }
15263
- selectRemoteCommands(remote) {
15605
+ selectRemoteTarget(remote) {
15606
+ const target = { deviceId: remote.target.deviceId, name: remote.target.name };
15607
+ if (this.gatewaySwitch.supportsCarrier()) {
15608
+ this.gatewaySwitch.selectRemote(new RemoteTypertGateway(remote.client), void 0, target);
15609
+ return;
15610
+ }
15611
+ this.proxySwitch.selectRemote(new RemoteHarnessApiProxy(remote.client).api, target);
15264
15612
  this.gatewaySwitch.selectRemote((request) => invokeRemoteCommand(remote.client, request), {
15265
15613
  execute: true,
15266
15614
  list: remote.features.commandList
15267
- });
15615
+ }, target);
15616
+ }
15617
+ assertRemoteCompatible(remote) {
15618
+ const localRemoteGateway = this.gatewaySwitch.supportsCarrier();
15619
+ if (localRemoteGateway && remote.features.remoteGateway || !localRemoteGateway && this.proxySwitch !== void 0 && remote.features.apiProxy) return;
15620
+ throw new ClientModeError(
15621
+ "HARNESS_VERSION_INCOMPATIBLE",
15622
+ localRemoteGateway ? "The selected Host does not provide the Harness alpha Remote Gateway transport." : "The selected Host does not provide the legacy Harness ApiProxy transport."
15623
+ );
15268
15624
  }
15269
15625
  async connect(targetDeviceId, signal) {
15270
15626
  signal?.throwIfAborted();
@@ -15331,7 +15687,7 @@ var ClientModeRuntime = class {
15331
15687
  if (this.connected?.client !== connectedClient) return;
15332
15688
  this.connected = void 0;
15333
15689
  this.pendingWorkspaceSelection = void 0;
15334
- this.proxySwitch.selectLocal();
15690
+ this.proxySwitch?.selectLocal();
15335
15691
  this.gatewaySwitch.selectLocal();
15336
15692
  void connectedClient.close().catch(() => void 0);
15337
15693
  this.logger.warn("remote Harness transport closed; falling back to local mode", {
@@ -15344,7 +15700,8 @@ var ClientModeRuntime = class {
15344
15700
  transport: connectedClient.getStats().mode,
15345
15701
  ...webrtcDiagnosticsLogFields(connectionDetails?.webRtc?.diagnostics)
15346
15702
  });
15347
- return { client: connectedClient, target, transport: connectedTransport, features: remoteHostFeatures(serverDevice.clientVersion) };
15703
+ const features = await probeRemoteHostFeatures(connectedClient, serverDevice.clientVersion);
15704
+ return { client: connectedClient, target, transport: connectedTransport, features };
15348
15705
  } catch (error) {
15349
15706
  await client?.close().catch(() => void 0);
15350
15707
  throw error;
@@ -15513,6 +15870,9 @@ function record(value) {
15513
15870
  }
15514
15871
  return value;
15515
15872
  }
15873
+ function isRecord3(value) {
15874
+ return typeof value === "object" && value !== null && !Array.isArray(value);
15875
+ }
15516
15876
  function ok(value) {
15517
15877
  return { ok: true, value };
15518
15878
  }
@@ -15528,6 +15888,27 @@ async function invokeRemoteCommand(client, request) {
15528
15888
  }
15529
15889
  return unwrapNativeResult(response);
15530
15890
  }
15891
+ async function readRemoteWorkspaceBaseline(gateway, signal) {
15892
+ const lifetime = new AbortController();
15893
+ const activeSignal = signal === void 0 ? lifetime.signal : AbortSignal.any([signal, lifetime.signal]);
15894
+ const source = await gateway.open("workspace/follow", { args: {} }, activeSignal);
15895
+ const iterator = source[Symbol.asyncIterator]();
15896
+ try {
15897
+ const first = await iterator.next();
15898
+ if (first.done || !isRecord3(first.value) || first.value.type !== "baseline" || !isRecord3(first.value.value) || !Array.isArray(first.value.value.items)) {
15899
+ throw new ClientModeError("INVALID_MESSAGE", "The remote Host returned an invalid Workspace baseline.");
15900
+ }
15901
+ return first.value.value.items.map((item) => {
15902
+ if (!isRecord3(item) || typeof item.workspaceId !== "string" || typeof item.path !== "string" || typeof item.title !== "string") {
15903
+ throw new ClientModeError("INVALID_MESSAGE", "The remote Host returned an invalid Workspace row.");
15904
+ }
15905
+ return { workspaceId: item.workspaceId, path: item.path, title: item.title };
15906
+ });
15907
+ } finally {
15908
+ lifetime.abort("workspace-baseline-read");
15909
+ await iterator.return?.();
15910
+ }
15911
+ }
15531
15912
  function unwrapNativeResult(response) {
15532
15913
  const result = response.result;
15533
15914
  if (typeof result !== "object" || result === null || !("ok" in result)) {
@@ -15564,7 +15945,34 @@ function shortId(value) {
15564
15945
  function remoteHostFeatures(clientVersion) {
15565
15946
  return {
15566
15947
  commandList: isVersionAtLeast(clientVersion, REMOTE_COMMAND_LIST_MIN_VERSION),
15567
- fileViewer: isVersionAtLeast(clientVersion, REMOTE_FILE_VIEWER_MIN_VERSION)
15948
+ fileViewer: isVersionAtLeast(clientVersion, REMOTE_FILE_VIEWER_MIN_VERSION),
15949
+ apiProxy: true,
15950
+ remoteGateway: false
15951
+ };
15952
+ }
15953
+ async function probeRemoteHostFeatures(client, clientVersion) {
15954
+ const fallback = remoteHostFeatures(clientVersion);
15955
+ let value;
15956
+ try {
15957
+ value = await client.rpc("harness.transport.describe", {});
15958
+ } catch (error) {
15959
+ if (error instanceof Error && "code" in error && error.code === "METHOD_NOT_FOUND") return fallback;
15960
+ throw error;
15961
+ }
15962
+ if (!isRecord3(value) || !Array.isArray(value.capabilities) || value.capabilities.some((capability) => typeof capability !== "string")) {
15963
+ throw new ClientModeError("INVALID_MESSAGE", "The remote Host returned invalid transport capabilities.");
15964
+ }
15965
+ const capabilities = new Set(value.capabilities);
15966
+ const apiProxy = capabilities.has("harness.api.v1");
15967
+ const remoteGateway = capabilities.has("harness.remote.v1");
15968
+ if (!apiProxy && !remoteGateway) {
15969
+ throw new ClientModeError("FEATURE_NOT_SUPPORTED", "The remote Host exposes no supported Harness transport.");
15970
+ }
15971
+ return {
15972
+ commandList: remoteGateway || apiProxy && fallback.commandList,
15973
+ fileViewer: capabilities.has("fileviewer.read.v1"),
15974
+ apiProxy,
15975
+ remoteGateway
15568
15976
  };
15569
15977
  }
15570
15978
  function isVersionAtLeast(value, minimum) {
@@ -16045,11 +16453,11 @@ function editableConfig(config) {
16045
16453
  } : false
16046
16454
  };
16047
16455
  }
16048
- function isRecord(value) {
16456
+ function isRecord4(value) {
16049
16457
  return typeof value === "object" && value !== null && !Array.isArray(value);
16050
16458
  }
16051
16459
  function record2(value) {
16052
- if (!isRecord(value)) throw new ClientModeError("INVALID_MESSAGE", "The control request payload is invalid.");
16460
+ if (!isRecord4(value)) throw new ClientModeError("INVALID_MESSAGE", "The control request payload is invalid.");
16053
16461
  return value;
16054
16462
  }
16055
16463
  function ok2(value) {
@@ -16103,7 +16511,6 @@ function redact(value, key = "") {
16103
16511
 
16104
16512
  // src/service.ts
16105
16513
  import { randomUUID } from "node:crypto";
16106
- import { RpcId } from "@deepseek-ai/dsh-host-apiproxy/api";
16107
16514
 
16108
16515
  // src/connection-controller.ts
16109
16516
  var ConnectionController = class {
@@ -16320,7 +16727,9 @@ function safeErrorCode(error) {
16320
16727
 
16321
16728
  // src/rpc-router.ts
16322
16729
  var wireRequestSchema = external_exports.object({ method: external_exports.string().min(1), params: external_exports.unknown() }).strict();
16730
+ var emptyParamsSchema = external_exports.object({}).strict();
16323
16731
  var apiMethods = /* @__PURE__ */ new Set([
16732
+ "harness.transport.describe",
16324
16733
  "harness.api.call",
16325
16734
  "harness.api.transfer.open",
16326
16735
  "harness.api.transfer.chunk",
@@ -16330,19 +16739,38 @@ var apiMethods = /* @__PURE__ */ new Set([
16330
16739
  "harness.api.respond",
16331
16740
  "harness.api.stream.open",
16332
16741
  "harness.api.stream.close",
16742
+ "harness.remote.call",
16743
+ "harness.remote.transfer.open",
16744
+ "harness.remote.transfer.chunk",
16745
+ "harness.remote.transfer.commit",
16746
+ "harness.remote.transfer.read",
16747
+ "harness.remote.transfer.close",
16748
+ "harness.remote.stream.open",
16749
+ "harness.remote.stream.close",
16333
16750
  "fileviewer.call"
16334
16751
  ]);
16335
- var HOST_CAPABILITIES = ["harness.api.v1", "harness.api.transfer.v1", "fileviewer.read.v1"];
16752
+ var HOST_CAPABILITIES = [
16753
+ "harness.api.v1",
16754
+ "harness.api.transfer.v1",
16755
+ "harness.remote.v1",
16756
+ "harness.remote.transfer.v1",
16757
+ "fileviewer.read.v1"
16758
+ ];
16336
16759
  var RpcRouter = class {
16337
- constructor(harnessApi, maxPending = 128, logger, fileViewer) {
16760
+ constructor(harnessApi, maxPending = 128, logger, fileViewer, harnessRemote, capabilities = () => HOST_CAPABILITIES) {
16338
16761
  this.harnessApi = harnessApi;
16339
16762
  this.maxPending = maxPending;
16340
16763
  this.logger = logger;
16341
16764
  this.fileViewer = fileViewer;
16765
+ this.harnessRemote = harnessRemote;
16766
+ this.capabilities = capabilities;
16342
16767
  }
16343
16768
  active = 0;
16344
- closePeerStreams() {
16345
- return this.harnessApi.closeAll();
16769
+ async closePeerStreams() {
16770
+ await Promise.all([
16771
+ this.harnessApi?.closeAll(),
16772
+ this.harnessRemote?.closeAll()
16773
+ ]);
16346
16774
  }
16347
16775
  async handle(message) {
16348
16776
  if (message.type !== "rpc.request") {
@@ -16381,24 +16809,44 @@ var RpcRouter = class {
16381
16809
  }
16382
16810
  invoke(method, params) {
16383
16811
  switch (method) {
16812
+ case "harness.transport.describe": {
16813
+ emptyParamsSchema.parse(params);
16814
+ return { capabilities: [...this.capabilities()] };
16815
+ }
16384
16816
  case "harness.api.call":
16385
- return this.harnessApi.call(params);
16817
+ return this.requireApiProxy().call(params);
16386
16818
  case "harness.api.transfer.open":
16387
- return this.harnessApi.openTransfer(params);
16819
+ return this.requireApiProxy().openTransfer(params);
16388
16820
  case "harness.api.transfer.chunk":
16389
- return this.harnessApi.appendTransfer(params);
16821
+ return this.requireApiProxy().appendTransfer(params);
16390
16822
  case "harness.api.transfer.commit":
16391
- return this.harnessApi.commitTransfer(params);
16823
+ return this.requireApiProxy().commitTransfer(params);
16392
16824
  case "harness.api.transfer.read":
16393
- return this.harnessApi.readTransfer(params);
16825
+ return this.requireApiProxy().readTransfer(params);
16394
16826
  case "harness.api.transfer.close":
16395
- return this.harnessApi.closeTransfer(params);
16827
+ return this.requireApiProxy().closeTransfer(params);
16396
16828
  case "harness.api.respond":
16397
- return this.harnessApi.respond(params);
16829
+ return this.requireApiProxy().respond(params);
16398
16830
  case "harness.api.stream.open":
16399
- return this.harnessApi.openStream(params);
16831
+ return this.requireApiProxy().openStream(params);
16400
16832
  case "harness.api.stream.close":
16401
- return this.harnessApi.closeStream(params);
16833
+ return this.requireApiProxy().closeStream(params);
16834
+ case "harness.remote.call":
16835
+ return this.requireRemoteGateway().call(params);
16836
+ case "harness.remote.transfer.open":
16837
+ return this.requireRemoteGateway().openTransfer(params);
16838
+ case "harness.remote.transfer.chunk":
16839
+ return this.requireRemoteGateway().appendTransfer(params);
16840
+ case "harness.remote.transfer.commit":
16841
+ return this.requireRemoteGateway().commitTransfer(params);
16842
+ case "harness.remote.transfer.read":
16843
+ return this.requireRemoteGateway().readTransfer(params);
16844
+ case "harness.remote.transfer.close":
16845
+ return this.requireRemoteGateway().closeTransfer(params);
16846
+ case "harness.remote.stream.open":
16847
+ return this.requireRemoteGateway().openStream(params);
16848
+ case "harness.remote.stream.close":
16849
+ return this.requireRemoteGateway().closeStream(params);
16402
16850
  case "fileviewer.call": {
16403
16851
  if (this.fileViewer === void 0) {
16404
16852
  throw new RpcError("FILE_VIEWER_UNAVAILABLE", "The Remote Host does not have DSH File Viewer available.");
@@ -16409,6 +16857,18 @@ var RpcRouter = class {
16409
16857
  throw new RpcError("METHOD_NOT_FOUND", "The requested method does not exist.");
16410
16858
  }
16411
16859
  }
16860
+ requireApiProxy() {
16861
+ if (this.harnessApi === void 0) {
16862
+ throw new RpcError("FEATURE_NOT_SUPPORTED", "This Harness version does not provide the legacy ApiProxy transport.");
16863
+ }
16864
+ return this.harnessApi;
16865
+ }
16866
+ requireRemoteGateway() {
16867
+ if (this.harnessRemote === void 0) {
16868
+ throw new RpcError("FEATURE_NOT_SUPPORTED", "This Harness version does not provide the Remote Gateway transport.");
16869
+ }
16870
+ return this.harnessRemote;
16871
+ }
16412
16872
  };
16413
16873
  function errorResponse(requestId, error) {
16414
16874
  const code = safeErrorCode(error);
@@ -18063,28 +18523,333 @@ function concatChunks(chunks, totalBytes) {
18063
18523
  return result;
18064
18524
  }
18065
18525
 
18526
+ // src/harness-remote-bridge.ts
18527
+ var endpointSchema = external_exports.string().min(1).max(128).regex(/^(?:\$events(?:\/result)?|[A-Za-z0-9_$.-]+\/[A-Za-z0-9_$.-]+)$/);
18528
+ var callSchema3 = external_exports.object({ endpoint: endpointSchema, payload: external_exports.unknown() }).strict();
18529
+ var streamOpenSchema2 = external_exports.object({
18530
+ streamId: external_exports.string().min(1).max(128),
18531
+ endpoint: endpointSchema,
18532
+ payload: external_exports.unknown()
18533
+ }).strict();
18534
+ var streamCloseSchema2 = external_exports.object({ streamId: external_exports.string().min(1).max(128) }).strict();
18535
+ var transferOpenSchema2 = external_exports.object({
18536
+ transferId: external_exports.string().uuid(),
18537
+ totalBytes: external_exports.number().int().positive().max(MAX_HARNESS_API_TRANSFER_BYTES),
18538
+ totalChunks: external_exports.number().int().positive()
18539
+ }).strict();
18540
+ var transferChunkSchema2 = external_exports.object({
18541
+ transferId: external_exports.string().uuid(),
18542
+ index: external_exports.number().int().nonnegative(),
18543
+ data: external_exports.string().min(1)
18544
+ }).strict();
18545
+ var transferIdSchema2 = external_exports.object({ transferId: external_exports.string().uuid() }).strict();
18546
+ var transferReadSchema2 = external_exports.object({
18547
+ transferId: external_exports.string().uuid(),
18548
+ index: external_exports.number().int().nonnegative()
18549
+ }).strict();
18550
+ var MAX_ACTIVE_STREAMS = 16;
18551
+ var MAX_ACTIVE_TRANSFERS = 2;
18552
+ var TRANSFER_IDLE_MS = 2 * 6e4;
18553
+ var INLINE_TRANSFER_RESPONSE_BYTES2 = 2 * 1024 * 1024;
18554
+ var HARNESS_REMOTE_ALLOWLIST = [
18555
+ "$events",
18556
+ "$events/result",
18557
+ "agentPresets/list",
18558
+ "agentPresets/read",
18559
+ "agentPresets/select",
18560
+ "commands/execute",
18561
+ "commands/list",
18562
+ "credentials/describe",
18563
+ "credentials/set",
18564
+ "credentials/unset",
18565
+ "directoryPicker/list",
18566
+ "fileReferences/list",
18567
+ "goals/clear",
18568
+ "goals/complete",
18569
+ "goals/create",
18570
+ "goals/edit",
18571
+ "goals/pause",
18572
+ "goals/resume",
18573
+ "llm/discoverModels",
18574
+ "llm/listConfigurableProviders",
18575
+ "llm/listProviders",
18576
+ "messageFeedback/delete",
18577
+ "messageFeedback/list",
18578
+ "messageFeedback/put",
18579
+ "pluginInventory/list",
18580
+ "session/attachment",
18581
+ "session/cancel",
18582
+ "session/control",
18583
+ "session/create",
18584
+ "session/follow",
18585
+ "session/fork",
18586
+ "session/list",
18587
+ "session/modelCatalog",
18588
+ "session/page",
18589
+ "session/prompt",
18590
+ "session/rename",
18591
+ "session/search",
18592
+ "session/selectModel",
18593
+ "session/updateQueue",
18594
+ "sessionReferenceResolver/candidates",
18595
+ "settings/describe",
18596
+ "settings/mutate",
18597
+ "settings/replace",
18598
+ "settings/update",
18599
+ "skills/list",
18600
+ "subagents/interruptByParent",
18601
+ "subagents/list",
18602
+ "subagents/prompt",
18603
+ "workspace/archiveSession",
18604
+ "workspace/create",
18605
+ "workspace/delete",
18606
+ "workspace/follow",
18607
+ "workspace/insertBefore",
18608
+ "workspace/insertSessionBefore",
18609
+ "workspace/rename"
18610
+ ];
18611
+ var allowedEndpoints = new Set(HARNESS_REMOTE_ALLOWLIST);
18612
+ var HarnessRemoteBridge = class {
18613
+ constructor(gateway, publish, logger) {
18614
+ this.gateway = gateway;
18615
+ this.publish = publish;
18616
+ this.logger = logger;
18617
+ }
18618
+ streams = /* @__PURE__ */ new Map();
18619
+ incomingTransfers = /* @__PURE__ */ new Map();
18620
+ outgoingTransfers = /* @__PURE__ */ new Map();
18621
+ async call(input) {
18622
+ const params = callSchema3.parse(input);
18623
+ this.assertAllowed(params.endpoint);
18624
+ const startedAt = performance.now();
18625
+ try {
18626
+ const result = await this.gateway.dispatch(params.endpoint, params.payload, AbortSignal.timeout(6e4));
18627
+ this.logger?.debug("harness remote call ok", {
18628
+ endpoint: params.endpoint,
18629
+ durationMs: Math.round(performance.now() - startedAt)
18630
+ });
18631
+ return result;
18632
+ } catch (error) {
18633
+ this.logger?.warn("harness remote call failed", {
18634
+ endpoint: params.endpoint,
18635
+ durationMs: Math.round(performance.now() - startedAt),
18636
+ code: this.gateway.failure(error).code
18637
+ });
18638
+ throw error;
18639
+ }
18640
+ }
18641
+ openTransfer(input) {
18642
+ this.pruneTransfers();
18643
+ const params = transferOpenSchema2.parse(input);
18644
+ if (params.totalChunks !== Math.ceil(params.totalBytes / HARNESS_API_TRANSFER_CHUNK_BYTES)) {
18645
+ throw new RpcError("INVALID_MESSAGE", "The Harness Remote transfer chunk count is invalid.");
18646
+ }
18647
+ if (this.incomingTransfers.has(params.transferId) || this.outgoingTransfers.has(params.transferId)) {
18648
+ throw new RpcError("REQUEST_CONFLICT", "The Harness Remote transfer id is already active.");
18649
+ }
18650
+ if (this.incomingTransfers.size >= MAX_ACTIVE_TRANSFERS) {
18651
+ throw new RpcError("RATE_LIMITED", "Too many Harness Remote transfers are active.", void 0, true);
18652
+ }
18653
+ this.incomingTransfers.set(params.transferId, {
18654
+ totalBytes: params.totalBytes,
18655
+ totalChunks: params.totalChunks,
18656
+ chunks: [],
18657
+ receivedBytes: 0,
18658
+ touchedAt: Date.now()
18659
+ });
18660
+ return { opened: true, transferId: params.transferId };
18661
+ }
18662
+ appendTransfer(input) {
18663
+ this.pruneTransfers();
18664
+ const params = transferChunkSchema2.parse(input);
18665
+ const transfer = this.incomingTransfers.get(params.transferId);
18666
+ if (transfer === void 0) throw new RpcError("TRANSFER_NOT_FOUND", "The Harness Remote transfer is not active.");
18667
+ if (params.index !== transfer.chunks.length || params.index >= transfer.totalChunks) {
18668
+ this.incomingTransfers.delete(params.transferId);
18669
+ throw new RpcError("INVALID_MESSAGE", "Harness Remote transfer chunks must arrive exactly once and in order.");
18670
+ }
18671
+ const chunk = decodeCanonicalBase642(params.data);
18672
+ const expectedBytes = Math.min(
18673
+ HARNESS_API_TRANSFER_CHUNK_BYTES,
18674
+ transfer.totalBytes - params.index * HARNESS_API_TRANSFER_CHUNK_BYTES
18675
+ );
18676
+ if (chunk.byteLength !== expectedBytes) {
18677
+ this.incomingTransfers.delete(params.transferId);
18678
+ throw new RpcError("INVALID_MESSAGE", "The Harness Remote transfer chunk size is invalid.");
18679
+ }
18680
+ transfer.chunks.push(chunk);
18681
+ transfer.receivedBytes += chunk.byteLength;
18682
+ transfer.touchedAt = Date.now();
18683
+ return { accepted: true, transferId: params.transferId, index: params.index };
18684
+ }
18685
+ async commitTransfer(input) {
18686
+ this.pruneTransfers();
18687
+ const params = transferIdSchema2.parse(input);
18688
+ const transfer = this.incomingTransfers.get(params.transferId);
18689
+ if (transfer === void 0) throw new RpcError("TRANSFER_NOT_FOUND", "The Harness Remote transfer is not active.");
18690
+ this.incomingTransfers.delete(params.transferId);
18691
+ if (transfer.chunks.length !== transfer.totalChunks || transfer.receivedBytes !== transfer.totalBytes) {
18692
+ throw new RpcError("INVALID_MESSAGE", "The Harness Remote transfer is incomplete.");
18693
+ }
18694
+ let request;
18695
+ try {
18696
+ request = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(concatChunks2(transfer.chunks, transfer.totalBytes)));
18697
+ } catch {
18698
+ throw new RpcError("INVALID_MESSAGE", "The Harness Remote transfer does not contain a valid request.");
18699
+ }
18700
+ const response = await this.call(callSchema3.parse(request));
18701
+ const responseBytes = new TextEncoder().encode(JSON.stringify(response));
18702
+ if (responseBytes.byteLength <= INLINE_TRANSFER_RESPONSE_BYTES2) return { kind: "inline", response };
18703
+ if (responseBytes.byteLength > MAX_HARNESS_API_TRANSFER_BYTES) {
18704
+ throw new RpcError("RESPONSE_TOO_LARGE", "The Harness Remote response exceeds the bounded transfer limit.");
18705
+ }
18706
+ if (this.outgoingTransfers.size >= MAX_ACTIVE_TRANSFERS) {
18707
+ throw new RpcError("RATE_LIMITED", "Too many Harness Remote response transfers are active.", void 0, true);
18708
+ }
18709
+ const totalChunks = Math.ceil(responseBytes.byteLength / HARNESS_API_TRANSFER_CHUNK_BYTES);
18710
+ this.outgoingTransfers.set(params.transferId, {
18711
+ bytes: responseBytes,
18712
+ totalChunks,
18713
+ nextIndex: 0,
18714
+ touchedAt: Date.now()
18715
+ });
18716
+ return { kind: "chunked", transferId: params.transferId, totalBytes: responseBytes.byteLength, totalChunks };
18717
+ }
18718
+ readTransfer(input) {
18719
+ this.pruneTransfers();
18720
+ const params = transferReadSchema2.parse(input);
18721
+ const transfer = this.outgoingTransfers.get(params.transferId);
18722
+ if (transfer === void 0) throw new RpcError("TRANSFER_NOT_FOUND", "The Harness Remote response transfer is not active.");
18723
+ if (params.index !== transfer.nextIndex || params.index >= transfer.totalChunks) {
18724
+ this.outgoingTransfers.delete(params.transferId);
18725
+ throw new RpcError("INVALID_MESSAGE", "Harness Remote response chunks must be read exactly once and in order.");
18726
+ }
18727
+ const start = params.index * HARNESS_API_TRANSFER_CHUNK_BYTES;
18728
+ const end = Math.min(start + HARNESS_API_TRANSFER_CHUNK_BYTES, transfer.bytes.byteLength);
18729
+ transfer.nextIndex += 1;
18730
+ transfer.touchedAt = Date.now();
18731
+ return {
18732
+ transferId: params.transferId,
18733
+ index: params.index,
18734
+ data: Buffer.from(transfer.bytes.subarray(start, end)).toString("base64")
18735
+ };
18736
+ }
18737
+ closeTransfer(input) {
18738
+ const params = transferIdSchema2.parse(input);
18739
+ const closed = this.incomingTransfers.delete(params.transferId) || this.outgoingTransfers.delete(params.transferId);
18740
+ return { closed, transferId: params.transferId };
18741
+ }
18742
+ async openStream(input) {
18743
+ const params = streamOpenSchema2.parse(input);
18744
+ this.assertAllowed(params.endpoint);
18745
+ if (this.streams.has(params.streamId)) throw new RpcError("REQUEST_CONFLICT", "The Harness Remote stream is already open.");
18746
+ if (this.streams.size >= MAX_ACTIVE_STREAMS) {
18747
+ throw new RpcError("RATE_LIMITED", "Too many Harness Remote streams are open.", void 0, true);
18748
+ }
18749
+ const controller = new AbortController();
18750
+ const source = await this.gateway.open(params.endpoint, params.payload, controller.signal);
18751
+ this.streams.set(params.streamId, { controller });
18752
+ void this.pump(params.streamId, source, controller.signal);
18753
+ return { opened: true, streamId: params.streamId };
18754
+ }
18755
+ closeStream(input) {
18756
+ const params = streamCloseSchema2.parse(input);
18757
+ const active = this.streams.get(params.streamId);
18758
+ if (active !== void 0) {
18759
+ this.streams.delete(params.streamId);
18760
+ active.controller.abort("client-closed");
18761
+ }
18762
+ return { closed: active !== void 0, streamId: params.streamId };
18763
+ }
18764
+ async closeAll(reason = "peer-disconnected") {
18765
+ const streams = [...this.streams.entries()];
18766
+ this.streams.clear();
18767
+ this.incomingTransfers.clear();
18768
+ this.outgoingTransfers.clear();
18769
+ for (const [, stream] of streams) stream.controller.abort(reason);
18770
+ }
18771
+ assertAllowed(endpoint) {
18772
+ if (!allowedEndpoints.has(endpoint)) {
18773
+ throw new RpcError("METHOD_NOT_ALLOWED", "The requested Harness Remote endpoint is not allowed.");
18774
+ }
18775
+ if (endpoint === "$events" && !this.gateway.supportsCarrier) {
18776
+ throw new RpcError("FEATURE_NOT_SUPPORTED", "This Harness version does not support Remote event transport.");
18777
+ }
18778
+ }
18779
+ async pump(streamId, source, signal) {
18780
+ let reason = "completed";
18781
+ let failure;
18782
+ try {
18783
+ for await (const value of source) {
18784
+ if (signal.aborted) break;
18785
+ await this.publish("harness.remote.frame", {
18786
+ streamId,
18787
+ hasValue: true,
18788
+ ...value === void 0 ? {} : { value }
18789
+ });
18790
+ }
18791
+ if (signal.aborted) reason = "cancelled";
18792
+ } catch (error) {
18793
+ reason = signal.aborted ? "cancelled" : "failed";
18794
+ if (!signal.aborted) failure = this.gateway.failure(error);
18795
+ } finally {
18796
+ this.streams.delete(streamId);
18797
+ await this.publish("harness.remote.stream.closed", {
18798
+ streamId,
18799
+ reason,
18800
+ ...failure === void 0 ? {} : { failure }
18801
+ }).catch(() => void 0);
18802
+ }
18803
+ }
18804
+ pruneTransfers() {
18805
+ const cutoff = Date.now() - TRANSFER_IDLE_MS;
18806
+ for (const [id, transfer] of this.incomingTransfers) if (transfer.touchedAt < cutoff) this.incomingTransfers.delete(id);
18807
+ for (const [id, transfer] of this.outgoingTransfers) if (transfer.touchedAt < cutoff) this.outgoingTransfers.delete(id);
18808
+ }
18809
+ };
18810
+ function decodeCanonicalBase642(value) {
18811
+ const bytes = Buffer.from(value, "base64");
18812
+ if (bytes.toString("base64") !== value) throw new RpcError("INVALID_MESSAGE", "The Harness Remote transfer chunk is invalid.");
18813
+ return bytes;
18814
+ }
18815
+ function concatChunks2(chunks, totalBytes) {
18816
+ const result = new Uint8Array(totalBytes);
18817
+ let offset = 0;
18818
+ for (const chunk of chunks) {
18819
+ result.set(chunk, offset);
18820
+ offset += chunk.byteLength;
18821
+ }
18822
+ return result;
18823
+ }
18824
+
18066
18825
  // src/service.ts
18067
18826
  var HostPluginRuntime = class {
18068
- constructor(config, identities, apiProxy, logger, typertGateway, fileViewerHost) {
18827
+ constructor(config, identities, apiProxy, logger, localGateway, fileViewerHost) {
18069
18828
  this.config = config;
18070
18829
  this.identities = identities;
18071
18830
  this.apiProxy = apiProxy;
18072
18831
  this.logger = logger;
18832
+ this.localGateway = localGateway;
18073
18833
  this.fileViewerHost = fileViewerHost;
18074
18834
  this.connections = new ConnectionController(this.identities, (_context, send) => {
18075
- const harnessApi = new HarnessApiBridge(
18835
+ const harnessApi = this.apiProxy === void 0 ? void 0 : new HarnessApiBridge(
18076
18836
  this.apiProxy,
18077
18837
  (event, data) => send(createEvent(event, data)),
18078
18838
  void 0,
18079
18839
  this.logger,
18080
- typertGateway?.(),
18840
+ this.localGateway,
18081
18841
  this.harnessVersion
18082
18842
  );
18843
+ const harnessRemote = this.localGateway?.supportsCarrier === true ? new HarnessRemoteBridge(
18844
+ this.localGateway,
18845
+ (event, data) => send(createEvent(event, data)),
18846
+ this.logger
18847
+ ) : void 0;
18083
18848
  const fileViewer = new RemoteFileViewerBridge(
18084
18849
  () => this.fileViewerHost?.(),
18085
18850
  this.logger
18086
18851
  );
18087
- return new RpcRouter(harnessApi, void 0, this.logger, fileViewer);
18852
+ return new RpcRouter(harnessApi, void 0, this.logger, fileViewer, harnessRemote, () => this.hostCapabilities());
18088
18853
  }, this.logger);
18089
18854
  if (config.serverUrl !== void 0) {
18090
18855
  this.serverApi = new HostServerApi(config.serverUrl, new ServerCredentialStore(identities.directory));
@@ -18225,7 +18990,7 @@ var HostPluginRuntime = class {
18225
18990
  this.logger,
18226
18991
  void 0,
18227
18992
  this.config.forceRelay ? void 0 : () => loadNodeRtcFactory({ routeTargets: this.config.serverUrl === void 0 ? [] : [this.config.serverUrl] }),
18228
- () => this.fileViewerHost?.() === void 0 ? ["harness.api.v1"] : ["harness.api.v1", "fileviewer.read.v1"],
18993
+ () => this.hostCapabilities(),
18229
18994
  this.harnessVersion
18230
18995
  );
18231
18996
  }
@@ -18233,7 +18998,8 @@ var HostPluginRuntime = class {
18233
18998
  let reportedVersion;
18234
18999
  let errorCode2;
18235
19000
  try {
18236
- const response = await this.apiProxy.host.describe({ rpcId: RpcId(randomUUID()), payload: {} });
19001
+ const response = await this.apiProxy?.host.describe({ rpcId: randomUUID(), payload: {} });
19002
+ if (response === void 0) throw new Error("ApiProxy is unavailable");
18237
19003
  if (!response.result.ok) {
18238
19004
  errorCode2 = response.result.error.code;
18239
19005
  } else {
@@ -18247,6 +19013,15 @@ var HostPluginRuntime = class {
18247
19013
  this.logger.warn("Harness version is unavailable", errorCode2 === void 0 ? void 0 : { code: errorCode2 });
18248
19014
  return void 0;
18249
19015
  }
19016
+ hostCapabilities() {
19017
+ const capabilities = [];
19018
+ if (this.apiProxy !== void 0) capabilities.push("harness.api.v1", "harness.api.transfer.v1");
19019
+ if (this.localGateway?.supportsCarrier === true) {
19020
+ capabilities.push("harness.remote.v1", "harness.remote.transfer.v1");
19021
+ }
19022
+ if (this.fileViewerHost?.() !== void 0) capabilities.push("fileviewer.read.v1");
19023
+ return capabilities;
19024
+ }
18250
19025
  };
18251
19026
  function shortId5(value) {
18252
19027
  return value.length <= 12 ? value : `${value.slice(0, 8)}\u2026${value.slice(-4)}`;
@@ -18254,6 +19029,7 @@ function shortId5(value) {
18254
19029
 
18255
19030
  // src/remote-file-content-provider.ts
18256
19031
  var REMOTE_FILE_SAVE_AS_MAX_BYTES = 100 * 1024 * 1024;
19032
+ var REMOTE_FILE_FAST_SAVE_AS_MAX_BYTES = 1024 * 1024 * 1024;
18257
19033
  function createRemoteFileContentProvider(call, options = {}) {
18258
19034
  return {
18259
19035
  id: "dsh-remote-files",
@@ -18261,7 +19037,7 @@ function createRemoteFileContentProvider(call, options = {}) {
18261
19037
  supports: () => true,
18262
19038
  saveAsAllowed: () => ({
18263
19039
  allowed: currentSaveAsAllowed(options.saveAsAllowed),
18264
- maxBytes: options.saveAsMaxBytes ?? REMOTE_FILE_SAVE_AS_MAX_BYTES
19040
+ maxBytes: currentSaveAsMaxBytes(options.saveAsMaxBytes)
18265
19041
  }),
18266
19042
  async stat(locator, signal) {
18267
19043
  const value = await call("fileviewer.stat", { path: locator }, signal);
@@ -18314,6 +19090,9 @@ function createRemoteFileContentProvider(call, options = {}) {
18314
19090
  function currentSaveAsAllowed(value) {
18315
19091
  return typeof value === "function" ? value() : value === true;
18316
19092
  }
19093
+ function currentSaveAsMaxBytes(value) {
19094
+ return typeof value === "function" ? value() : value ?? REMOTE_FILE_SAVE_AS_MAX_BYTES;
19095
+ }
18317
19096
  function decodeBase64(value) {
18318
19097
  const binary = atob(value);
18319
19098
  const bytes = new Uint8Array(binary.length);
@@ -18325,7 +19104,13 @@ function decodeBase64(value) {
18325
19104
  var name = "ds-harness-remote";
18326
19105
  var legacyLoaderModuleNames = /* @__PURE__ */ new Set(["dsh-remote", "@dsh-remote/plugin"]);
18327
19106
  function apply(ctx, input = {}) {
18328
- ctx.inject(["settings", "apiProxy", "connection", "typertGateway"], (runtimeContext) => activate(runtimeContext, input));
19107
+ ctx.inject(["settings", "connection", "typertGateway"], (runtimeContext) => {
19108
+ const gateway = runtimeContext.get("typertGateway");
19109
+ if (runtimeContext.get("apiProxy") !== void 0 || new TypertGatewaySwitch(gateway).supportsCarrier()) {
19110
+ return activate(runtimeContext, input);
19111
+ }
19112
+ runtimeContext.inject(["apiProxy"], (legacyContext) => activate(legacyContext, input));
19113
+ });
18329
19114
  }
18330
19115
  async function activate(ctx, input) {
18331
19116
  const settings = ctx.get("settings");
@@ -18370,12 +19155,12 @@ async function activate(ctx, input) {
18370
19155
  hostIdentities,
18371
19156
  apiProxy,
18372
19157
  logger,
18373
- () => localTypertGateway,
19158
+ localTypertGateway,
18374
19159
  () => ctx.get("fileViewerHost")
18375
19160
  );
18376
19161
  let clientRuntime;
18377
19162
  const hostControl = runtime;
18378
- if (config.serverUrl !== void 0 && apiProxy !== void 0 && connection !== void 0) {
19163
+ if (config.serverUrl !== void 0 && connection !== void 0) {
18379
19164
  const clientIdentities = new IdentityStore({
18380
19165
  directory: serverStorageDirectory(defaultIdentityDirectory, config.serverUrl, "client")
18381
19166
  });
@@ -18402,6 +19187,7 @@ async function activate(ctx, input) {
18402
19187
  logger.warn("client remote mode is unavailable", {
18403
19188
  serverConfigured: config.serverUrl !== void 0,
18404
19189
  apiProxyAvailable: apiProxy !== void 0,
19190
+ remoteGatewayAvailable: localTypertGateway.supportsCarrier,
18405
19191
  connectionAvailable: connection !== void 0
18406
19192
  });
18407
19193
  }
@@ -18451,8 +19237,10 @@ export {
18451
19237
  ConnectionController,
18452
19238
  ConnectionRejectedError,
18453
19239
  HARNESS_API_ALLOWLIST,
19240
+ HARNESS_REMOTE_ALLOWLIST,
18454
19241
  HOST_CAPABILITIES,
18455
19242
  HarnessApiBridge,
19243
+ HarnessRemoteBridge,
18456
19244
  HostPluginRuntime,
18457
19245
  HostServerApi,
18458
19246
  HostServerConnection,
@@ -18461,6 +19249,7 @@ export {
18461
19249
  PluginControlRuntime,
18462
19250
  RemoteFileViewerBridge,
18463
19251
  RemoteHarnessApiProxy,
19252
+ RemoteTypertGateway,
18464
19253
  RpcError,
18465
19254
  RpcRouter,
18466
19255
  ServerApiError,