ds-harness-remote 0.4.14 → 0.4.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/client-runtime.d.ts +5 -0
- package/dist/client-runtime.d.ts.map +1 -1
- package/dist/client-runtime.js +24 -0
- package/dist/client-runtime.js.map +1 -1
- package/dist/client-secure-transport.d.ts +1 -0
- package/dist/client-secure-transport.d.ts.map +1 -1
- package/dist/client-secure-transport.js +12 -5
- package/dist/client-secure-transport.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.github.js +259 -76
- package/dist/client.js +259 -76
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +12 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +6 -0
- package/dist/config.js.map +1 -1
- package/dist/control-route.d.ts +10 -1
- package/dist/control-route.d.ts.map +1 -1
- package/dist/control-route.js +17 -4
- package/dist/control-route.js.map +1 -1
- package/dist/control-runtime.d.ts +8 -1
- package/dist/control-runtime.d.ts.map +1 -1
- package/dist/control-runtime.js +40 -1
- package/dist/control-runtime.js.map +1 -1
- package/dist/control-stream.d.ts +67 -0
- package/dist/control-stream.d.ts.map +1 -0
- package/dist/control-stream.js +179 -0
- package/dist/control-stream.js.map +1 -0
- package/dist/harness-api-bridge.js +3 -0
- package/dist/harness-api-bridge.js.map +1 -1
- package/dist/harness-remote-bridge.d.ts +4 -2
- package/dist/harness-remote-bridge.d.ts.map +1 -1
- package/dist/harness-remote-bridge.js +42 -4
- package/dist/harness-remote-bridge.js.map +1 -1
- package/dist/index.js +1559 -580
- package/dist/index.js.map +4 -4
- package/dist/loopback-host.d.ts +20 -0
- package/dist/loopback-host.d.ts.map +1 -0
- package/dist/loopback-host.js +220 -0
- package/dist/loopback-host.js.map +1 -0
- package/dist/loopback-preview.d.ts +19 -0
- package/dist/loopback-preview.d.ts.map +1 -0
- package/dist/loopback-preview.js +232 -0
- package/dist/loopback-preview.js.map +1 -0
- package/dist/remote-typert-gateway.js +16 -4
- package/dist/remote-typert-gateway.js.map +1 -1
- package/dist/rpc-router.d.ts +3 -1
- package/dist/rpc-router.d.ts.map +1 -1
- package/dist/rpc-router.js +10 -1
- package/dist/rpc-router.js.map +1 -1
- package/dist/server-api.d.ts +6 -3
- package/dist/server-api.d.ts.map +1 -1
- package/dist/server-api.js +50 -26
- package/dist/server-api.js.map +1 -1
- package/dist/server-connection.d.ts +1 -0
- package/dist/server-connection.d.ts.map +1 -1
- package/dist/server-connection.js +54 -7
- package/dist/server-connection.js.map +1 -1
- package/dist/server-credentials.d.ts +9 -0
- package/dist/server-credentials.d.ts.map +1 -1
- package/dist/server-credentials.js +34 -0
- package/dist/server-credentials.js.map +1 -1
- package/dist/service.d.ts +5 -0
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +19 -2
- package/dist/service.js.map +1 -1
- package/dist/status-stream.d.ts +48 -0
- package/dist/status-stream.d.ts.map +1 -0
- package/dist/status-stream.js +133 -0
- package/dist/status-stream.js.map +1 -0
- package/dist/terminal-policy.d.ts +21 -0
- package/dist/terminal-policy.d.ts.map +1 -0
- package/dist/terminal-policy.js +62 -0
- package/dist/terminal-policy.js.map +1 -0
- package/dist/tui-command.js +6 -0
- package/dist/tui-command.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dsh-plugin.json +1 -1
- package/package.json +36 -16
- package/public.d.ts +2 -0
package/dist/index.js
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
|
-
var __export = (
|
|
2
|
+
var __export = (target2, all) => {
|
|
3
3
|
for (var name2 in all)
|
|
4
|
-
__defProp(
|
|
4
|
+
__defProp(target2, name2, { get: all[name2], enumerable: true });
|
|
5
5
|
};
|
|
6
6
|
|
|
7
|
+
// src/loopback-preview.ts
|
|
8
|
+
import { randomBytes, randomUUID } from "node:crypto";
|
|
9
|
+
import { createServer } from "node:http";
|
|
10
|
+
import { WebSocketServer } from "ws";
|
|
11
|
+
|
|
7
12
|
// ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
|
|
8
13
|
var external_exports = {};
|
|
9
14
|
__export(external_exports, {
|
|
@@ -208,15 +213,15 @@ var ZodParsedType = util.arrayToEnum([
|
|
|
208
213
|
"map",
|
|
209
214
|
"set"
|
|
210
215
|
]);
|
|
211
|
-
var getParsedType = (
|
|
212
|
-
const t = typeof
|
|
216
|
+
var getParsedType = (data2) => {
|
|
217
|
+
const t = typeof data2;
|
|
213
218
|
switch (t) {
|
|
214
219
|
case "undefined":
|
|
215
220
|
return ZodParsedType.undefined;
|
|
216
221
|
case "string":
|
|
217
222
|
return ZodParsedType.string;
|
|
218
223
|
case "number":
|
|
219
|
-
return Number.isNaN(
|
|
224
|
+
return Number.isNaN(data2) ? ZodParsedType.nan : ZodParsedType.number;
|
|
220
225
|
case "boolean":
|
|
221
226
|
return ZodParsedType.boolean;
|
|
222
227
|
case "function":
|
|
@@ -226,22 +231,22 @@ var getParsedType = (data) => {
|
|
|
226
231
|
case "symbol":
|
|
227
232
|
return ZodParsedType.symbol;
|
|
228
233
|
case "object":
|
|
229
|
-
if (Array.isArray(
|
|
234
|
+
if (Array.isArray(data2)) {
|
|
230
235
|
return ZodParsedType.array;
|
|
231
236
|
}
|
|
232
|
-
if (
|
|
237
|
+
if (data2 === null) {
|
|
233
238
|
return ZodParsedType.null;
|
|
234
239
|
}
|
|
235
|
-
if (
|
|
240
|
+
if (data2.then && typeof data2.then === "function" && data2.catch && typeof data2.catch === "function") {
|
|
236
241
|
return ZodParsedType.promise;
|
|
237
242
|
}
|
|
238
|
-
if (typeof Map !== "undefined" &&
|
|
243
|
+
if (typeof Map !== "undefined" && data2 instanceof Map) {
|
|
239
244
|
return ZodParsedType.map;
|
|
240
245
|
}
|
|
241
|
-
if (typeof Set !== "undefined" &&
|
|
246
|
+
if (typeof Set !== "undefined" && data2 instanceof Set) {
|
|
242
247
|
return ZodParsedType.set;
|
|
243
248
|
}
|
|
244
|
-
if (typeof Date !== "undefined" &&
|
|
249
|
+
if (typeof Date !== "undefined" && data2 instanceof Date) {
|
|
245
250
|
return ZodParsedType.date;
|
|
246
251
|
}
|
|
247
252
|
return ZodParsedType.object;
|
|
@@ -482,7 +487,7 @@ function getErrorMap() {
|
|
|
482
487
|
|
|
483
488
|
// ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
|
|
484
489
|
var makeIssue = (params) => {
|
|
485
|
-
const { data, path, errorMaps, issueData } = params;
|
|
490
|
+
const { data: data2, path, errorMaps, issueData } = params;
|
|
486
491
|
const fullPath = [...path, ...issueData.path || []];
|
|
487
492
|
const fullIssue = {
|
|
488
493
|
...issueData,
|
|
@@ -498,7 +503,7 @@ var makeIssue = (params) => {
|
|
|
498
503
|
let errorMessage = "";
|
|
499
504
|
const maps = errorMaps.filter((m) => !!m).slice().reverse();
|
|
500
505
|
for (const map of maps) {
|
|
501
|
-
errorMessage = map(fullIssue, { data, defaultError: errorMessage }).message;
|
|
506
|
+
errorMessage = map(fullIssue, { data: data2, defaultError: errorMessage }).message;
|
|
502
507
|
}
|
|
503
508
|
return {
|
|
504
509
|
...issueData,
|
|
@@ -700,13 +705,13 @@ var ZodType = class {
|
|
|
700
705
|
const result = this._parse(input2);
|
|
701
706
|
return Promise.resolve(result);
|
|
702
707
|
}
|
|
703
|
-
parse(
|
|
704
|
-
const result = this.safeParse(
|
|
708
|
+
parse(data2, params) {
|
|
709
|
+
const result = this.safeParse(data2, params);
|
|
705
710
|
if (result.success)
|
|
706
711
|
return result.data;
|
|
707
712
|
throw result.error;
|
|
708
713
|
}
|
|
709
|
-
safeParse(
|
|
714
|
+
safeParse(data2, params) {
|
|
710
715
|
const ctx = {
|
|
711
716
|
common: {
|
|
712
717
|
issues: [],
|
|
@@ -716,13 +721,13 @@ var ZodType = class {
|
|
|
716
721
|
path: params?.path || [],
|
|
717
722
|
schemaErrorMap: this._def.errorMap,
|
|
718
723
|
parent: null,
|
|
719
|
-
data,
|
|
720
|
-
parsedType: getParsedType(
|
|
724
|
+
data: data2,
|
|
725
|
+
parsedType: getParsedType(data2)
|
|
721
726
|
};
|
|
722
|
-
const result = this._parseSync({ data, path: ctx.path, parent: ctx });
|
|
727
|
+
const result = this._parseSync({ data: data2, path: ctx.path, parent: ctx });
|
|
723
728
|
return handleResult(ctx, result);
|
|
724
729
|
}
|
|
725
|
-
"~validate"(
|
|
730
|
+
"~validate"(data2) {
|
|
726
731
|
const ctx = {
|
|
727
732
|
common: {
|
|
728
733
|
issues: [],
|
|
@@ -731,12 +736,12 @@ var ZodType = class {
|
|
|
731
736
|
path: [],
|
|
732
737
|
schemaErrorMap: this._def.errorMap,
|
|
733
738
|
parent: null,
|
|
734
|
-
data,
|
|
735
|
-
parsedType: getParsedType(
|
|
739
|
+
data: data2,
|
|
740
|
+
parsedType: getParsedType(data2)
|
|
736
741
|
};
|
|
737
742
|
if (!this["~standard"].async) {
|
|
738
743
|
try {
|
|
739
|
-
const result = this._parseSync({ data, path: [], parent: ctx });
|
|
744
|
+
const result = this._parseSync({ data: data2, path: [], parent: ctx });
|
|
740
745
|
return isValid(result) ? {
|
|
741
746
|
value: result.value
|
|
742
747
|
} : {
|
|
@@ -752,19 +757,19 @@ var ZodType = class {
|
|
|
752
757
|
};
|
|
753
758
|
}
|
|
754
759
|
}
|
|
755
|
-
return this._parseAsync({ data, path: [], parent: ctx }).then((result) => isValid(result) ? {
|
|
760
|
+
return this._parseAsync({ data: data2, path: [], parent: ctx }).then((result) => isValid(result) ? {
|
|
756
761
|
value: result.value
|
|
757
762
|
} : {
|
|
758
763
|
issues: ctx.common.issues
|
|
759
764
|
});
|
|
760
765
|
}
|
|
761
|
-
async parseAsync(
|
|
762
|
-
const result = await this.safeParseAsync(
|
|
766
|
+
async parseAsync(data2, params) {
|
|
767
|
+
const result = await this.safeParseAsync(data2, params);
|
|
763
768
|
if (result.success)
|
|
764
769
|
return result.data;
|
|
765
770
|
throw result.error;
|
|
766
771
|
}
|
|
767
|
-
async safeParseAsync(
|
|
772
|
+
async safeParseAsync(data2, params) {
|
|
768
773
|
const ctx = {
|
|
769
774
|
common: {
|
|
770
775
|
issues: [],
|
|
@@ -774,10 +779,10 @@ var ZodType = class {
|
|
|
774
779
|
path: params?.path || [],
|
|
775
780
|
schemaErrorMap: this._def.errorMap,
|
|
776
781
|
parent: null,
|
|
777
|
-
data,
|
|
778
|
-
parsedType: getParsedType(
|
|
782
|
+
data: data2,
|
|
783
|
+
parsedType: getParsedType(data2)
|
|
779
784
|
};
|
|
780
|
-
const maybeAsyncResult = this._parse({ data, path: ctx.path, parent: ctx });
|
|
785
|
+
const maybeAsyncResult = this._parse({ data: data2, path: ctx.path, parent: ctx });
|
|
781
786
|
const result = await (isAsync(maybeAsyncResult) ? maybeAsyncResult : Promise.resolve(maybeAsyncResult));
|
|
782
787
|
return handleResult(ctx, result);
|
|
783
788
|
}
|
|
@@ -798,8 +803,8 @@ var ZodType = class {
|
|
|
798
803
|
...getIssueProperties(val)
|
|
799
804
|
});
|
|
800
805
|
if (typeof Promise !== "undefined" && result instanceof Promise) {
|
|
801
|
-
return result.then((
|
|
802
|
-
if (!
|
|
806
|
+
return result.then((data2) => {
|
|
807
|
+
if (!data2) {
|
|
803
808
|
setError();
|
|
804
809
|
return false;
|
|
805
810
|
} else {
|
|
@@ -865,7 +870,7 @@ var ZodType = class {
|
|
|
865
870
|
this["~standard"] = {
|
|
866
871
|
version: 1,
|
|
867
872
|
vendor: "zod",
|
|
868
|
-
validate: (
|
|
873
|
+
validate: (data2) => this["~validate"](data2)
|
|
869
874
|
};
|
|
870
875
|
}
|
|
871
876
|
optional() {
|
|
@@ -929,8 +934,8 @@ var ZodType = class {
|
|
|
929
934
|
description
|
|
930
935
|
});
|
|
931
936
|
}
|
|
932
|
-
pipe(
|
|
933
|
-
return ZodPipeline.create(this,
|
|
937
|
+
pipe(target2) {
|
|
938
|
+
return ZodPipeline.create(this, target2);
|
|
934
939
|
}
|
|
935
940
|
readonly() {
|
|
936
941
|
return ZodReadonly.create(this);
|
|
@@ -1325,7 +1330,7 @@ var ZodString = class _ZodString extends ZodType {
|
|
|
1325
1330
|
return { status: status2.value, value: input2.data };
|
|
1326
1331
|
}
|
|
1327
1332
|
_regex(regex, validation, message) {
|
|
1328
|
-
return this.refinement((
|
|
1333
|
+
return this.refinement((data2) => regex.test(data2), {
|
|
1329
1334
|
validation,
|
|
1330
1335
|
code: ZodIssueCode.invalid_string,
|
|
1331
1336
|
...errorUtil.errToObj(message)
|
|
@@ -3541,8 +3546,8 @@ var ZodPromise = class extends ZodType {
|
|
|
3541
3546
|
return INVALID;
|
|
3542
3547
|
}
|
|
3543
3548
|
const promisified = ctx.parsedType === ZodParsedType.promise ? ctx.data : Promise.resolve(ctx.data);
|
|
3544
|
-
return OK(promisified.then((
|
|
3545
|
-
return this._def.type.parseAsync(
|
|
3549
|
+
return OK(promisified.then((data2) => {
|
|
3550
|
+
return this._def.type.parseAsync(data2, {
|
|
3546
3551
|
path: ctx.path,
|
|
3547
3552
|
errorMap: ctx.common.contextualErrorMap
|
|
3548
3553
|
});
|
|
@@ -3736,12 +3741,12 @@ ZodNullable.create = (type, params) => {
|
|
|
3736
3741
|
var ZodDefault = class extends ZodType {
|
|
3737
3742
|
_parse(input2) {
|
|
3738
3743
|
const { ctx } = this._processInputParams(input2);
|
|
3739
|
-
let
|
|
3744
|
+
let data2 = ctx.data;
|
|
3740
3745
|
if (ctx.parsedType === ZodParsedType.undefined) {
|
|
3741
|
-
|
|
3746
|
+
data2 = this._def.defaultValue();
|
|
3742
3747
|
}
|
|
3743
3748
|
return this._def.innerType._parse({
|
|
3744
|
-
data,
|
|
3749
|
+
data: data2,
|
|
3745
3750
|
path: ctx.path,
|
|
3746
3751
|
parent: ctx
|
|
3747
3752
|
});
|
|
@@ -3836,9 +3841,9 @@ var BRAND = Symbol("zod_brand");
|
|
|
3836
3841
|
var ZodBranded = class extends ZodType {
|
|
3837
3842
|
_parse(input2) {
|
|
3838
3843
|
const { ctx } = this._processInputParams(input2);
|
|
3839
|
-
const
|
|
3844
|
+
const data2 = ctx.data;
|
|
3840
3845
|
return this._def.type._parse({
|
|
3841
|
-
data,
|
|
3846
|
+
data: data2,
|
|
3842
3847
|
path: ctx.path,
|
|
3843
3848
|
parent: ctx
|
|
3844
3849
|
});
|
|
@@ -3905,13 +3910,13 @@ var ZodPipeline = class _ZodPipeline extends ZodType {
|
|
|
3905
3910
|
var ZodReadonly = class extends ZodType {
|
|
3906
3911
|
_parse(input2) {
|
|
3907
3912
|
const result = this._def.innerType._parse(input2);
|
|
3908
|
-
const freeze = (
|
|
3909
|
-
if (isValid(
|
|
3910
|
-
|
|
3913
|
+
const freeze = (data2) => {
|
|
3914
|
+
if (isValid(data2)) {
|
|
3915
|
+
data2.value = Object.freeze(data2.value);
|
|
3911
3916
|
}
|
|
3912
|
-
return
|
|
3917
|
+
return data2;
|
|
3913
3918
|
};
|
|
3914
|
-
return isAsync(result) ? result.then((
|
|
3919
|
+
return isAsync(result) ? result.then((data2) => freeze(data2)) : freeze(result);
|
|
3915
3920
|
}
|
|
3916
3921
|
unwrap() {
|
|
3917
3922
|
return this._def.innerType;
|
|
@@ -3924,26 +3929,26 @@ ZodReadonly.create = (type, params) => {
|
|
|
3924
3929
|
...processCreateParams(params)
|
|
3925
3930
|
});
|
|
3926
3931
|
};
|
|
3927
|
-
function cleanParams(params,
|
|
3928
|
-
const p = typeof params === "function" ? params(
|
|
3932
|
+
function cleanParams(params, data2) {
|
|
3933
|
+
const p = typeof params === "function" ? params(data2) : typeof params === "string" ? { message: params } : params;
|
|
3929
3934
|
const p2 = typeof p === "string" ? { message: p } : p;
|
|
3930
3935
|
return p2;
|
|
3931
3936
|
}
|
|
3932
3937
|
function custom(check, _params = {}, fatal) {
|
|
3933
3938
|
if (check)
|
|
3934
|
-
return ZodAny.create().superRefine((
|
|
3935
|
-
const r = check(
|
|
3939
|
+
return ZodAny.create().superRefine((data2, ctx) => {
|
|
3940
|
+
const r = check(data2);
|
|
3936
3941
|
if (r instanceof Promise) {
|
|
3937
3942
|
return r.then((r2) => {
|
|
3938
3943
|
if (!r2) {
|
|
3939
|
-
const params = cleanParams(_params,
|
|
3944
|
+
const params = cleanParams(_params, data2);
|
|
3940
3945
|
const _fatal = params.fatal ?? fatal ?? true;
|
|
3941
3946
|
ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
|
|
3942
3947
|
}
|
|
3943
3948
|
});
|
|
3944
3949
|
}
|
|
3945
3950
|
if (!r) {
|
|
3946
|
-
const params = cleanParams(_params,
|
|
3951
|
+
const params = cleanParams(_params, data2);
|
|
3947
3952
|
const _fatal = params.fatal ?? fatal ?? true;
|
|
3948
3953
|
ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
|
|
3949
3954
|
}
|
|
@@ -3995,7 +4000,7 @@ var ZodFirstPartyTypeKind;
|
|
|
3995
4000
|
})(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {}));
|
|
3996
4001
|
var instanceOfType = (cls, params = {
|
|
3997
4002
|
message: `Input not instance of ${cls.name}`
|
|
3998
|
-
}) => custom((
|
|
4003
|
+
}) => custom((data2) => data2 instanceof cls, params);
|
|
3999
4004
|
var stringType = ZodString.create;
|
|
4000
4005
|
var numberType = ZodNumber.create;
|
|
4001
4006
|
var nanType = ZodNaN.create;
|
|
@@ -4045,6 +4050,28 @@ var coerce = {
|
|
|
4045
4050
|
};
|
|
4046
4051
|
var NEVER = INVALID;
|
|
4047
4052
|
|
|
4053
|
+
// ../protocol/dist/loopback.js
|
|
4054
|
+
var LOOPBACK_CHUNK_BYTES = 64 * 1024;
|
|
4055
|
+
var LOOPBACK_MAX_BODY_BYTES = 1024 * 1024;
|
|
4056
|
+
var LOOPBACK_MAX_WS_BYTES = 256 * 1024;
|
|
4057
|
+
var LOOPBACK_MAX_CONNECTIONS = 16;
|
|
4058
|
+
var id = external_exports.string().uuid();
|
|
4059
|
+
var data = external_exports.string().max(Math.ceil(LOOPBACK_MAX_BODY_BYTES / 3) * 4).regex(/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/);
|
|
4060
|
+
var target = {
|
|
4061
|
+
port: external_exports.number().int().min(1024).max(65535),
|
|
4062
|
+
path: external_exports.string().min(1).max(8192).regex(/^\/(?!\/)[^\x00-\x20\x7f\\]*$/),
|
|
4063
|
+
headers: external_exports.array(external_exports.tuple([external_exports.string().regex(/^[!#$%&'*+.^_`|~0-9A-Za-z-]{1,128}$/), external_exports.string().max(8192).regex(/^[^\x00-\x08\x0a-\x1f\x7f]*$/)])).max(64)
|
|
4064
|
+
};
|
|
4065
|
+
var loopbackRequestSchema = external_exports.discriminatedUnion("op", [
|
|
4066
|
+
external_exports.object({ op: external_exports.literal("describe") }).strict(),
|
|
4067
|
+
external_exports.object({ op: external_exports.literal("http.open"), id, ...target, method: external_exports.enum(["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"]), body: data.optional() }).strict(),
|
|
4068
|
+
external_exports.object({ op: external_exports.literal("http.read"), id }).strict(),
|
|
4069
|
+
external_exports.object({ op: external_exports.literal("close"), id }).strict(),
|
|
4070
|
+
external_exports.object({ op: external_exports.literal("ws.open"), id, ...target, protocols: external_exports.array(external_exports.string().regex(/^[!#$%&'*+.^_`|~0-9A-Za-z-]{1,128}$/)).max(16) }).strict(),
|
|
4071
|
+
external_exports.object({ op: external_exports.literal("ws.read"), id }).strict(),
|
|
4072
|
+
external_exports.object({ op: external_exports.literal("ws.send"), id, data, binary: external_exports.boolean() }).strict()
|
|
4073
|
+
]);
|
|
4074
|
+
|
|
4048
4075
|
// ../protocol/dist/index.js
|
|
4049
4076
|
var PROTOCOL_VERSION = 1;
|
|
4050
4077
|
var MAX_CONTROL_FRAME_BYTES = 64 * 1024;
|
|
@@ -4114,6 +4141,7 @@ var rpcMethods = [
|
|
|
4114
4141
|
"harness.remote.transfer.close",
|
|
4115
4142
|
"harness.remote.stream.open",
|
|
4116
4143
|
"harness.remote.stream.close",
|
|
4144
|
+
"loopback.call",
|
|
4117
4145
|
"fileviewer.call",
|
|
4118
4146
|
"codex.app.call",
|
|
4119
4147
|
"codex.app.respond",
|
|
@@ -4404,26 +4432,26 @@ var rpcErrorPayloadSchema = external_exports.object({
|
|
|
4404
4432
|
retryable: external_exports.boolean().optional(),
|
|
4405
4433
|
details: external_exports.unknown().optional()
|
|
4406
4434
|
});
|
|
4407
|
-
function createMessage(type, payload,
|
|
4435
|
+
function createMessage(type, payload, id4 = cryptoRandomId()) {
|
|
4408
4436
|
return {
|
|
4409
4437
|
v: PROTOCOL_VERSION,
|
|
4410
|
-
id:
|
|
4438
|
+
id: id4,
|
|
4411
4439
|
type,
|
|
4412
4440
|
timestamp: Date.now(),
|
|
4413
4441
|
payload
|
|
4414
4442
|
};
|
|
4415
4443
|
}
|
|
4416
|
-
function createControlFrame(type, payload,
|
|
4444
|
+
function createControlFrame(type, payload, id4 = cryptoRandomId()) {
|
|
4417
4445
|
return {
|
|
4418
4446
|
v: PROTOCOL_VERSION,
|
|
4419
|
-
id:
|
|
4447
|
+
id: id4,
|
|
4420
4448
|
type,
|
|
4421
4449
|
timestamp: Date.now(),
|
|
4422
4450
|
payload
|
|
4423
4451
|
};
|
|
4424
4452
|
}
|
|
4425
|
-
function createRpcRequest(method, params,
|
|
4426
|
-
return createMessage("rpc.request", { method, params },
|
|
4453
|
+
function createRpcRequest(method, params, id4) {
|
|
4454
|
+
return createMessage("rpc.request", { method, params }, id4);
|
|
4427
4455
|
}
|
|
4428
4456
|
function createRpcResponse(requestId, result) {
|
|
4429
4457
|
return createMessage("rpc.response", { requestId, result });
|
|
@@ -4431,8 +4459,8 @@ function createRpcResponse(requestId, result) {
|
|
|
4431
4459
|
function createRpcError(requestId, code, message, details, retryable) {
|
|
4432
4460
|
return createMessage("rpc.error", { requestId, code, message, details, retryable });
|
|
4433
4461
|
}
|
|
4434
|
-
function createEvent(event,
|
|
4435
|
-
return createMessage("event", { event, data, ...options });
|
|
4462
|
+
function createEvent(event, data2, options = {}) {
|
|
4463
|
+
return createMessage("event", { event, data: data2, ...options });
|
|
4436
4464
|
}
|
|
4437
4465
|
function parseRemoteMessage(input2) {
|
|
4438
4466
|
return remoteMessageSchema.parse(input2);
|
|
@@ -4451,11 +4479,11 @@ function encodeControlFrame(frame, limits = {}) {
|
|
|
4451
4479
|
assertControlFrameSize(new TextEncoder().encode(encoded).byteLength, parsed.type, limits);
|
|
4452
4480
|
return encoded;
|
|
4453
4481
|
}
|
|
4454
|
-
function decodeControlFrame(
|
|
4455
|
-
const encoded = typeof
|
|
4482
|
+
function decodeControlFrame(data2, limits = {}) {
|
|
4483
|
+
const encoded = typeof data2 === "string" ? new TextEncoder().encode(data2) : data2;
|
|
4456
4484
|
if (encoded.byteLength > MAX_RELAY_FRAME_BYTES)
|
|
4457
4485
|
throw new Error("Control frame exceeds the Relay frame limit.");
|
|
4458
|
-
const text = typeof
|
|
4486
|
+
const text = typeof data2 === "string" ? data2 : new TextDecoder("utf-8", { fatal: true }).decode(data2);
|
|
4459
4487
|
const frame = parseControlFrame(JSON.parse(text));
|
|
4460
4488
|
assertControlFrameSize(encoded.byteLength, frame.type, limits);
|
|
4461
4489
|
return frame;
|
|
@@ -4463,8 +4491,8 @@ function decodeControlFrame(data, limits = {}) {
|
|
|
4463
4491
|
function encodeMessage(message) {
|
|
4464
4492
|
return new TextEncoder().encode(JSON.stringify(message));
|
|
4465
4493
|
}
|
|
4466
|
-
function decodeMessage(
|
|
4467
|
-
const text = typeof
|
|
4494
|
+
function decodeMessage(data2) {
|
|
4495
|
+
const text = typeof data2 === "string" ? data2 : new TextDecoder().decode(data2);
|
|
4468
4496
|
return parseRemoteMessage(JSON.parse(text));
|
|
4469
4497
|
}
|
|
4470
4498
|
var SecureMessageCodec = class {
|
|
@@ -4571,6 +4599,504 @@ function cryptoRandomId() {
|
|
|
4571
4599
|
return `msg_${Date.now().toString(36)}_${Math.random().toString(36).slice(2)}`;
|
|
4572
4600
|
}
|
|
4573
4601
|
|
|
4602
|
+
// src/loopback-host.ts
|
|
4603
|
+
import { request as httpRequest } from "node:http";
|
|
4604
|
+
import { WebSocket as WebSocket2 } from "ws";
|
|
4605
|
+
|
|
4606
|
+
// src/safe-error.ts
|
|
4607
|
+
var RpcError = class extends Error {
|
|
4608
|
+
constructor(code, message, details, retryable = false) {
|
|
4609
|
+
super(message);
|
|
4610
|
+
this.code = code;
|
|
4611
|
+
this.details = details;
|
|
4612
|
+
this.retryable = retryable;
|
|
4613
|
+
}
|
|
4614
|
+
};
|
|
4615
|
+
function safeErrorCode(error) {
|
|
4616
|
+
if (error instanceof RpcError) return error.code;
|
|
4617
|
+
if (error instanceof external_exports.ZodError) return "INVALID_MESSAGE";
|
|
4618
|
+
return "INTERNAL_ERROR";
|
|
4619
|
+
}
|
|
4620
|
+
|
|
4621
|
+
// src/loopback-host.ts
|
|
4622
|
+
var HOP_HEADERS = /* @__PURE__ */ new Set([
|
|
4623
|
+
"connection",
|
|
4624
|
+
"keep-alive",
|
|
4625
|
+
"proxy-authenticate",
|
|
4626
|
+
"proxy-authorization",
|
|
4627
|
+
"te",
|
|
4628
|
+
"trailer",
|
|
4629
|
+
"transfer-encoding",
|
|
4630
|
+
"upgrade",
|
|
4631
|
+
"host",
|
|
4632
|
+
"content-length",
|
|
4633
|
+
"forwarded",
|
|
4634
|
+
"x-forwarded-for",
|
|
4635
|
+
"x-forwarded-host",
|
|
4636
|
+
"x-forwarded-proto"
|
|
4637
|
+
]);
|
|
4638
|
+
function cleanHeaders(headers) {
|
|
4639
|
+
const removed = new Set(HOP_HEADERS);
|
|
4640
|
+
for (const [name2, value] of headers) if (name2.toLowerCase() === "connection") {
|
|
4641
|
+
for (const token of value.split(",")) removed.add(token.trim().toLowerCase());
|
|
4642
|
+
}
|
|
4643
|
+
return headers.filter(([name2]) => !removed.has(name2.toLowerCase()) && !name2.toLowerCase().startsWith("sec-websocket-"));
|
|
4644
|
+
}
|
|
4645
|
+
function headerPairs(headers) {
|
|
4646
|
+
return Object.entries(headers).flatMap(([name2, value]) => value === void 0 ? [] : (Array.isArray(value) ? value : [value]).map((item) => [name2, item]));
|
|
4647
|
+
}
|
|
4648
|
+
var LoopbackHost = class {
|
|
4649
|
+
handles = /* @__PURE__ */ new Map();
|
|
4650
|
+
ports;
|
|
4651
|
+
closed = false;
|
|
4652
|
+
timer;
|
|
4653
|
+
constructor(ports) {
|
|
4654
|
+
this.ports = [...ports];
|
|
4655
|
+
this.timer = setInterval(() => {
|
|
4656
|
+
for (const [id4, handle] of this.handles) if (Date.now() - handle.touched > 6e4) this.close(id4);
|
|
4657
|
+
}, 1e4);
|
|
4658
|
+
this.timer.unref();
|
|
4659
|
+
}
|
|
4660
|
+
setPorts(ports) {
|
|
4661
|
+
this.ports = [...ports];
|
|
4662
|
+
}
|
|
4663
|
+
hasPorts() {
|
|
4664
|
+
return this.ports.length > 0;
|
|
4665
|
+
}
|
|
4666
|
+
async call(input2) {
|
|
4667
|
+
if (this.closed) throw new RpcError("TRANSPORT_CLOSED", "Preview connection closed.");
|
|
4668
|
+
const value = loopbackRequestSchema.parse(input2);
|
|
4669
|
+
if (value.op === "describe") return { ports: [...this.ports] };
|
|
4670
|
+
if (value.op === "close") {
|
|
4671
|
+
this.close(value.id);
|
|
4672
|
+
return { closed: true };
|
|
4673
|
+
}
|
|
4674
|
+
if (value.op === "http.open" || value.op === "ws.open") {
|
|
4675
|
+
if (!this.ports.includes(value.port)) throw new RpcError(
|
|
4676
|
+
"LOOPBACK_PORT_DENIED",
|
|
4677
|
+
"This preview port is not allowed. Add it to loopback.ports in the Host Remote settings and save the access settings. / \u8BF7\u5728 Host Remote \u8BBE\u7F6E\u4E2D\u5141\u8BB8\u6B64\u9884\u89C8\u7AEF\u53E3\u5E76\u4FDD\u5B58\u8BBF\u95EE\u8BBE\u7F6E\u3002"
|
|
4678
|
+
);
|
|
4679
|
+
if (this.handles.has(value.id)) throw new RpcError("REQUEST_CONFLICT", "Preview handle is already in use.");
|
|
4680
|
+
if (this.handles.size >= LOOPBACK_MAX_CONNECTIONS) throw new RpcError("RATE_LIMITED", "Too many active preview requests.");
|
|
4681
|
+
try {
|
|
4682
|
+
return value.op === "http.open" ? await this.openHttp(value) : await this.openWs(value);
|
|
4683
|
+
} catch {
|
|
4684
|
+
this.close(value.id);
|
|
4685
|
+
throw new RpcError("LOOPBACK_UNAVAILABLE", "The allowed loopback service did not respond. Check that it is running on the Host.");
|
|
4686
|
+
}
|
|
4687
|
+
}
|
|
4688
|
+
const handle = this.handles.get(value.id);
|
|
4689
|
+
if (handle === void 0) throw new RpcError("LOOPBACK_CLOSED", "Preview request closed; reload the preview.");
|
|
4690
|
+
handle.touched = Date.now();
|
|
4691
|
+
if (value.op === "http.read" && handle.kind === "http") {
|
|
4692
|
+
if (handle.reading) throw new RpcError("REQUEST_CONFLICT", "Only one preview read may be pending.");
|
|
4693
|
+
handle.reading = true;
|
|
4694
|
+
try {
|
|
4695
|
+
const response = handle.response;
|
|
4696
|
+
let chunk = response.read(Math.min(response.readableLength || LOOPBACK_CHUNK_BYTES, LOOPBACK_CHUNK_BYTES));
|
|
4697
|
+
if (chunk === null && !response.readableEnded && !response.destroyed) {
|
|
4698
|
+
await new Promise((resolve3, reject) => {
|
|
4699
|
+
const timer = setTimeout(() => finish(new Error("timeout")), 2e4);
|
|
4700
|
+
const ready = () => finish();
|
|
4701
|
+
const fail4 = () => finish(new Error("closed"));
|
|
4702
|
+
const finish = (error) => {
|
|
4703
|
+
clearTimeout(timer);
|
|
4704
|
+
response.off("readable", ready);
|
|
4705
|
+
response.off("end", ready);
|
|
4706
|
+
response.off("close", ready);
|
|
4707
|
+
response.off("error", fail4);
|
|
4708
|
+
error === void 0 ? resolve3() : reject(error);
|
|
4709
|
+
};
|
|
4710
|
+
response.once("readable", ready);
|
|
4711
|
+
response.once("end", ready);
|
|
4712
|
+
response.once("close", ready);
|
|
4713
|
+
response.once("error", fail4);
|
|
4714
|
+
});
|
|
4715
|
+
chunk = response.read(Math.min(response.readableLength || LOOPBACK_CHUNK_BYTES, LOOPBACK_CHUNK_BYTES));
|
|
4716
|
+
if (chunk === null && response.readableLength > 0) chunk = response.read(Math.min(response.readableLength, LOOPBACK_CHUNK_BYTES));
|
|
4717
|
+
}
|
|
4718
|
+
const done = chunk === null && (response.readableEnded || response.destroyed);
|
|
4719
|
+
if (done && !response.complete) throw new Error("truncated");
|
|
4720
|
+
handle.total += chunk?.length ?? 0;
|
|
4721
|
+
if (handle.total > 64 * 1024 * 1024) throw new Error("size");
|
|
4722
|
+
if (done) this.close(value.id);
|
|
4723
|
+
return { data: chunk?.toString("base64") ?? "", done };
|
|
4724
|
+
} catch {
|
|
4725
|
+
this.close(value.id);
|
|
4726
|
+
throw new RpcError("LOOPBACK_READ_FAILED", "Preview response ended or exceeded its limit; reload to retry.");
|
|
4727
|
+
} finally {
|
|
4728
|
+
handle.reading = false;
|
|
4729
|
+
}
|
|
4730
|
+
}
|
|
4731
|
+
if (handle.kind === "ws") {
|
|
4732
|
+
if (value.op === "ws.send") {
|
|
4733
|
+
const data2 = Buffer.from(value.data, "base64");
|
|
4734
|
+
if (data2.length > LOOPBACK_MAX_WS_BYTES || handle.socket.bufferedAmount > 1024 * 1024) {
|
|
4735
|
+
this.close(value.id);
|
|
4736
|
+
throw new RpcError("RATE_LIMITED", "Preview WebSocket buffer exceeded its limit.");
|
|
4737
|
+
}
|
|
4738
|
+
await new Promise((resolve3, reject) => handle.socket.send(data2, { binary: value.binary }, (error) => error ? reject(new RpcError("LOOPBACK_CLOSED", "Preview socket closed.")) : resolve3()));
|
|
4739
|
+
return { sent: true };
|
|
4740
|
+
}
|
|
4741
|
+
if (value.op === "ws.read") {
|
|
4742
|
+
if (handle.reading) throw new RpcError("REQUEST_CONFLICT", "Only one preview read may be pending.");
|
|
4743
|
+
handle.reading = true;
|
|
4744
|
+
try {
|
|
4745
|
+
if (handle.messages.length === 0 && !handle.closed) await new Promise((resolve3) => {
|
|
4746
|
+
const timer = setTimeout(() => {
|
|
4747
|
+
handle.wake = void 0;
|
|
4748
|
+
resolve3();
|
|
4749
|
+
}, 2e4);
|
|
4750
|
+
handle.wake = () => {
|
|
4751
|
+
clearTimeout(timer);
|
|
4752
|
+
handle.wake = void 0;
|
|
4753
|
+
resolve3();
|
|
4754
|
+
};
|
|
4755
|
+
});
|
|
4756
|
+
let bytes = 0;
|
|
4757
|
+
const messages = [];
|
|
4758
|
+
while (handle.messages.length > 0 && bytes < LOOPBACK_MAX_WS_BYTES) {
|
|
4759
|
+
const message = handle.messages.shift();
|
|
4760
|
+
bytes += message.data.length;
|
|
4761
|
+
messages.push(message);
|
|
4762
|
+
}
|
|
4763
|
+
handle.bytes -= bytes;
|
|
4764
|
+
const closed = handle.closed && handle.messages.length === 0;
|
|
4765
|
+
if (closed) this.close(value.id);
|
|
4766
|
+
return { messages, closed };
|
|
4767
|
+
} finally {
|
|
4768
|
+
handle.reading = false;
|
|
4769
|
+
}
|
|
4770
|
+
}
|
|
4771
|
+
}
|
|
4772
|
+
throw new RpcError("INVALID_MESSAGE", "Preview handle kind does not match the operation.");
|
|
4773
|
+
}
|
|
4774
|
+
closeAll() {
|
|
4775
|
+
this.closed = true;
|
|
4776
|
+
clearInterval(this.timer);
|
|
4777
|
+
for (const id4 of this.handles.keys()) this.close(id4);
|
|
4778
|
+
}
|
|
4779
|
+
close(id4) {
|
|
4780
|
+
const handle = this.handles.get(id4);
|
|
4781
|
+
this.handles.delete(id4);
|
|
4782
|
+
if (handle?.kind === "http") {
|
|
4783
|
+
handle.response?.destroy();
|
|
4784
|
+
handle.request.destroy();
|
|
4785
|
+
}
|
|
4786
|
+
if (handle?.kind === "ws") {
|
|
4787
|
+
handle.closed = true;
|
|
4788
|
+
handle.wake?.();
|
|
4789
|
+
handle.socket.terminate();
|
|
4790
|
+
}
|
|
4791
|
+
}
|
|
4792
|
+
openHttp(value) {
|
|
4793
|
+
const body = value.body === void 0 ? void 0 : Buffer.from(value.body, "base64");
|
|
4794
|
+
if ((body?.length ?? 0) > LOOPBACK_MAX_BODY_BYTES) throw new Error("body limit");
|
|
4795
|
+
return new Promise((resolve3, reject) => {
|
|
4796
|
+
const headers = Object.fromEntries(cleanHeaders(value.headers));
|
|
4797
|
+
const request = httpRequest({
|
|
4798
|
+
hostname: "127.0.0.1",
|
|
4799
|
+
port: value.port,
|
|
4800
|
+
method: value.method,
|
|
4801
|
+
path: value.path,
|
|
4802
|
+
headers,
|
|
4803
|
+
agent: false,
|
|
4804
|
+
maxHeaderSize: 32 * 1024
|
|
4805
|
+
}, (response) => {
|
|
4806
|
+
const handle = this.handles.get(value.id);
|
|
4807
|
+
if (handle?.kind !== "http") {
|
|
4808
|
+
response.destroy();
|
|
4809
|
+
return;
|
|
4810
|
+
}
|
|
4811
|
+
handle.response = response;
|
|
4812
|
+
response.on("error", () => void 0);
|
|
4813
|
+
clearTimeout(timer);
|
|
4814
|
+
resolve3({ status: response.statusCode ?? 502, headers: cleanHeaders(headerPairs(response.headers)) });
|
|
4815
|
+
});
|
|
4816
|
+
const timer = setTimeout(() => {
|
|
4817
|
+
request.destroy();
|
|
4818
|
+
reject(new Error("timeout"));
|
|
4819
|
+
}, 2e4);
|
|
4820
|
+
request.on("error", () => {
|
|
4821
|
+
clearTimeout(timer);
|
|
4822
|
+
reject(new Error("upstream"));
|
|
4823
|
+
});
|
|
4824
|
+
this.handles.set(value.id, { kind: "http", request, reading: false, total: 0, touched: Date.now() });
|
|
4825
|
+
request.end(body);
|
|
4826
|
+
});
|
|
4827
|
+
}
|
|
4828
|
+
openWs(value) {
|
|
4829
|
+
return new Promise((resolve3, reject) => {
|
|
4830
|
+
const socket = new WebSocket2(`ws://127.0.0.1:${value.port}${value.path}`, value.protocols, {
|
|
4831
|
+
headers: Object.fromEntries(cleanHeaders(value.headers)),
|
|
4832
|
+
followRedirects: false,
|
|
4833
|
+
handshakeTimeout: 15e3,
|
|
4834
|
+
maxPayload: LOOPBACK_MAX_WS_BYTES,
|
|
4835
|
+
perMessageDeflate: false
|
|
4836
|
+
});
|
|
4837
|
+
const handle = { kind: "ws", socket, messages: [], bytes: 0, reading: false, closed: false, touched: Date.now() };
|
|
4838
|
+
this.handles.set(value.id, handle);
|
|
4839
|
+
socket.once("open", () => resolve3({ protocol: socket.protocol }));
|
|
4840
|
+
socket.on("message", (data2, binary) => {
|
|
4841
|
+
const message = { data: Buffer.from(data2).toString("base64"), binary };
|
|
4842
|
+
if (handle.bytes + message.data.length > 1024 * 1024 || handle.messages.length >= 256) {
|
|
4843
|
+
this.close(value.id);
|
|
4844
|
+
return;
|
|
4845
|
+
}
|
|
4846
|
+
handle.messages.push(message);
|
|
4847
|
+
handle.bytes += message.data.length;
|
|
4848
|
+
handle.wake?.();
|
|
4849
|
+
});
|
|
4850
|
+
socket.on("close", () => {
|
|
4851
|
+
handle.closed = true;
|
|
4852
|
+
handle.wake?.();
|
|
4853
|
+
reject(new Error("closed"));
|
|
4854
|
+
});
|
|
4855
|
+
socket.on("error", () => {
|
|
4856
|
+
handle.closed = true;
|
|
4857
|
+
handle.wake?.();
|
|
4858
|
+
reject(new Error("upstream"));
|
|
4859
|
+
});
|
|
4860
|
+
});
|
|
4861
|
+
}
|
|
4862
|
+
};
|
|
4863
|
+
|
|
4864
|
+
// src/loopback-preview.ts
|
|
4865
|
+
var LoopbackPreview = class {
|
|
4866
|
+
constructor(client) {
|
|
4867
|
+
this.client = client;
|
|
4868
|
+
}
|
|
4869
|
+
servers = /* @__PURE__ */ new Map();
|
|
4870
|
+
sockets = /* @__PURE__ */ new Set();
|
|
4871
|
+
lifetime = new AbortController();
|
|
4872
|
+
async open(port) {
|
|
4873
|
+
if (!Number.isInteger(port) || port < 1024 || port > 65535) throw new RpcError("INVALID_MESSAGE", "Enter a port between 1024 and 65535.");
|
|
4874
|
+
this.lifetime.signal.throwIfAborted();
|
|
4875
|
+
const description = await this.client.rpc("loopback.call", { op: "describe" }, this.lifetime.signal);
|
|
4876
|
+
if (!description.ports.includes(port)) throw new RpcError("LOOPBACK_PORT_DENIED", "Allow this port in the Host Remote settings (loopback.ports), then save the access settings. / \u8BF7\u5728 Host Remote \u8BBE\u7F6E\u4E2D\u5141\u8BB8\u6B64\u7AEF\u53E3\uFF0C\u7136\u540E\u4FDD\u5B58\u8BBF\u95EE\u8BBE\u7F6E\u3002");
|
|
4877
|
+
let pending = this.servers.get(port);
|
|
4878
|
+
if (pending === void 0) {
|
|
4879
|
+
if (this.servers.size >= 16) throw new RpcError("RATE_LIMITED", "Too many preview origins.");
|
|
4880
|
+
pending = this.start(port);
|
|
4881
|
+
this.servers.set(port, pending);
|
|
4882
|
+
void pending.catch(() => {
|
|
4883
|
+
this.servers.delete(port);
|
|
4884
|
+
});
|
|
4885
|
+
}
|
|
4886
|
+
return { url: (await pending).url };
|
|
4887
|
+
}
|
|
4888
|
+
async close() {
|
|
4889
|
+
this.lifetime.abort();
|
|
4890
|
+
for (const socket of this.sockets) socket.destroy();
|
|
4891
|
+
for (const pending of this.servers.values()) {
|
|
4892
|
+
const running = await pending.catch(() => void 0);
|
|
4893
|
+
if (running === void 0) continue;
|
|
4894
|
+
for (const client of running.ws.clients) client.terminate();
|
|
4895
|
+
running.ws.close();
|
|
4896
|
+
running.server.closeAllConnections();
|
|
4897
|
+
await new Promise((resolve3) => running.server.close(() => resolve3()));
|
|
4898
|
+
}
|
|
4899
|
+
this.servers.clear();
|
|
4900
|
+
}
|
|
4901
|
+
async start(port) {
|
|
4902
|
+
const hostname4 = `dsh-${randomBytes(24).toString("hex")}.localhost`;
|
|
4903
|
+
let authority = "";
|
|
4904
|
+
const accepted = (req) => req.headers.host === authority && (req.headers.origin === void 0 || req.headers.origin === `http://${authority}`) && req.headers["service-worker"] === void 0;
|
|
4905
|
+
const server = createServer((req, res) => {
|
|
4906
|
+
if (!accepted(req)) {
|
|
4907
|
+
res.writeHead(403);
|
|
4908
|
+
res.end("Preview origin denied.");
|
|
4909
|
+
return;
|
|
4910
|
+
}
|
|
4911
|
+
void this.http(port, authority, req, res);
|
|
4912
|
+
});
|
|
4913
|
+
server.requestTimeout = 3e4;
|
|
4914
|
+
server.headersTimeout = 15e3;
|
|
4915
|
+
server.maxHeadersCount = 64;
|
|
4916
|
+
server.on("connection", (socket) => {
|
|
4917
|
+
if (this.sockets.size >= 64 || this.lifetime.signal.aborted) {
|
|
4918
|
+
socket.destroy();
|
|
4919
|
+
return;
|
|
4920
|
+
}
|
|
4921
|
+
this.sockets.add(socket);
|
|
4922
|
+
socket.on("close", () => this.sockets.delete(socket));
|
|
4923
|
+
});
|
|
4924
|
+
const selectedProtocols = /* @__PURE__ */ new WeakMap();
|
|
4925
|
+
const ws = new WebSocketServer({
|
|
4926
|
+
noServer: true,
|
|
4927
|
+
maxPayload: LOOPBACK_MAX_WS_BYTES,
|
|
4928
|
+
perMessageDeflate: false,
|
|
4929
|
+
handleProtocols: (_protocols, req) => selectedProtocols.get(req) || false
|
|
4930
|
+
});
|
|
4931
|
+
server.on("upgrade", (req, socket, head) => {
|
|
4932
|
+
if (!accepted(req)) {
|
|
4933
|
+
socket.destroy();
|
|
4934
|
+
return;
|
|
4935
|
+
}
|
|
4936
|
+
const id4 = randomUUID();
|
|
4937
|
+
const headers = this.headers(req, port);
|
|
4938
|
+
const protocols = (req.headers["sec-websocket-protocol"] ?? "").split(",").map((item) => item.trim()).filter(Boolean);
|
|
4939
|
+
void this.client.rpc("loopback.call", { op: "ws.open", id: id4, port, path: req.url ?? "/", headers, protocols }, this.lifetime.signal).then((result) => {
|
|
4940
|
+
if (socket.destroyed || this.lifetime.signal.aborted) {
|
|
4941
|
+
socket.destroy();
|
|
4942
|
+
void this.release(id4);
|
|
4943
|
+
return;
|
|
4944
|
+
}
|
|
4945
|
+
selectedProtocols.set(req, result.protocol);
|
|
4946
|
+
ws.handleUpgrade(req, socket, head, (local) => this.websocket(id4, local));
|
|
4947
|
+
}).catch(() => {
|
|
4948
|
+
socket.destroy();
|
|
4949
|
+
void this.release(id4);
|
|
4950
|
+
});
|
|
4951
|
+
});
|
|
4952
|
+
await new Promise((resolve3, reject) => {
|
|
4953
|
+
server.once("error", reject);
|
|
4954
|
+
server.listen(0, "127.0.0.1", () => {
|
|
4955
|
+
server.off("error", reject);
|
|
4956
|
+
resolve3();
|
|
4957
|
+
});
|
|
4958
|
+
});
|
|
4959
|
+
const address = server.address();
|
|
4960
|
+
if (address === null || typeof address === "string") {
|
|
4961
|
+
server.close();
|
|
4962
|
+
throw new Error("Preview listener unavailable");
|
|
4963
|
+
}
|
|
4964
|
+
authority = `${hostname4}:${address.port}`;
|
|
4965
|
+
if (this.lifetime.signal.aborted) {
|
|
4966
|
+
server.close();
|
|
4967
|
+
ws.close();
|
|
4968
|
+
throw new Error("Preview closed");
|
|
4969
|
+
}
|
|
4970
|
+
return { server, ws, url: `http://${authority}/` };
|
|
4971
|
+
}
|
|
4972
|
+
headers(req, port) {
|
|
4973
|
+
return cleanHeaders(headerPairs(req.headers)).filter(([name2]) => !["origin", "referer"].includes(name2.toLowerCase())).concat(req.headers.origin === void 0 ? [] : [["origin", `http://127.0.0.1:${port}`]]);
|
|
4974
|
+
}
|
|
4975
|
+
async http(port, authority, req, res) {
|
|
4976
|
+
const id4 = randomUUID();
|
|
4977
|
+
const controller = new AbortController();
|
|
4978
|
+
const abort = () => {
|
|
4979
|
+
controller.abort();
|
|
4980
|
+
void this.release(id4);
|
|
4981
|
+
};
|
|
4982
|
+
res.once("close", abort);
|
|
4983
|
+
const signal = AbortSignal.any([controller.signal, this.lifetime.signal]);
|
|
4984
|
+
try {
|
|
4985
|
+
const chunks = [];
|
|
4986
|
+
let length = 0;
|
|
4987
|
+
for await (const chunk of req) {
|
|
4988
|
+
length += chunk.length;
|
|
4989
|
+
if (length > LOOPBACK_MAX_BODY_BYTES) throw new RpcError("PAYLOAD_TOO_LARGE", "Preview request body is limited to 1 MiB.");
|
|
4990
|
+
chunks.push(chunk);
|
|
4991
|
+
}
|
|
4992
|
+
const head = await this.client.rpc("loopback.call", {
|
|
4993
|
+
op: "http.open",
|
|
4994
|
+
id: id4,
|
|
4995
|
+
port,
|
|
4996
|
+
path: req.url ?? "/",
|
|
4997
|
+
method: req.method ?? "GET",
|
|
4998
|
+
headers: this.headers(req, port),
|
|
4999
|
+
...length === 0 ? {} : { body: Buffer.concat(chunks).toString("base64") }
|
|
5000
|
+
}, signal);
|
|
5001
|
+
const headers = {};
|
|
5002
|
+
for (let [name2, value] of cleanHeaders(head.headers)) {
|
|
5003
|
+
name2 = name2.toLowerCase();
|
|
5004
|
+
if (name2 === "location") {
|
|
5005
|
+
const url = new URL(value, `http://127.0.0.1:${port}/`);
|
|
5006
|
+
if (!["127.0.0.1", "localhost", "[::1]"].includes(url.hostname) || Number(url.port || 80) !== port || url.protocol !== "http:") {
|
|
5007
|
+
throw new Error("Redirect is outside the authorized preview service");
|
|
5008
|
+
}
|
|
5009
|
+
value = `http://${authority}${url.pathname}${url.search}${url.hash}`;
|
|
5010
|
+
}
|
|
5011
|
+
if (name2 === "set-cookie") {
|
|
5012
|
+
value = value.replace(/;\s*domain=[^;]*/ig, "");
|
|
5013
|
+
const existing = headers[name2];
|
|
5014
|
+
headers[name2] = [...Array.isArray(existing) ? existing : [], value];
|
|
5015
|
+
} else if (!["service-worker-allowed", "clear-site-data", "alt-svc"].includes(name2)) headers[name2] = value;
|
|
5016
|
+
}
|
|
5017
|
+
headers["referrer-policy"] = "no-referrer";
|
|
5018
|
+
res.writeHead(head.status, headers);
|
|
5019
|
+
while (!signal.aborted) {
|
|
5020
|
+
const chunk = await this.client.rpc("loopback.call", { op: "http.read", id: id4 }, signal);
|
|
5021
|
+
if (chunk.data !== "" && !res.write(Buffer.from(chunk.data, "base64"))) {
|
|
5022
|
+
await new Promise((resolve3, reject) => {
|
|
5023
|
+
const ready = () => {
|
|
5024
|
+
cleanup();
|
|
5025
|
+
resolve3();
|
|
5026
|
+
};
|
|
5027
|
+
const closed = () => {
|
|
5028
|
+
cleanup();
|
|
5029
|
+
reject(new Error("closed"));
|
|
5030
|
+
};
|
|
5031
|
+
const cleanup = () => {
|
|
5032
|
+
res.off("drain", ready);
|
|
5033
|
+
res.off("close", closed);
|
|
5034
|
+
};
|
|
5035
|
+
res.once("drain", ready);
|
|
5036
|
+
res.once("close", closed);
|
|
5037
|
+
});
|
|
5038
|
+
}
|
|
5039
|
+
if (chunk.done) break;
|
|
5040
|
+
}
|
|
5041
|
+
res.end();
|
|
5042
|
+
} catch {
|
|
5043
|
+
if (!res.headersSent) {
|
|
5044
|
+
res.writeHead(502, { "Content-Type": "text/plain; charset=utf-8" });
|
|
5045
|
+
res.end("Remote preview unavailable. Check the Host service and connection, then reload.");
|
|
5046
|
+
} else res.destroy();
|
|
5047
|
+
} finally {
|
|
5048
|
+
res.off("close", abort);
|
|
5049
|
+
await this.release(id4);
|
|
5050
|
+
}
|
|
5051
|
+
}
|
|
5052
|
+
websocket(id4, socket) {
|
|
5053
|
+
let input2 = Promise.resolve();
|
|
5054
|
+
let queued = 0;
|
|
5055
|
+
let count = 0;
|
|
5056
|
+
const controller = new AbortController();
|
|
5057
|
+
const signal = AbortSignal.any([controller.signal, this.lifetime.signal]);
|
|
5058
|
+
const close = () => {
|
|
5059
|
+
controller.abort();
|
|
5060
|
+
socket.terminate();
|
|
5061
|
+
void this.release(id4);
|
|
5062
|
+
};
|
|
5063
|
+
socket.on("error", close);
|
|
5064
|
+
socket.on("close", close);
|
|
5065
|
+
socket.on("message", (data2, binary) => {
|
|
5066
|
+
const bytes = Buffer.from(data2);
|
|
5067
|
+
queued += bytes.length;
|
|
5068
|
+
count += 1;
|
|
5069
|
+
if (queued > 1024 * 1024 || count > 256) {
|
|
5070
|
+
close();
|
|
5071
|
+
return;
|
|
5072
|
+
}
|
|
5073
|
+
input2 = input2.then(async () => {
|
|
5074
|
+
await this.client.rpc("loopback.call", { op: "ws.send", id: id4, data: bytes.toString("base64"), binary }, signal);
|
|
5075
|
+
queued -= bytes.length;
|
|
5076
|
+
count -= 1;
|
|
5077
|
+
}).catch(close);
|
|
5078
|
+
});
|
|
5079
|
+
void (async () => {
|
|
5080
|
+
try {
|
|
5081
|
+
while (!signal.aborted) {
|
|
5082
|
+
const result = await this.client.rpc("loopback.call", { op: "ws.read", id: id4 }, signal);
|
|
5083
|
+
for (const message of result.messages) {
|
|
5084
|
+
if (socket.bufferedAmount > 1024 * 1024) throw new Error("slow preview");
|
|
5085
|
+
await new Promise((resolve3, reject) => socket.send(Buffer.from(message.data, "base64"), { binary: message.binary }, (error) => error ? reject(error) : resolve3()));
|
|
5086
|
+
}
|
|
5087
|
+
if (result.closed) break;
|
|
5088
|
+
}
|
|
5089
|
+
} catch {
|
|
5090
|
+
} finally {
|
|
5091
|
+
close();
|
|
5092
|
+
}
|
|
5093
|
+
})();
|
|
5094
|
+
}
|
|
5095
|
+
async release(id4) {
|
|
5096
|
+
if (!this.lifetime.signal.aborted) await this.client.rpc("loopback.call", { op: "close", id: id4 }).catch(() => void 0);
|
|
5097
|
+
}
|
|
5098
|
+
};
|
|
5099
|
+
|
|
4574
5100
|
// ../client-core/dist/remote-gateway.js
|
|
4575
5101
|
var DIRECT_REMOTE_CALL_BYTES = 2 * 1024 * 1024;
|
|
4576
5102
|
var RemoteGatewayError = class extends Error {
|
|
@@ -4787,12 +5313,12 @@ function deriveCodexCwdWorkspaces(values) {
|
|
|
4787
5313
|
continue;
|
|
4788
5314
|
}
|
|
4789
5315
|
const baseId = `cwd-${hashWorkspacePath(key)}`;
|
|
4790
|
-
let
|
|
4791
|
-
for (let suffix = 2; usedIds.has(
|
|
4792
|
-
|
|
4793
|
-
usedIds.add(
|
|
5316
|
+
let id4 = baseId;
|
|
5317
|
+
for (let suffix = 2; usedIds.has(id4); suffix += 1)
|
|
5318
|
+
id4 = `${baseId}-${suffix}`;
|
|
5319
|
+
usedIds.add(id4);
|
|
4794
5320
|
byPath.set(key, {
|
|
4795
|
-
id:
|
|
5321
|
+
id: id4,
|
|
4796
5322
|
name: workspaceBasename(path),
|
|
4797
5323
|
path,
|
|
4798
5324
|
position: byPath.size,
|
|
@@ -4966,16 +5492,16 @@ function parseDataImageUrl(value) {
|
|
|
4966
5492
|
if (match === null)
|
|
4967
5493
|
return void 0;
|
|
4968
5494
|
const mediaType = match[1];
|
|
4969
|
-
const
|
|
4970
|
-
if (!CODEX_IMAGE_MEDIA_TYPES.has(mediaType) || !isCanonicalBase64(
|
|
5495
|
+
const data2 = match[2];
|
|
5496
|
+
if (!CODEX_IMAGE_MEDIA_TYPES.has(mediaType) || !isCanonicalBase64(data2))
|
|
4971
5497
|
return void 0;
|
|
4972
|
-
return { url: `data:${mediaType};base64,${
|
|
5498
|
+
return { url: `data:${mediaType};base64,${data2}`, mediaType, data: data2 };
|
|
4973
5499
|
}
|
|
4974
5500
|
function isCanonicalBase64(value) {
|
|
4975
5501
|
return value.length >= 4 && value.length % 4 === 0 && /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u.test(value);
|
|
4976
5502
|
}
|
|
4977
|
-
function sniffImageMediaType(
|
|
4978
|
-
const bytes = base64PrefixBytes(
|
|
5503
|
+
function sniffImageMediaType(data2) {
|
|
5504
|
+
const bytes = base64PrefixBytes(data2, 32);
|
|
4979
5505
|
if (bytes.length >= 8 && bytes[0] === 137 && bytes[1] === 80 && bytes[2] === 78 && bytes[3] === 71 && bytes[4] === 13 && bytes[5] === 10 && bytes[6] === 26 && bytes[7] === 10)
|
|
4980
5506
|
return "image/png";
|
|
4981
5507
|
if (bytes.length >= 3 && bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255)
|
|
@@ -5181,7 +5707,7 @@ var RemoteClientCore = class {
|
|
|
5181
5707
|
if (this.unsubscribeTransport !== void 0)
|
|
5182
5708
|
return;
|
|
5183
5709
|
this.closeNotified = false;
|
|
5184
|
-
this.unsubscribeTransport = this.transport.onMessage((
|
|
5710
|
+
this.unsubscribeTransport = this.transport.onMessage((data2) => this.handleMessage(data2));
|
|
5185
5711
|
this.unsubscribeClose = this.transport.onClose?.(() => this.handleTransportClose());
|
|
5186
5712
|
try {
|
|
5187
5713
|
await this.transport.connect();
|
|
@@ -5252,8 +5778,8 @@ var RemoteClientCore = class {
|
|
|
5252
5778
|
this.rejectAllPending((pending) => new RemoteClientError("TRANSPORT_CLOSED", `RPC ${pending.method} terminated because the remote transport closed`));
|
|
5253
5779
|
this.notifyClose();
|
|
5254
5780
|
}
|
|
5255
|
-
handleMessage(
|
|
5256
|
-
const message = decodeMessage(
|
|
5781
|
+
handleMessage(data2) {
|
|
5782
|
+
const message = decodeMessage(data2);
|
|
5257
5783
|
if (message.type === "rpc.response")
|
|
5258
5784
|
this.handleResponse(message);
|
|
5259
5785
|
if (message.type === "rpc.error")
|
|
@@ -5314,6 +5840,18 @@ function transportSendError(error) {
|
|
|
5314
5840
|
return error instanceof Error ? error : new Error("remote transport send failed", { cause: error });
|
|
5315
5841
|
}
|
|
5316
5842
|
|
|
5843
|
+
// ../webrtc/dist/websocket-backpressure.js
|
|
5844
|
+
async function waitForRelayCapacity(socket) {
|
|
5845
|
+
const started = Date.now();
|
|
5846
|
+
while ((socket.bufferedAmount ?? 0) > 512 * 1024) {
|
|
5847
|
+
if (socket.readyState !== 1 || Date.now() - started > 1e4)
|
|
5848
|
+
throw new Error("Relay consumer is too slow or disconnected");
|
|
5849
|
+
await new Promise((resolve3) => setTimeout(resolve3, 10));
|
|
5850
|
+
}
|
|
5851
|
+
if (socket.readyState !== 1)
|
|
5852
|
+
throw new Error("Relay transport closed");
|
|
5853
|
+
}
|
|
5854
|
+
|
|
5317
5855
|
// ../webrtc/dist/transport.js
|
|
5318
5856
|
var BaseTransport = class {
|
|
5319
5857
|
handlers = /* @__PURE__ */ new Set();
|
|
@@ -5326,9 +5864,9 @@ var BaseTransport = class {
|
|
|
5326
5864
|
this.closeHandlers.add(cb);
|
|
5327
5865
|
return () => this.closeHandlers.delete(cb);
|
|
5328
5866
|
}
|
|
5329
|
-
emit(
|
|
5867
|
+
emit(data2) {
|
|
5330
5868
|
for (const handler of this.handlers)
|
|
5331
|
-
handler(
|
|
5869
|
+
handler(data2);
|
|
5332
5870
|
}
|
|
5333
5871
|
emitClose() {
|
|
5334
5872
|
for (const handler of this.closeHandlers)
|
|
@@ -5571,8 +6109,8 @@ function adaptDataChannel(raw) {
|
|
|
5571
6109
|
get onbufferedamountlow() {
|
|
5572
6110
|
return raw.onbufferedamountlow;
|
|
5573
6111
|
},
|
|
5574
|
-
send(
|
|
5575
|
-
raw.send(
|
|
6112
|
+
send(data2) {
|
|
6113
|
+
raw.send(data2);
|
|
5576
6114
|
},
|
|
5577
6115
|
close() {
|
|
5578
6116
|
raw.close();
|
|
@@ -5591,19 +6129,19 @@ var MAX_ASSEMBLY_AGE_MS = 3e4;
|
|
|
5591
6129
|
var RtcChunkCodec = class {
|
|
5592
6130
|
nextMessageId = 1;
|
|
5593
6131
|
assemblies = /* @__PURE__ */ new Map();
|
|
5594
|
-
encode(
|
|
5595
|
-
if (
|
|
5596
|
-
return [
|
|
5597
|
-
if (
|
|
6132
|
+
encode(data2) {
|
|
6133
|
+
if (data2.byteLength <= RTC_CHUNK_PAYLOAD_BYTES)
|
|
6134
|
+
return [data2];
|
|
6135
|
+
if (data2.byteLength > RTC_CHUNK_MAX_MESSAGE_BYTES) {
|
|
5598
6136
|
throw new Error("WebRTC transport message exceeds the reassembly limit.");
|
|
5599
6137
|
}
|
|
5600
6138
|
const messageId = this.nextMessageId;
|
|
5601
6139
|
this.nextMessageId = messageId === 4294967295 ? 1 : messageId + 1;
|
|
5602
|
-
const total = Math.ceil(
|
|
6140
|
+
const total = Math.ceil(data2.byteLength / RTC_CHUNK_PAYLOAD_BYTES);
|
|
5603
6141
|
const frames = [];
|
|
5604
6142
|
for (let index = 0; index < total; index += 1) {
|
|
5605
6143
|
const start = index * RTC_CHUNK_PAYLOAD_BYTES;
|
|
5606
|
-
const chunk =
|
|
6144
|
+
const chunk = data2.subarray(start, Math.min(data2.byteLength, start + RTC_CHUNK_PAYLOAD_BYTES));
|
|
5607
6145
|
const frame = new Uint8Array(RTC_CHUNK_HEADER_BYTES + chunk.byteLength);
|
|
5608
6146
|
frame.set(RTC_CHUNK_MAGIC);
|
|
5609
6147
|
const view = new DataView(frame.buffer);
|
|
@@ -5789,11 +6327,11 @@ var RtcDataChannelTransport = class {
|
|
|
5789
6327
|
else
|
|
5790
6328
|
void this.handleIce(signal.candidate);
|
|
5791
6329
|
}
|
|
5792
|
-
async send(
|
|
6330
|
+
async send(data2) {
|
|
5793
6331
|
const channel = this.requireOpenChannel();
|
|
5794
6332
|
if (channel.readyState !== "open")
|
|
5795
6333
|
throw new Error("WebRTC data channel is not open.");
|
|
5796
|
-
for (const frame of this.outgoing.encode(
|
|
6334
|
+
for (const frame of this.outgoing.encode(data2)) {
|
|
5797
6335
|
try {
|
|
5798
6336
|
channel.send(toArrayBuffer(frame));
|
|
5799
6337
|
} catch (error) {
|
|
@@ -5801,7 +6339,7 @@ var RtcDataChannelTransport = class {
|
|
|
5801
6339
|
throw error;
|
|
5802
6340
|
}
|
|
5803
6341
|
}
|
|
5804
|
-
this.bytesSent +=
|
|
6342
|
+
this.bytesSent += data2.byteLength;
|
|
5805
6343
|
this.armWatchdog(channel);
|
|
5806
6344
|
}
|
|
5807
6345
|
onMessage(handler) {
|
|
@@ -5990,8 +6528,8 @@ var RtcDataChannelTransport = class {
|
|
|
5990
6528
|
channel.onmessage = (event) => {
|
|
5991
6529
|
if (this.closed || !this.opened)
|
|
5992
6530
|
return;
|
|
5993
|
-
const
|
|
5994
|
-
const frame = typeof
|
|
6531
|
+
const data2 = event.data;
|
|
6532
|
+
const frame = typeof data2 === "string" ? new TextEncoder().encode(data2) : new Uint8Array(data2);
|
|
5995
6533
|
this.bytesReceived += frame.byteLength;
|
|
5996
6534
|
try {
|
|
5997
6535
|
const message = this.incoming.decode(frame);
|
|
@@ -6182,7 +6720,7 @@ function inspectSelectedPath(stats) {
|
|
|
6182
6720
|
}
|
|
6183
6721
|
}
|
|
6184
6722
|
const pairs = [
|
|
6185
|
-
...[...selectedPairIds].map((
|
|
6723
|
+
...[...selectedPairIds].map((id4) => candidatePairs.get(id4)).filter((pair) => pair !== void 0),
|
|
6186
6724
|
...selectedPairs
|
|
6187
6725
|
];
|
|
6188
6726
|
for (const pair of pairs) {
|
|
@@ -6370,10 +6908,10 @@ function requireSdp(description) {
|
|
|
6370
6908
|
}
|
|
6371
6909
|
return description.sdp;
|
|
6372
6910
|
}
|
|
6373
|
-
function toArrayBuffer(
|
|
6374
|
-
if (
|
|
6375
|
-
return
|
|
6376
|
-
return
|
|
6911
|
+
function toArrayBuffer(data2) {
|
|
6912
|
+
if (data2.byteOffset === 0 && data2.byteLength === data2.buffer.byteLength)
|
|
6913
|
+
return data2.buffer;
|
|
6914
|
+
return data2.slice().buffer;
|
|
6377
6915
|
}
|
|
6378
6916
|
function asError(error) {
|
|
6379
6917
|
return error instanceof Error ? error : new RtcConnectError("RTC_FAILED", "WebRTC negotiation failed.");
|
|
@@ -6445,26 +6983,27 @@ var AdaptiveTransport = class extends BaseTransport {
|
|
|
6445
6983
|
};
|
|
6446
6984
|
});
|
|
6447
6985
|
}
|
|
6448
|
-
async send(
|
|
6986
|
+
async send(data2) {
|
|
6449
6987
|
if (this.connectionId === void 0)
|
|
6450
6988
|
throw new Error("adaptive transport has not been authorized");
|
|
6451
6989
|
if (this.dataMode === "webrtc") {
|
|
6452
6990
|
const rtc = this.rtc;
|
|
6453
6991
|
if (rtc === void 0)
|
|
6454
6992
|
throw new Error("webrtc data channel is not available");
|
|
6455
|
-
this.bytesSent +=
|
|
6456
|
-
await rtc.send(
|
|
6993
|
+
this.bytesSent += data2.byteLength;
|
|
6994
|
+
await rtc.send(data2);
|
|
6457
6995
|
return;
|
|
6458
6996
|
}
|
|
6459
6997
|
if (this.socket?.readyState !== WebSocket.OPEN)
|
|
6460
6998
|
throw new Error("adaptive transport is not connected");
|
|
6999
|
+
await waitForRelayCapacity(this.socket);
|
|
6461
7000
|
this.sendControl("relay", {
|
|
6462
7001
|
connectionId: this.connectionId,
|
|
6463
7002
|
targetDeviceId: this.options.targetDeviceId,
|
|
6464
7003
|
counter: this.relayCounter,
|
|
6465
|
-
ciphertext: toBase64Url(
|
|
7004
|
+
ciphertext: toBase64Url(data2)
|
|
6466
7005
|
});
|
|
6467
|
-
this.bytesSent +=
|
|
7006
|
+
this.bytesSent += data2.byteLength;
|
|
6468
7007
|
this.relayCounter += 1;
|
|
6469
7008
|
}
|
|
6470
7009
|
connectionInfo() {
|
|
@@ -6488,7 +7027,7 @@ var AdaptiveTransport = class extends BaseTransport {
|
|
|
6488
7027
|
...this.rtc === void 0 ? {} : { webRtc: await this.rtc.connectionDetails() }
|
|
6489
7028
|
};
|
|
6490
7029
|
}
|
|
6491
|
-
async sendHandshake(step,
|
|
7030
|
+
async sendHandshake(step, data2) {
|
|
6492
7031
|
if (this.socket?.readyState !== WebSocket.OPEN || this.connectionId === void 0) {
|
|
6493
7032
|
throw new Error("adaptive transport has not been authorized");
|
|
6494
7033
|
}
|
|
@@ -6496,7 +7035,7 @@ var AdaptiveTransport = class extends BaseTransport {
|
|
|
6496
7035
|
connectionId: this.connectionId,
|
|
6497
7036
|
targetDeviceId: this.options.targetDeviceId,
|
|
6498
7037
|
step,
|
|
6499
|
-
data: toBase64Url(
|
|
7038
|
+
data: toBase64Url(data2)
|
|
6500
7039
|
});
|
|
6501
7040
|
}
|
|
6502
7041
|
onHandshake(cb) {
|
|
@@ -6580,9 +7119,9 @@ var AdaptiveTransport = class extends BaseTransport {
|
|
|
6580
7119
|
if (payload.connectionId !== this.connectionId || payload.targetDeviceId !== this.options.deviceId || !Number.isSafeInteger(payload.step) || typeof payload.data !== "string") {
|
|
6581
7120
|
throw new Error("Received a secure handshake frame for an unknown connection");
|
|
6582
7121
|
}
|
|
6583
|
-
const
|
|
7122
|
+
const data2 = fromBase64Url(payload.data);
|
|
6584
7123
|
for (const handler of this.handshakeHandlers)
|
|
6585
|
-
handler(payload.step,
|
|
7124
|
+
handler(payload.step, data2);
|
|
6586
7125
|
return;
|
|
6587
7126
|
}
|
|
6588
7127
|
if (frame.type === "signal.answer") {
|
|
@@ -6611,9 +7150,9 @@ var AdaptiveTransport = class extends BaseTransport {
|
|
|
6611
7150
|
if (payload.connectionId !== this.connectionId || payload.targetDeviceId !== this.options.deviceId || !Number.isSafeInteger(payload.counter) || typeof payload.ciphertext !== "string") {
|
|
6612
7151
|
throw new Error("Received a relay frame for an unknown connection");
|
|
6613
7152
|
}
|
|
6614
|
-
const
|
|
6615
|
-
this.bytesReceived +=
|
|
6616
|
-
this.emit(
|
|
7153
|
+
const data2 = fromBase64Url(payload.ciphertext);
|
|
7154
|
+
this.bytesReceived += data2.byteLength;
|
|
7155
|
+
this.emit(data2);
|
|
6617
7156
|
}
|
|
6618
7157
|
async negotiate() {
|
|
6619
7158
|
if (this.connectionId === void 0)
|
|
@@ -6705,9 +7244,9 @@ var AdaptiveTransport = class extends BaseTransport {
|
|
|
6705
7244
|
}
|
|
6706
7245
|
});
|
|
6707
7246
|
this.rtc = rtc;
|
|
6708
|
-
rtc.onMessage((
|
|
6709
|
-
this.bytesReceived +=
|
|
6710
|
-
this.emit(
|
|
7247
|
+
rtc.onMessage((data2) => {
|
|
7248
|
+
this.bytesReceived += data2.byteLength;
|
|
7249
|
+
this.emit(data2);
|
|
6711
7250
|
});
|
|
6712
7251
|
rtc.onClose(() => {
|
|
6713
7252
|
if (this.rtc === rtc && this.dataMode === "webrtc")
|
|
@@ -6802,6 +7341,27 @@ function negotiateTimeout(serverMs, localMs) {
|
|
|
6802
7341
|
return Math.max(serverMs, localMs);
|
|
6803
7342
|
}
|
|
6804
7343
|
|
|
7344
|
+
// ../webrtc/dist/serial-send.js
|
|
7345
|
+
var SerialSend = class {
|
|
7346
|
+
tail = Promise.resolve();
|
|
7347
|
+
bytes = 0;
|
|
7348
|
+
count = 0;
|
|
7349
|
+
async run(bytes, send) {
|
|
7350
|
+
if (this.bytes + bytes > 8 * 1024 * 1024 || this.count >= 256)
|
|
7351
|
+
throw new Error("Encrypted send queue exceeds its limit");
|
|
7352
|
+
this.bytes += bytes;
|
|
7353
|
+
this.count += 1;
|
|
7354
|
+
const task = this.tail.then(send);
|
|
7355
|
+
this.tail = task.catch(() => void 0);
|
|
7356
|
+
try {
|
|
7357
|
+
await task;
|
|
7358
|
+
} finally {
|
|
7359
|
+
this.bytes -= bytes;
|
|
7360
|
+
this.count -= 1;
|
|
7361
|
+
}
|
|
7362
|
+
}
|
|
7363
|
+
};
|
|
7364
|
+
|
|
6805
7365
|
// src/api-proxy-switch.ts
|
|
6806
7366
|
var SWITCHED_DOMAINS = [
|
|
6807
7367
|
"sessions",
|
|
@@ -6855,10 +7415,10 @@ var ApiProxySwitch = class {
|
|
|
6855
7415
|
});
|
|
6856
7416
|
this.installed = true;
|
|
6857
7417
|
}
|
|
6858
|
-
selectRemote(api,
|
|
7418
|
+
selectRemote(api, target2) {
|
|
6859
7419
|
if (!this.installed) throw new Error("The Harness API switch is not installed.");
|
|
6860
7420
|
this.remote = api;
|
|
6861
|
-
this.target = { ...
|
|
7421
|
+
this.target = { ...target2 };
|
|
6862
7422
|
this.mode = "remote";
|
|
6863
7423
|
}
|
|
6864
7424
|
selectLocal() {
|
|
@@ -6904,7 +7464,7 @@ import * as nc from "node:crypto";
|
|
|
6904
7464
|
var crypto = nc && typeof nc === "object" && "webcrypto" in nc ? nc.webcrypto : nc && typeof nc === "object" && "randomBytes" in nc ? nc : void 0;
|
|
6905
7465
|
|
|
6906
7466
|
// ../../node_modules/.pnpm/@noble+ciphers@1.3.0/node_modules/@noble/ciphers/esm/webcrypto.js
|
|
6907
|
-
function
|
|
7467
|
+
function randomBytes2(bytesLength = 32) {
|
|
6908
7468
|
if (crypto && typeof crypto.getRandomValues === "function") {
|
|
6909
7469
|
return crypto.getRandomValues(new Uint8Array(bytesLength));
|
|
6910
7470
|
}
|
|
@@ -7004,11 +7564,11 @@ function utf8ToBytes(str) {
|
|
|
7004
7564
|
throw new Error("string expected");
|
|
7005
7565
|
return new Uint8Array(new TextEncoder().encode(str));
|
|
7006
7566
|
}
|
|
7007
|
-
function toBytes(
|
|
7008
|
-
if (typeof
|
|
7009
|
-
|
|
7010
|
-
abytes(
|
|
7011
|
-
return
|
|
7567
|
+
function toBytes(data2) {
|
|
7568
|
+
if (typeof data2 === "string")
|
|
7569
|
+
data2 = utf8ToBytes(data2);
|
|
7570
|
+
abytes(data2);
|
|
7571
|
+
return data2;
|
|
7012
7572
|
}
|
|
7013
7573
|
function concatBytes(...arrays) {
|
|
7014
7574
|
let sum = 0;
|
|
@@ -7035,7 +7595,7 @@ function createHasher(hashCons) {
|
|
|
7035
7595
|
hashC.create = () => hashCons();
|
|
7036
7596
|
return hashC;
|
|
7037
7597
|
}
|
|
7038
|
-
function
|
|
7598
|
+
function randomBytes3(bytesLength = 32) {
|
|
7039
7599
|
if (crypto2 && typeof crypto2.getRandomValues === "function") {
|
|
7040
7600
|
return crypto2.getRandomValues(new Uint8Array(bytesLength));
|
|
7041
7601
|
}
|
|
@@ -7072,21 +7632,21 @@ var HashMD = class extends Hash {
|
|
|
7072
7632
|
this.buffer = new Uint8Array(blockLen);
|
|
7073
7633
|
this.view = createView(this.buffer);
|
|
7074
7634
|
}
|
|
7075
|
-
update(
|
|
7635
|
+
update(data2) {
|
|
7076
7636
|
aexists(this);
|
|
7077
|
-
|
|
7078
|
-
abytes(
|
|
7637
|
+
data2 = toBytes(data2);
|
|
7638
|
+
abytes(data2);
|
|
7079
7639
|
const { view, buffer, blockLen } = this;
|
|
7080
|
-
const len =
|
|
7640
|
+
const len = data2.length;
|
|
7081
7641
|
for (let pos = 0; pos < len; ) {
|
|
7082
7642
|
const take = Math.min(blockLen - this.pos, len - pos);
|
|
7083
7643
|
if (take === blockLen) {
|
|
7084
|
-
const dataView = createView(
|
|
7644
|
+
const dataView = createView(data2);
|
|
7085
7645
|
for (; blockLen <= len - pos; pos += blockLen)
|
|
7086
7646
|
this.process(dataView, pos);
|
|
7087
7647
|
continue;
|
|
7088
7648
|
}
|
|
7089
|
-
buffer.set(
|
|
7649
|
+
buffer.set(data2.subarray(pos, pos + take), this.pos);
|
|
7090
7650
|
this.pos += take;
|
|
7091
7651
|
pos += take;
|
|
7092
7652
|
if (this.pos === blockLen) {
|
|
@@ -7094,7 +7654,7 @@ var HashMD = class extends Hash {
|
|
|
7094
7654
|
this.pos = 0;
|
|
7095
7655
|
}
|
|
7096
7656
|
}
|
|
7097
|
-
this.length +=
|
|
7657
|
+
this.length += data2.length;
|
|
7098
7658
|
this.roundClean();
|
|
7099
7659
|
return this;
|
|
7100
7660
|
}
|
|
@@ -8471,13 +9031,13 @@ function eddsa(Point, cHash, eddsaOpts = {}) {
|
|
|
8471
9031
|
});
|
|
8472
9032
|
const { prehash } = eddsaOpts;
|
|
8473
9033
|
const { BASE, Fp: Fp2, Fn: Fn2 } = Point;
|
|
8474
|
-
const
|
|
9034
|
+
const randomBytes8 = eddsaOpts.randomBytes || randomBytes3;
|
|
8475
9035
|
const adjustScalarBytes3 = eddsaOpts.adjustScalarBytes || ((bytes) => bytes);
|
|
8476
|
-
const domain = eddsaOpts.domain || ((
|
|
9036
|
+
const domain = eddsaOpts.domain || ((data2, ctx, phflag) => {
|
|
8477
9037
|
_abool2(phflag, "phflag");
|
|
8478
9038
|
if (ctx.length || phflag)
|
|
8479
9039
|
throw new Error("Contexts/pre-hash are not supported");
|
|
8480
|
-
return
|
|
9040
|
+
return data2;
|
|
8481
9041
|
});
|
|
8482
9042
|
function modN_LE(hash) {
|
|
8483
9043
|
return Fn2.create(bytesToNumberLE(hash));
|
|
@@ -8553,7 +9113,7 @@ function eddsa(Point, cHash, eddsaOpts = {}) {
|
|
|
8553
9113
|
signature: 2 * _size,
|
|
8554
9114
|
seed: _size
|
|
8555
9115
|
};
|
|
8556
|
-
function randomSecretKey(seed =
|
|
9116
|
+
function randomSecretKey(seed = randomBytes8(lengths.seed)) {
|
|
8557
9117
|
return _abytes2(seed, lengths.seed, "seed");
|
|
8558
9118
|
}
|
|
8559
9119
|
function keygen(seed) {
|
|
@@ -8672,7 +9232,7 @@ function montgomery(curveDef) {
|
|
|
8672
9232
|
const is25519 = type === "x25519";
|
|
8673
9233
|
if (!is25519 && type !== "x448")
|
|
8674
9234
|
throw new Error("invalid type");
|
|
8675
|
-
const randomBytes_ = rand ||
|
|
9235
|
+
const randomBytes_ = rand || randomBytes3;
|
|
8676
9236
|
const montgomeryBits = is25519 ? 255 : 448;
|
|
8677
9237
|
const fieldLen = is25519 ? 32 : 56;
|
|
8678
9238
|
const Gu = is25519 ? BigInt(9) : BigInt(5);
|
|
@@ -9321,8 +9881,8 @@ var SymmetricState = class {
|
|
|
9321
9881
|
}
|
|
9322
9882
|
this.ck.set(this.h);
|
|
9323
9883
|
}
|
|
9324
|
-
mixHash(
|
|
9325
|
-
this.h.set(this.H.hash(concat(this.h,
|
|
9884
|
+
mixHash(data2) {
|
|
9885
|
+
this.h.set(this.H.hash(concat(this.h, data2)).subarray(0, this.H.hashLen));
|
|
9326
9886
|
}
|
|
9327
9887
|
mixKey(inputKeyMaterial) {
|
|
9328
9888
|
const [ck, temp] = this.H.hkdf(this.ck, inputKeyMaterial);
|
|
@@ -9345,13 +9905,13 @@ var SymmetricState = class {
|
|
|
9345
9905
|
}
|
|
9346
9906
|
this.mixHash(out);
|
|
9347
9907
|
}
|
|
9348
|
-
decryptAndHash(
|
|
9908
|
+
decryptAndHash(data2, out) {
|
|
9349
9909
|
if (this.cipherstate) {
|
|
9350
|
-
this.cipherstate.decryptWithAd(this.h,
|
|
9910
|
+
this.cipherstate.decryptWithAd(this.h, data2, out);
|
|
9351
9911
|
} else {
|
|
9352
|
-
out.set(
|
|
9912
|
+
out.set(data2);
|
|
9353
9913
|
}
|
|
9354
|
-
this.mixHash(
|
|
9914
|
+
this.mixHash(data2);
|
|
9355
9915
|
}
|
|
9356
9916
|
split() {
|
|
9357
9917
|
if (!this.hasKey()) {
|
|
@@ -9751,7 +10311,7 @@ function createHasher2(hashCons, info = {}) {
|
|
|
9751
10311
|
Object.assign(hashC, info);
|
|
9752
10312
|
return Object.freeze(hashC);
|
|
9753
10313
|
}
|
|
9754
|
-
function
|
|
10314
|
+
function randomBytes4(bytesLength = 32) {
|
|
9755
10315
|
anumber2(bytesLength, "bytesLength");
|
|
9756
10316
|
const cr = typeof globalThis === "object" ? globalThis.crypto : null;
|
|
9757
10317
|
if (typeof cr?.getRandomValues !== "function")
|
|
@@ -9794,22 +10354,22 @@ var HashMD2 = class {
|
|
|
9794
10354
|
this.buffer = new Uint8Array(blockLen);
|
|
9795
10355
|
this.view = createView2(this.buffer);
|
|
9796
10356
|
}
|
|
9797
|
-
update(
|
|
10357
|
+
update(data2) {
|
|
9798
10358
|
aexists2(this);
|
|
9799
|
-
abytes2(
|
|
10359
|
+
abytes2(data2);
|
|
9800
10360
|
const { view, buffer, blockLen } = this;
|
|
9801
|
-
const len =
|
|
10361
|
+
const len = data2.length;
|
|
9802
10362
|
let processed = false;
|
|
9803
10363
|
for (let pos = 0; pos < len; ) {
|
|
9804
10364
|
const take = Math.min(blockLen - this.pos, len - pos);
|
|
9805
10365
|
if (take === blockLen) {
|
|
9806
|
-
const dataView = createView2(
|
|
10366
|
+
const dataView = createView2(data2);
|
|
9807
10367
|
for (; blockLen <= len - pos; pos += blockLen)
|
|
9808
10368
|
this.process(dataView, pos);
|
|
9809
10369
|
processed = true;
|
|
9810
10370
|
continue;
|
|
9811
10371
|
}
|
|
9812
|
-
buffer.set(pos === 0 && take === len ?
|
|
10372
|
+
buffer.set(pos === 0 && take === len ? data2 : data2.subarray(pos, pos + take), this.pos);
|
|
9813
10373
|
this.pos += take;
|
|
9814
10374
|
pos += take;
|
|
9815
10375
|
if (this.pos === blockLen) {
|
|
@@ -9818,7 +10378,7 @@ var HashMD2 = class {
|
|
|
9818
10378
|
processed = true;
|
|
9819
10379
|
}
|
|
9820
10380
|
}
|
|
9821
|
-
this.length +=
|
|
10381
|
+
this.length += data2.length;
|
|
9822
10382
|
if (processed)
|
|
9823
10383
|
this.roundClean();
|
|
9824
10384
|
return this;
|
|
@@ -10318,7 +10878,7 @@ function afunction(value, title) {
|
|
|
10318
10878
|
var bytesToHex3 = bytesToHex2;
|
|
10319
10879
|
var hexToBytes3 = (hex) => hexToBytes2(hex);
|
|
10320
10880
|
var isBytes3 = isBytes2;
|
|
10321
|
-
var
|
|
10881
|
+
var randomBytes5 = (bytesLength) => randomBytes4(bytesLength);
|
|
10322
10882
|
var _0n7 = /* @__PURE__ */ BigInt(0);
|
|
10323
10883
|
var _1n7 = /* @__PURE__ */ BigInt(1);
|
|
10324
10884
|
var atitle2 = (title) => title ? `"${title}" ` : "";
|
|
@@ -10877,18 +11437,18 @@ function validateTableBytes(numPoints, fpBytes) {
|
|
|
10877
11437
|
if (bytes > TABLE_BYTES_MAX)
|
|
10878
11438
|
throw new Error("invalid window size: table would need ~" + Math.ceil(bytes / 2 ** 20) + " MiB, max " + TABLE_BYTES_MAX / 2 ** 20 + " MiB");
|
|
10879
11439
|
}
|
|
10880
|
-
function probeRandomBytes(
|
|
10881
|
-
if (
|
|
11440
|
+
function probeRandomBytes(randomBytes8, length) {
|
|
11441
|
+
if (randomBytes8 === void 0)
|
|
10882
11442
|
return void 0;
|
|
10883
|
-
afunction(
|
|
11443
|
+
afunction(randomBytes8, "randomBytes");
|
|
10884
11444
|
try {
|
|
10885
|
-
const probe =
|
|
11445
|
+
const probe = randomBytes8(length);
|
|
10886
11446
|
if (!isBytes3(probe) || probe.length !== length)
|
|
10887
11447
|
return void 0;
|
|
10888
11448
|
} catch {
|
|
10889
11449
|
return void 0;
|
|
10890
11450
|
}
|
|
10891
|
-
return
|
|
11451
|
+
return randomBytes8;
|
|
10892
11452
|
}
|
|
10893
11453
|
function validateMSMPoints2(points, c) {
|
|
10894
11454
|
aarray(points, "points");
|
|
@@ -10981,9 +11541,9 @@ var ScalarMultiplier = class {
|
|
|
10981
11541
|
baseCanBeBlinded;
|
|
10982
11542
|
bits;
|
|
10983
11543
|
// Parametrized with a given Point class (not individual point)
|
|
10984
|
-
constructor(Point,
|
|
11544
|
+
constructor(Point, randomBytes8) {
|
|
10985
11545
|
validatePointCons(Point);
|
|
10986
|
-
this.randomBytes = probeRandomBytes(
|
|
11546
|
+
this.randomBytes = probeRandomBytes(randomBytes8, BLIND_BYTES);
|
|
10987
11547
|
this.Point = Point;
|
|
10988
11548
|
this.BASE = Point.BASE;
|
|
10989
11549
|
this.ZERO = Point.ZERO;
|
|
@@ -11254,7 +11814,7 @@ function edwards2(params, extraOpts = {}) {
|
|
|
11254
11814
|
if (FpLegendre2(Fp2, CURVE.d) !== -1)
|
|
11255
11815
|
throw new Error("edwards: CURVE.d must be a non-square in Fp for complete addition formulas");
|
|
11256
11816
|
validateObject2(opts, {}, { uvRatio: "function", randomBytes: "function" });
|
|
11257
|
-
const
|
|
11817
|
+
const randomBytes8 = opts.randomBytes === void 0 ? randomBytes5 : opts.randomBytes;
|
|
11258
11818
|
const MASK = _2n6 << BigInt(Fp2.BYTES * 8) - _1n10;
|
|
11259
11819
|
function isOdd(n) {
|
|
11260
11820
|
if (!Fp2.isOdd)
|
|
@@ -11515,7 +12075,7 @@ function edwards2(params, extraOpts = {}) {
|
|
|
11515
12075
|
}
|
|
11516
12076
|
}
|
|
11517
12077
|
const normalize = (points) => normalizeZ2(Point, points);
|
|
11518
|
-
const wnaf = new ScalarMultiplier(Point,
|
|
12078
|
+
const wnaf = new ScalarMultiplier(Point, randomBytes8);
|
|
11519
12079
|
if (wnaf.bits >= 6)
|
|
11520
12080
|
Point.BASE.precompute(6);
|
|
11521
12081
|
Object.freeze(Point.prototype);
|
|
@@ -11558,7 +12118,7 @@ function montgomery2(curveDef) {
|
|
|
11558
12118
|
const is25519 = type === "x25519";
|
|
11559
12119
|
if (!is25519 && type !== "x448")
|
|
11560
12120
|
throw new Error("invalid type");
|
|
11561
|
-
const randomBytes_ = rand === void 0 ?
|
|
12121
|
+
const randomBytes_ = rand === void 0 ? randomBytes5 : rand;
|
|
11562
12122
|
const montgomeryBits = is25519 ? 255 : 448;
|
|
11563
12123
|
const swap = cswap(P);
|
|
11564
12124
|
const fieldLen = is25519 ? 32 : 56;
|
|
@@ -11847,11 +12407,11 @@ var NqHandshake = class {
|
|
|
11847
12407
|
isFinished() {
|
|
11848
12408
|
return this.status === 2;
|
|
11849
12409
|
}
|
|
11850
|
-
mixHash(
|
|
11851
|
-
this.symmetric.mixHash(
|
|
12410
|
+
mixHash(data2) {
|
|
12411
|
+
this.symmetric.mixHash(data2);
|
|
11852
12412
|
}
|
|
11853
|
-
mixKeyAndHash(
|
|
11854
|
-
this.symmetric.mixKeyAndHash(
|
|
12413
|
+
mixKeyAndHash(data2) {
|
|
12414
|
+
this.symmetric.mixKeyAndHash(data2);
|
|
11855
12415
|
}
|
|
11856
12416
|
pushPsk(psk) {
|
|
11857
12417
|
if (psk.length !== PSK_LEN) {
|
|
@@ -12525,13 +13085,13 @@ var _BLAKE2 = class {
|
|
|
12525
13085
|
this.buffer = new Uint8Array(blockLen);
|
|
12526
13086
|
this.buffer32 = u32(this.buffer);
|
|
12527
13087
|
}
|
|
12528
|
-
update(
|
|
13088
|
+
update(data2) {
|
|
12529
13089
|
aexists2(this);
|
|
12530
|
-
abytes2(
|
|
13090
|
+
abytes2(data2);
|
|
12531
13091
|
const { blockLen, buffer, buffer32 } = this;
|
|
12532
|
-
const len =
|
|
12533
|
-
const offset =
|
|
12534
|
-
const buf =
|
|
13092
|
+
const len = data2.length;
|
|
13093
|
+
const offset = data2.byteOffset;
|
|
13094
|
+
const buf = data2.buffer;
|
|
12535
13095
|
for (let pos = 0; pos < len; ) {
|
|
12536
13096
|
if (this.pos === blockLen) {
|
|
12537
13097
|
swap32IfBE(buffer32);
|
|
@@ -12551,7 +13111,7 @@ var _BLAKE2 = class {
|
|
|
12551
13111
|
swap32IfBE(data32);
|
|
12552
13112
|
continue;
|
|
12553
13113
|
}
|
|
12554
|
-
buffer.set(pos === 0 && take === len ?
|
|
13114
|
+
buffer.set(pos === 0 && take === len ? data2 : data2.subarray(pos, pos + take), this.pos);
|
|
12555
13115
|
this.pos += take;
|
|
12556
13116
|
this.length += take;
|
|
12557
13117
|
pos += take;
|
|
@@ -12935,8 +13495,8 @@ var hmac = /* @__PURE__ */ (() => {
|
|
|
12935
13495
|
|
|
12936
13496
|
// ../../node_modules/.pnpm/@lukeburns+clatterjs@1.0.0/node_modules/@lukeburns/clatterjs/dist/crypto/hash.js
|
|
12937
13497
|
function makeHash(name2, hash, hashLen, blockLen) {
|
|
12938
|
-
const doHash = (
|
|
12939
|
-
const hmac1 = (key,
|
|
13498
|
+
const doHash = (data2) => hash.create().update(data2).digest();
|
|
13499
|
+
const hmac1 = (key, data2) => hmac(hash, key, data2);
|
|
12940
13500
|
const hmacMany = (key, parts) => hmac1(key, parts.length === 1 ? parts[0] : concatBytes2(...parts));
|
|
12941
13501
|
return {
|
|
12942
13502
|
name: name2,
|
|
@@ -13089,20 +13649,20 @@ var wrapCipher = /* @__NO_SIDE_EFFECTS__ */ (params, constructor) => {
|
|
|
13089
13649
|
};
|
|
13090
13650
|
let called = false;
|
|
13091
13651
|
const wrCipher = {
|
|
13092
|
-
encrypt(
|
|
13652
|
+
encrypt(data2, output) {
|
|
13093
13653
|
if (called)
|
|
13094
13654
|
throw new Error("cannot encrypt() twice with same key + nonce");
|
|
13095
13655
|
called = true;
|
|
13096
|
-
abytes4(
|
|
13656
|
+
abytes4(data2, void 0, "data");
|
|
13097
13657
|
checkOutput(cipher.encrypt.length, output);
|
|
13098
|
-
return cipher.encrypt(
|
|
13658
|
+
return cipher.encrypt(data2, output);
|
|
13099
13659
|
},
|
|
13100
|
-
decrypt(
|
|
13101
|
-
abytes4(
|
|
13102
|
-
if (tagl &&
|
|
13660
|
+
decrypt(data2, output) {
|
|
13661
|
+
abytes4(data2, void 0, "data");
|
|
13662
|
+
if (tagl && data2.length < tagl)
|
|
13103
13663
|
throw new Error('"ciphertext" expected length >= tagLength=' + tagl);
|
|
13104
13664
|
checkOutput(cipher.decrypt.length, output);
|
|
13105
|
-
return cipher.decrypt(
|
|
13665
|
+
return cipher.decrypt(data2, output);
|
|
13106
13666
|
}
|
|
13107
13667
|
};
|
|
13108
13668
|
return wrCipher;
|
|
@@ -13146,12 +13706,12 @@ var BLOCK_LEN = 64;
|
|
|
13146
13706
|
var BLOCK_LEN32 = 16;
|
|
13147
13707
|
var MAX_COUNTER = /* @__PURE__ */ (() => 2 ** 32 - 1)();
|
|
13148
13708
|
var U32_EMPTY = /* @__PURE__ */ Uint32Array.of();
|
|
13149
|
-
function runCipher(core, sigma, key, nonce,
|
|
13150
|
-
const len =
|
|
13709
|
+
function runCipher(core, sigma, key, nonce, data2, output, counter, rounds) {
|
|
13710
|
+
const len = data2.length;
|
|
13151
13711
|
const block = new Uint8Array(BLOCK_LEN);
|
|
13152
13712
|
const b32 = u322(block);
|
|
13153
|
-
const isAligned = isLE2 && isAligned32(
|
|
13154
|
-
const d32 = isAligned ? u322(
|
|
13713
|
+
const isAligned = isLE2 && isAligned32(data2) && isAligned32(output);
|
|
13714
|
+
const d32 = isAligned ? u322(data2) : U32_EMPTY;
|
|
13155
13715
|
const o32 = isAligned ? u322(output) : U32_EMPTY;
|
|
13156
13716
|
if (!isLE2) {
|
|
13157
13717
|
for (let pos = 0; pos < len; counter++) {
|
|
@@ -13162,7 +13722,7 @@ function runCipher(core, sigma, key, nonce, data, output, counter, rounds) {
|
|
|
13162
13722
|
const take = Math.min(BLOCK_LEN, len - pos);
|
|
13163
13723
|
for (let j = 0, posj; j < take; j++) {
|
|
13164
13724
|
posj = pos + j;
|
|
13165
|
-
output[posj] =
|
|
13725
|
+
output[posj] = data2[posj] ^ block[j];
|
|
13166
13726
|
}
|
|
13167
13727
|
pos += take;
|
|
13168
13728
|
}
|
|
@@ -13186,7 +13746,7 @@ function runCipher(core, sigma, key, nonce, data, output, counter, rounds) {
|
|
|
13186
13746
|
}
|
|
13187
13747
|
for (let j = 0, posj; j < take; j++) {
|
|
13188
13748
|
posj = pos + j;
|
|
13189
|
-
output[posj] =
|
|
13749
|
+
output[posj] = data2[posj] ^ block[j];
|
|
13190
13750
|
}
|
|
13191
13751
|
pos += take;
|
|
13192
13752
|
}
|
|
@@ -13199,11 +13759,11 @@ function createCipher(core, opts) {
|
|
|
13199
13759
|
anumber4(rounds);
|
|
13200
13760
|
abool2(counterRight);
|
|
13201
13761
|
abool2(allowShortKeys);
|
|
13202
|
-
return (key, nonce,
|
|
13762
|
+
return (key, nonce, data2, output, counter = 0) => {
|
|
13203
13763
|
abytes4(key, void 0, "key");
|
|
13204
13764
|
abytes4(nonce, void 0, "nonce");
|
|
13205
|
-
abytes4(
|
|
13206
|
-
const len =
|
|
13765
|
+
abytes4(data2, void 0, "data");
|
|
13766
|
+
const len = data2.length;
|
|
13207
13767
|
output = getOutput(len, output, false);
|
|
13208
13768
|
anumber4(counter);
|
|
13209
13769
|
if (counter < 0 || counter >= MAX_COUNTER)
|
|
@@ -13254,7 +13814,7 @@ function createCipher(core, opts) {
|
|
|
13254
13814
|
}
|
|
13255
13815
|
const n32 = swap32IfBE2(u322(nonce));
|
|
13256
13816
|
try {
|
|
13257
|
-
runCipher(core, sigma, k32, n32,
|
|
13817
|
+
runCipher(core, sigma, k32, n32, data2, output, counter, rounds);
|
|
13258
13818
|
return output;
|
|
13259
13819
|
} finally {
|
|
13260
13820
|
clean3(...toClean);
|
|
@@ -13301,7 +13861,7 @@ var Poly1305 = class {
|
|
|
13301
13861
|
for (let i = 0; i < 8; i++)
|
|
13302
13862
|
this.pad[i] = u8to16(key, 16 + 2 * i);
|
|
13303
13863
|
}
|
|
13304
|
-
process(
|
|
13864
|
+
process(data2, offset, isLast = false) {
|
|
13305
13865
|
const hibit = isLast ? 0 : 1 << 11;
|
|
13306
13866
|
const { h, r } = this;
|
|
13307
13867
|
const r0 = r[0];
|
|
@@ -13314,14 +13874,14 @@ var Poly1305 = class {
|
|
|
13314
13874
|
const r7 = r[7];
|
|
13315
13875
|
const r8 = r[8];
|
|
13316
13876
|
const r9 = r[9];
|
|
13317
|
-
const t0 = u8to16(
|
|
13318
|
-
const t1 = u8to16(
|
|
13319
|
-
const t2 = u8to16(
|
|
13320
|
-
const t3 = u8to16(
|
|
13321
|
-
const t4 = u8to16(
|
|
13322
|
-
const t5 = u8to16(
|
|
13323
|
-
const t6 = u8to16(
|
|
13324
|
-
const t7 = u8to16(
|
|
13877
|
+
const t0 = u8to16(data2, offset + 0);
|
|
13878
|
+
const t1 = u8to16(data2, offset + 2);
|
|
13879
|
+
const t2 = u8to16(data2, offset + 4);
|
|
13880
|
+
const t3 = u8to16(data2, offset + 6);
|
|
13881
|
+
const t4 = u8to16(data2, offset + 8);
|
|
13882
|
+
const t5 = u8to16(data2, offset + 10);
|
|
13883
|
+
const t6 = u8to16(data2, offset + 12);
|
|
13884
|
+
const t7 = u8to16(data2, offset + 14);
|
|
13325
13885
|
let h0 = h[0] + (t0 & 8191);
|
|
13326
13886
|
let h1 = h[1] + ((t0 >>> 13 | t1 << 3) & 8191);
|
|
13327
13887
|
let h2 = h[2] + ((t1 >>> 10 | t2 << 6) & 8191);
|
|
@@ -13457,20 +14017,20 @@ var Poly1305 = class {
|
|
|
13457
14017
|
}
|
|
13458
14018
|
clean3(g);
|
|
13459
14019
|
}
|
|
13460
|
-
update(
|
|
14020
|
+
update(data2) {
|
|
13461
14021
|
aexists3(this);
|
|
13462
|
-
abytes4(
|
|
13463
|
-
|
|
14022
|
+
abytes4(data2);
|
|
14023
|
+
data2 = copyBytes4(data2);
|
|
13464
14024
|
const { buffer, blockLen } = this;
|
|
13465
|
-
const len =
|
|
14025
|
+
const len = data2.length;
|
|
13466
14026
|
for (let pos = 0; pos < len; ) {
|
|
13467
14027
|
const take = Math.min(blockLen - this.pos, len - pos);
|
|
13468
14028
|
if (take === blockLen) {
|
|
13469
14029
|
for (; blockLen <= len - pos; pos += blockLen)
|
|
13470
|
-
this.process(
|
|
14030
|
+
this.process(data2, pos);
|
|
13471
14031
|
continue;
|
|
13472
14032
|
}
|
|
13473
|
-
buffer.set(
|
|
14033
|
+
buffer.set(data2.subarray(pos, pos + take), this.pos);
|
|
13474
14034
|
this.pos += take;
|
|
13475
14035
|
pos += take;
|
|
13476
14036
|
if (this.pos === blockLen) {
|
|
@@ -13644,9 +14204,9 @@ var _poly1305_aead = (xorStream) => (key, nonce, AAD) => {
|
|
|
13644
14204
|
},
|
|
13645
14205
|
decrypt(ciphertext, output) {
|
|
13646
14206
|
output = getOutput(ciphertext.length - tagLength, output, false);
|
|
13647
|
-
const
|
|
14207
|
+
const data2 = ciphertext.subarray(0, -tagLength);
|
|
13648
14208
|
const passedTag = ciphertext.subarray(-tagLength);
|
|
13649
|
-
const tag = computeTag(xorStream, key, nonce,
|
|
14209
|
+
const tag = computeTag(xorStream, key, nonce, data2, AAD);
|
|
13650
14210
|
if (!equalBytes2(passedTag, tag)) {
|
|
13651
14211
|
clean3(tag);
|
|
13652
14212
|
throw new Error("invalid tag");
|
|
@@ -13765,7 +14325,7 @@ var NoiseIkSession = class {
|
|
|
13765
14325
|
...initiator ? { rs: Uint8Array.from(this.expectedRemoteStatic) } : {},
|
|
13766
14326
|
cipher: chachaPoly,
|
|
13767
14327
|
hash: sha256H,
|
|
13768
|
-
rng: options.random ??
|
|
14328
|
+
rng: options.random ?? randomBytes2
|
|
13769
14329
|
});
|
|
13770
14330
|
if (this.handshake.getName() !== NOISE_IK_PROTOCOL) {
|
|
13771
14331
|
throw new NoiseSessionError("NOISE_SUITE_MISMATCH", "The Noise provider selected an unexpected cipher suite.");
|
|
@@ -13915,6 +14475,7 @@ var ClientSecureTransport = class {
|
|
|
13915
14475
|
incoming = new SecureMessageCodec();
|
|
13916
14476
|
outgoing = new SecureMessageCodec();
|
|
13917
14477
|
closed = false;
|
|
14478
|
+
sends = new SerialSend();
|
|
13918
14479
|
async connect() {
|
|
13919
14480
|
this.closed = false;
|
|
13920
14481
|
this.incoming.reset();
|
|
@@ -13942,12 +14503,14 @@ var ClientSecureTransport = class {
|
|
|
13942
14503
|
throw error;
|
|
13943
14504
|
}
|
|
13944
14505
|
}
|
|
13945
|
-
async send(
|
|
13946
|
-
|
|
14506
|
+
async send(data2) {
|
|
14507
|
+
if (data2.byteLength > MAX_SECURE_MESSAGE_BYTES) throw new Error("Secure message exceeds the reassembly limit.");
|
|
14508
|
+
const noise = this.requireNoise();
|
|
13947
14509
|
try {
|
|
13948
|
-
|
|
13949
|
-
|
|
13950
|
-
|
|
14510
|
+
await this.sends.run(data2.byteLength, async () => {
|
|
14511
|
+
if (this.closed || this.noise !== noise) throw new Error("Secure transport replaced");
|
|
14512
|
+
for (const plaintext of this.outgoing.encode(data2)) await this.inner.send(noise.encrypt(plaintext));
|
|
14513
|
+
});
|
|
13951
14514
|
} catch (error) {
|
|
13952
14515
|
await this.close().catch(() => void 0);
|
|
13953
14516
|
throw error;
|
|
@@ -13955,11 +14518,11 @@ var ClientSecureTransport = class {
|
|
|
13955
14518
|
}
|
|
13956
14519
|
onMessage(handler) {
|
|
13957
14520
|
this.unsubscribeInner?.();
|
|
13958
|
-
this.unsubscribeInner = this.inner.onMessage((
|
|
14521
|
+
this.unsubscribeInner = this.inner.onMessage((data2) => {
|
|
13959
14522
|
const noise = this.noise;
|
|
13960
14523
|
if (noise === void 0 || !noise.complete || this.closed) return;
|
|
13961
14524
|
try {
|
|
13962
|
-
const message = this.incoming.decode(noise.decrypt(
|
|
14525
|
+
const message = this.incoming.decode(noise.decrypt(data2));
|
|
13963
14526
|
if (message !== void 0) handler(message);
|
|
13964
14527
|
} catch {
|
|
13965
14528
|
void this.close();
|
|
@@ -13998,11 +14561,11 @@ async function waitForResponder(inner, noise) {
|
|
|
13998
14561
|
await new Promise((resolve3, reject) => {
|
|
13999
14562
|
let settled = false;
|
|
14000
14563
|
const timer = setTimeout(() => finish(new Error("Noise IK handshake timed out.")), 1e4);
|
|
14001
|
-
const unsubscribe = inner.onHandshake((step,
|
|
14564
|
+
const unsubscribe = inner.onHandshake((step, data2) => {
|
|
14002
14565
|
if (settled) return;
|
|
14003
14566
|
try {
|
|
14004
14567
|
if (step !== 2) throw new Error("Noise IK responder sent an out-of-order handshake message.");
|
|
14005
|
-
noise.readHandshake(
|
|
14568
|
+
noise.readHandshake(data2);
|
|
14006
14569
|
if (!noise.complete) throw new Error("Noise IK handshake did not complete.");
|
|
14007
14570
|
finish();
|
|
14008
14571
|
} catch (error) {
|
|
@@ -14025,16 +14588,19 @@ async function waitForResponder(inner, noise) {
|
|
|
14025
14588
|
|
|
14026
14589
|
// src/control-route.ts
|
|
14027
14590
|
var CONTROL_RPC_PREFIX = "/ds-harness-remote";
|
|
14591
|
+
var STATUS_STREAM_ENDPOINT = "status.events";
|
|
14592
|
+
var STATUS_STREAM_PATH = `${CONTROL_RPC_PREFIX}/${STATUS_STREAM_ENDPOINT}`;
|
|
14028
14593
|
var ENDPOINT_SEGMENT_PATTERN = /^[A-Za-z0-9_$.-]+$/;
|
|
14029
14594
|
var INVALID_REQUEST_RPC_ID = "invalid-request";
|
|
14030
|
-
function registerControlRoute(connection, handler, webServer) {
|
|
14595
|
+
function registerControlRoute(connection, handler, webServer, statusStream) {
|
|
14031
14596
|
if (webServer !== void 0 && connection.requestRejection !== void 0) {
|
|
14032
14597
|
const dispose = webServer.register({
|
|
14033
14598
|
kind: "prefix",
|
|
14034
14599
|
path: CONTROL_RPC_PREFIX,
|
|
14035
|
-
handler: (req, res) => handleControlRequest(connection, handler, req, res)
|
|
14600
|
+
handler: (req, res) => handleControlRequest(connection, handler, req, res, statusStream)
|
|
14036
14601
|
});
|
|
14037
14602
|
return async () => {
|
|
14603
|
+
statusStream?.close();
|
|
14038
14604
|
await dispose();
|
|
14039
14605
|
};
|
|
14040
14606
|
}
|
|
@@ -14042,7 +14608,7 @@ function registerControlRoute(connection, handler, webServer) {
|
|
|
14042
14608
|
authority: "loopback"
|
|
14043
14609
|
});
|
|
14044
14610
|
}
|
|
14045
|
-
async function handleControlRequest(connection, handler, req, res) {
|
|
14611
|
+
async function handleControlRequest(connection, handler, req, res, statusStream) {
|
|
14046
14612
|
const rejection = connection.requestRejection?.(req);
|
|
14047
14613
|
if (rejection !== void 0) {
|
|
14048
14614
|
res.writeHead(rejection);
|
|
@@ -14050,6 +14616,10 @@ async function handleControlRequest(connection, handler, req, res) {
|
|
|
14050
14616
|
return;
|
|
14051
14617
|
}
|
|
14052
14618
|
const endpoint = endpointFromPath(CONTROL_RPC_PREFIX, new URL(req.url ?? "/", "http://dsh.internal").pathname);
|
|
14619
|
+
if (req.method === "GET" && endpoint === STATUS_STREAM_ENDPOINT && statusStream !== void 0) {
|
|
14620
|
+
await statusStream.handle(res);
|
|
14621
|
+
return;
|
|
14622
|
+
}
|
|
14053
14623
|
if (req.method !== "POST" || endpoint === void 0) {
|
|
14054
14624
|
writeText(res, 404, "not found");
|
|
14055
14625
|
return;
|
|
@@ -14141,12 +14711,12 @@ function writeText(res, status2, body) {
|
|
|
14141
14711
|
}
|
|
14142
14712
|
|
|
14143
14713
|
// src/ids.ts
|
|
14144
|
-
import { randomBytes as
|
|
14714
|
+
import { randomBytes as randomBytes7 } from "node:crypto";
|
|
14145
14715
|
function uuidV7(now = Date.now()) {
|
|
14146
14716
|
if (!Number.isSafeInteger(now) || now < 0 || now > 281474976710655) {
|
|
14147
14717
|
throw new RangeError("UUIDv7 timestamp must be a non-negative 48-bit integer");
|
|
14148
14718
|
}
|
|
14149
|
-
const bytes =
|
|
14719
|
+
const bytes = randomBytes7(16);
|
|
14150
14720
|
let timestamp = BigInt(now);
|
|
14151
14721
|
for (let index = 5; index >= 0; index -= 1) {
|
|
14152
14722
|
bytes[index] = Number(timestamp & 0xffn);
|
|
@@ -14460,13 +15030,13 @@ var AsyncFrameQueue = class {
|
|
|
14460
15030
|
};
|
|
14461
15031
|
function routeStreamEvent(event, streamId, queue) {
|
|
14462
15032
|
if (event.event === "harness.api.frame") {
|
|
14463
|
-
const
|
|
14464
|
-
if (
|
|
14465
|
-
queue.push(
|
|
15033
|
+
const data2 = event.data;
|
|
15034
|
+
if (data2.streamId !== streamId || typeof data2.frame !== "object" || data2.frame === null || typeof data2.frame.rpcId !== "string" || !("payload" in data2.frame)) return;
|
|
15035
|
+
queue.push(data2.frame);
|
|
14466
15036
|
}
|
|
14467
15037
|
if (event.event === "harness.api.stream.closed") {
|
|
14468
|
-
const
|
|
14469
|
-
if (
|
|
15038
|
+
const data2 = event.data;
|
|
15039
|
+
if (data2.streamId === streamId) queue.close();
|
|
14470
15040
|
}
|
|
14471
15041
|
}
|
|
14472
15042
|
function bytesToBase642(bytes) {
|
|
@@ -14575,8 +15145,8 @@ function normalizeEvent(value) {
|
|
|
14575
15145
|
const { sourceEventSeqs: _sourceEventSeqs, ...rest } = next;
|
|
14576
15146
|
next = rest;
|
|
14577
15147
|
}
|
|
14578
|
-
const
|
|
14579
|
-
if (
|
|
15148
|
+
const data2 = normalizeEventData(type, next.data);
|
|
15149
|
+
if (data2 !== next.data) next = { ...next, data: data2 };
|
|
14580
15150
|
const surfaceOp = normalizeSurfaceOp(next.surfaceOp);
|
|
14581
15151
|
if (surfaceOp !== next.surfaceOp) next = { ...next, surfaceOp };
|
|
14582
15152
|
return next;
|
|
@@ -14774,14 +15344,26 @@ var RemoteTypertGateway2 = class {
|
|
|
14774
15344
|
};
|
|
14775
15345
|
var AsyncValueQueue = class {
|
|
14776
15346
|
values = [];
|
|
15347
|
+
bytes = 0;
|
|
14777
15348
|
waiters = [];
|
|
14778
15349
|
closed = false;
|
|
14779
15350
|
error;
|
|
14780
15351
|
push(value) {
|
|
14781
15352
|
if (this.closed) return;
|
|
14782
15353
|
const waiter = this.waiters.shift();
|
|
14783
|
-
if (waiter
|
|
14784
|
-
|
|
15354
|
+
if (waiter !== void 0) {
|
|
15355
|
+
waiter({ done: false, value });
|
|
15356
|
+
return;
|
|
15357
|
+
}
|
|
15358
|
+
const bytes = new TextEncoder().encode(JSON.stringify(value) ?? "").byteLength;
|
|
15359
|
+
if (this.values.length >= 256 || this.bytes + bytes > 4 * 1024 * 1024) {
|
|
15360
|
+
this.values.length = 0;
|
|
15361
|
+
this.bytes = 0;
|
|
15362
|
+
this.fail(new RemoteClientError("TRANSPORT_CLOSED", "Remote stream consumer is too slow; reconnect to recover."));
|
|
15363
|
+
return;
|
|
15364
|
+
}
|
|
15365
|
+
this.bytes += bytes;
|
|
15366
|
+
this.values.push({ value, bytes });
|
|
14785
15367
|
}
|
|
14786
15368
|
close() {
|
|
14787
15369
|
if (this.closed) return;
|
|
@@ -14796,7 +15378,9 @@ var AsyncValueQueue = class {
|
|
|
14796
15378
|
async *[Symbol.asyncIterator]() {
|
|
14797
15379
|
while (true) {
|
|
14798
15380
|
if (this.values.length > 0) {
|
|
14799
|
-
|
|
15381
|
+
const entry = this.values.shift();
|
|
15382
|
+
this.bytes -= entry.bytes;
|
|
15383
|
+
yield entry.value;
|
|
14800
15384
|
continue;
|
|
14801
15385
|
}
|
|
14802
15386
|
if (this.closed) {
|
|
@@ -14814,15 +15398,15 @@ var AsyncValueQueue = class {
|
|
|
14814
15398
|
};
|
|
14815
15399
|
function routeStreamEvent2(event, streamId, queue) {
|
|
14816
15400
|
if (event.event === "harness.remote.frame") {
|
|
14817
|
-
const
|
|
14818
|
-
if (
|
|
15401
|
+
const data3 = event.data;
|
|
15402
|
+
if (data3.streamId === streamId && data3.hasValue === true) queue.push(data3.value);
|
|
14819
15403
|
return;
|
|
14820
15404
|
}
|
|
14821
15405
|
if (event.event !== "harness.remote.stream.closed") return;
|
|
14822
|
-
const
|
|
14823
|
-
if (
|
|
14824
|
-
if (
|
|
14825
|
-
const failure2 =
|
|
15406
|
+
const data2 = event.data;
|
|
15407
|
+
if (data2.streamId !== streamId) return;
|
|
15408
|
+
if (data2.reason === "failed") {
|
|
15409
|
+
const failure2 = data2.failure;
|
|
14826
15410
|
queue.fail(remoteFailure({
|
|
14827
15411
|
code: typeof failure2?.code === "string" ? failure2.code : "internal",
|
|
14828
15412
|
message: typeof failure2?.message === "string" ? failure2.message : "The remote Harness stream failed.",
|
|
@@ -15589,7 +16173,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
|
|
|
15589
16173
|
follow.liveToolResultSeq.delete(itemId);
|
|
15590
16174
|
follow.liveToolResultSeq.delete(`${itemId}:call`);
|
|
15591
16175
|
}
|
|
15592
|
-
pushEvent(follow, type,
|
|
16176
|
+
pushEvent(follow, type, data2, view, placement) {
|
|
15593
16177
|
const seq = follow.nextSeq++;
|
|
15594
16178
|
const event = {
|
|
15595
16179
|
type,
|
|
@@ -15597,7 +16181,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
|
|
|
15597
16181
|
time: Date.now(),
|
|
15598
16182
|
data: adaptEventData(
|
|
15599
16183
|
type,
|
|
15600
|
-
type === "assistant/message" && this.sessionGeneration === "v3" && follow.assistantAttempt !== void 0 ? { ...record2(
|
|
16184
|
+
type === "assistant/message" && this.sessionGeneration === "v3" && follow.assistantAttempt !== void 0 ? { ...record2(data2), stream: follow.assistantAttempt.stream } : data2,
|
|
15601
16185
|
this.sessionGeneration
|
|
15602
16186
|
),
|
|
15603
16187
|
...isSurfaceEvent(type) ? placement === void 0 ? { surfaceOp: "append" } : {
|
|
@@ -15625,9 +16209,9 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
|
|
|
15625
16209
|
for (const block of collectImageBlocks(value)) {
|
|
15626
16210
|
const attachment = record2(block.attachment);
|
|
15627
16211
|
const attachmentId = string(attachment.attachmentId);
|
|
15628
|
-
const
|
|
15629
|
-
if (attachmentId === void 0 ||
|
|
15630
|
-
this.imageAttachments.set(attachmentId, { sessionId, attachment, data });
|
|
16212
|
+
const data2 = string(block.data);
|
|
16213
|
+
if (attachmentId === void 0 || data2 === void 0 || !attachmentId.startsWith(CODEX_IMAGE_ATTACHMENT_PREFIX)) continue;
|
|
16214
|
+
this.imageAttachments.set(attachmentId, { sessionId, attachment, data: data2 });
|
|
15631
16215
|
}
|
|
15632
16216
|
}
|
|
15633
16217
|
ensureStreamBlock(follow, key, itemId, kind) {
|
|
@@ -16287,7 +16871,7 @@ async function loadCatalog(client, signal, pendingThreads) {
|
|
|
16287
16871
|
if (cursor2 === void 0) break;
|
|
16288
16872
|
}
|
|
16289
16873
|
if (pendingThreads !== void 0) {
|
|
16290
|
-
const listedIds = new Set(threads.map((thread) => string(thread.id)).filter((
|
|
16874
|
+
const listedIds = new Set(threads.map((thread) => string(thread.id)).filter((id4) => id4 !== void 0));
|
|
16291
16875
|
for (const [threadId, thread] of pendingThreads) {
|
|
16292
16876
|
if (listedIds.has(threadId)) pendingThreads.delete(threadId);
|
|
16293
16877
|
else threads.unshift(thread);
|
|
@@ -16362,8 +16946,8 @@ async function loadModelDirectory(client, signal) {
|
|
|
16362
16946
|
for (const value of array(result.data)) {
|
|
16363
16947
|
const source = record2(value);
|
|
16364
16948
|
if (source.hidden === true) continue;
|
|
16365
|
-
const
|
|
16366
|
-
if (
|
|
16949
|
+
const id4 = string(source.model) ?? string(source.id);
|
|
16950
|
+
if (id4 === void 0 || models.has(id4)) continue;
|
|
16367
16951
|
const efforts = array(source.supportedReasoningEfforts).map((value2) => {
|
|
16368
16952
|
const effort = record2(value2);
|
|
16369
16953
|
const effortId = string(effort.reasoningEffort);
|
|
@@ -16378,8 +16962,8 @@ async function loadModelDirectory(client, signal) {
|
|
|
16378
16962
|
const defaultEffort2 = string(source.defaultReasoningEffort);
|
|
16379
16963
|
const description = string(source.description);
|
|
16380
16964
|
const model = {
|
|
16381
|
-
id:
|
|
16382
|
-
name: string(source.displayName) ??
|
|
16965
|
+
id: id4,
|
|
16966
|
+
name: string(source.displayName) ?? id4,
|
|
16383
16967
|
...description === void 0 ? {} : { description },
|
|
16384
16968
|
...efforts.length === 0 ? {} : {
|
|
16385
16969
|
reasoning: {
|
|
@@ -16388,8 +16972,8 @@ async function loadModelDirectory(client, signal) {
|
|
|
16388
16972
|
}
|
|
16389
16973
|
}
|
|
16390
16974
|
};
|
|
16391
|
-
models.set(
|
|
16392
|
-
if (source.isDefault === true) defaultModelId =
|
|
16975
|
+
models.set(id4, model);
|
|
16976
|
+
if (source.isDefault === true) defaultModelId = id4;
|
|
16393
16977
|
}
|
|
16394
16978
|
cursor2 = typeof result.nextCursor === "string" && result.nextCursor.length > 0 ? result.nextCursor : void 0;
|
|
16395
16979
|
if (cursor2 === void 0) break;
|
|
@@ -16412,7 +16996,7 @@ function projectCodexNativeHistory(thread, sessionId, sessionGeneration = "legac
|
|
|
16412
16996
|
let seq = 0;
|
|
16413
16997
|
let turnNumber = 0;
|
|
16414
16998
|
const active = activeTurnId(thread.turns);
|
|
16415
|
-
const append = (type,
|
|
16999
|
+
const append = (type, data2, time = Date.now(), view, sourceEventSeqs) => {
|
|
16416
17000
|
const eventSeq = seq++;
|
|
16417
17001
|
entries.push({
|
|
16418
17002
|
type: "event",
|
|
@@ -16420,7 +17004,7 @@ function projectCodexNativeHistory(thread, sessionId, sessionGeneration = "legac
|
|
|
16420
17004
|
type,
|
|
16421
17005
|
seq: eventSeq,
|
|
16422
17006
|
time,
|
|
16423
|
-
data: adaptEventData(type,
|
|
17007
|
+
data: adaptEventData(type, data2, sessionGeneration),
|
|
16424
17008
|
...sourceEventSeqs === void 0 ? {} : { sourceEventSeqs },
|
|
16425
17009
|
...isSurfaceEvent(type) ? { surfaceOp: "append" } : {}
|
|
16426
17010
|
},
|
|
@@ -16510,9 +17094,9 @@ function paginateCodexNativeHistory(history, request) {
|
|
|
16510
17094
|
...history.activeTurnId === void 0 ? {} : { activeTurnId: history.activeTurnId }
|
|
16511
17095
|
};
|
|
16512
17096
|
}
|
|
16513
|
-
function adaptEventData(type,
|
|
16514
|
-
if (sessionGeneration !== "v3" || type !== "assistant/message") return
|
|
16515
|
-
const value = record2(
|
|
17097
|
+
function adaptEventData(type, data2, sessionGeneration) {
|
|
17098
|
+
if (sessionGeneration !== "v3" || type !== "assistant/message") return data2;
|
|
17099
|
+
const value = record2(data2);
|
|
16516
17100
|
return Array.isArray(value.stream) ? value : { ...value, stream: [] };
|
|
16517
17101
|
}
|
|
16518
17102
|
function adaptCodexHistoryPage(value, sessionGeneration) {
|
|
@@ -16546,14 +17130,14 @@ function adaptCodexHistoryPage(value, sessionGeneration) {
|
|
|
16546
17130
|
}
|
|
16547
17131
|
function itemEvents(item, turn, step, requestId, selection = modelSelection(), sessionId = CODEX_SESSION_PREFIX, supplementalImages = []) {
|
|
16548
17132
|
const type = string(item.type);
|
|
16549
|
-
const
|
|
17133
|
+
const id4 = string(item.id) ?? `${turn}:${step}:${hashString(JSON.stringify(item))}`;
|
|
16550
17134
|
const text = itemText2(item);
|
|
16551
17135
|
if (type === "userMessage") return [{
|
|
16552
17136
|
type: "user/message",
|
|
16553
17137
|
data: {
|
|
16554
|
-
id:
|
|
17138
|
+
id: id4,
|
|
16555
17139
|
role: "user",
|
|
16556
|
-
content: messageContent(item, `${sessionId}:${
|
|
17140
|
+
content: messageContent(item, `${sessionId}:${id4}`, "text", text),
|
|
16557
17141
|
source: requestId === void 0 || requestId === "" ? { kind: "user" } : { kind: "user", rpcId: requestId }
|
|
16558
17142
|
}
|
|
16559
17143
|
}];
|
|
@@ -16563,11 +17147,11 @@ function itemEvents(item, turn, step, requestId, selection = modelSelection(), s
|
|
|
16563
17147
|
turn,
|
|
16564
17148
|
step,
|
|
16565
17149
|
message: {
|
|
16566
|
-
id:
|
|
17150
|
+
id: id4,
|
|
16567
17151
|
role: "assistant",
|
|
16568
17152
|
content: messageContent(
|
|
16569
17153
|
item,
|
|
16570
|
-
`${sessionId}:${
|
|
17154
|
+
`${sessionId}:${id4}`,
|
|
16571
17155
|
type === "reasoning" ? "reasoning" : "text",
|
|
16572
17156
|
text,
|
|
16573
17157
|
type === "agentMessage" ? supplementalImages : []
|
|
@@ -16582,10 +17166,10 @@ function itemEvents(item, turn, step, requestId, selection = modelSelection(), s
|
|
|
16582
17166
|
return [
|
|
16583
17167
|
{
|
|
16584
17168
|
type: "tool/call",
|
|
16585
|
-
data: { turn, step, callId:
|
|
17169
|
+
data: { turn, step, callId: id4, name: name2, arguments: JSON.stringify(args) },
|
|
16586
17170
|
view: { for: "call", view: toolCallView(item, type, args) }
|
|
16587
17171
|
},
|
|
16588
|
-
toolResultEvent(item, turn, step,
|
|
17172
|
+
toolResultEvent(item, turn, step, id4, toolResultText(item, type, text))
|
|
16589
17173
|
];
|
|
16590
17174
|
}
|
|
16591
17175
|
if (type === "error") return [{
|
|
@@ -16594,7 +17178,7 @@ function itemEvents(item, turn, step, requestId, selection = modelSelection(), s
|
|
|
16594
17178
|
turn,
|
|
16595
17179
|
step,
|
|
16596
17180
|
message: {
|
|
16597
|
-
id:
|
|
17181
|
+
id: id4,
|
|
16598
17182
|
role: "assistant",
|
|
16599
17183
|
content: [{ type: "text", text: text ?? "CodeX reported an error." }],
|
|
16600
17184
|
source: { kind: "model", provider: selection.provider, model: selection.model }
|
|
@@ -16681,19 +17265,19 @@ function parseImageBlock(block) {
|
|
|
16681
17265
|
const url = string(block.url) ?? string(block.image_url) ?? string(record2(block.image_url).url);
|
|
16682
17266
|
const parsed = url === void 0 ? void 0 : parseDataImageUrl2(url);
|
|
16683
17267
|
if (parsed !== void 0) return parsed;
|
|
16684
|
-
const
|
|
16685
|
-
if (
|
|
16686
|
-
if (
|
|
16687
|
-
const mediaType = string(block.mediaType) ?? string(block.mimeType) ?? sniffImageMediaType2(
|
|
16688
|
-
return mediaType !== void 0 && CODEX_IMAGE_MEDIA_TYPES2.has(mediaType) ? { mediaType, data, url: `data:${mediaType};base64,${
|
|
17268
|
+
const data2 = string(block.data);
|
|
17269
|
+
if (data2 === void 0 || !isCanonicalBase642(data2)) return void 0;
|
|
17270
|
+
if (data2.length > MAX_CODEX_IMAGE_BASE64) return void 0;
|
|
17271
|
+
const mediaType = string(block.mediaType) ?? string(block.mimeType) ?? sniffImageMediaType2(data2);
|
|
17272
|
+
return mediaType !== void 0 && CODEX_IMAGE_MEDIA_TYPES2.has(mediaType) ? { mediaType, data: data2, url: `data:${mediaType};base64,${data2}` } : void 0;
|
|
16689
17273
|
}
|
|
16690
17274
|
function parseDataImageUrl2(value) {
|
|
16691
17275
|
const match = DATA_IMAGE_URL2.exec(value);
|
|
16692
17276
|
if (match === null) return void 0;
|
|
16693
17277
|
const mediaType = match[1];
|
|
16694
|
-
const
|
|
16695
|
-
if (!CODEX_IMAGE_MEDIA_TYPES2.has(mediaType) || !isCanonicalBase642(
|
|
16696
|
-
return { mediaType, data, url: `data:${mediaType};base64,${
|
|
17278
|
+
const data2 = match[2];
|
|
17279
|
+
if (!CODEX_IMAGE_MEDIA_TYPES2.has(mediaType) || !isCanonicalBase642(data2)) return void 0;
|
|
17280
|
+
return { mediaType, data: data2, url: `data:${mediaType};base64,${data2}` };
|
|
16697
17281
|
}
|
|
16698
17282
|
function collectImageBlocks(value, output = []) {
|
|
16699
17283
|
if (Array.isArray(value)) {
|
|
@@ -16714,8 +17298,8 @@ function base64ByteLength(value) {
|
|
|
16714
17298
|
const padding = value.endsWith("==") ? 2 : value.endsWith("=") ? 1 : 0;
|
|
16715
17299
|
return Math.max(1, Math.floor(value.length * 3 / 4) - padding);
|
|
16716
17300
|
}
|
|
16717
|
-
function imageDimensions(mediaType,
|
|
16718
|
-
const bytes = base64PrefixBytes2(
|
|
17301
|
+
function imageDimensions(mediaType, data2) {
|
|
17302
|
+
const bytes = base64PrefixBytes2(data2, 256 * 1024);
|
|
16719
17303
|
const parsed = mediaType === "image/png" ? pngDimensions(bytes) : mediaType === "image/jpeg" ? jpegDimensions(bytes) : mediaType === "image/gif" ? gifDimensions(bytes) : mediaType === "image/webp" ? webpDimensions(bytes) : void 0;
|
|
16720
17304
|
return parsed ?? { width: 1, height: 1 };
|
|
16721
17305
|
}
|
|
@@ -16768,8 +17352,8 @@ function webpDimensions(bytes) {
|
|
|
16768
17352
|
}
|
|
16769
17353
|
return void 0;
|
|
16770
17354
|
}
|
|
16771
|
-
function sniffImageMediaType2(
|
|
16772
|
-
const bytes = base64PrefixBytes2(
|
|
17355
|
+
function sniffImageMediaType2(data2) {
|
|
17356
|
+
const bytes = base64PrefixBytes2(data2, 32);
|
|
16773
17357
|
if (pngDimensions(bytes) !== void 0) return "image/png";
|
|
16774
17358
|
if (bytes.length >= 3 && bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) return "image/jpeg";
|
|
16775
17359
|
if (gifDimensions(bytes) !== void 0) return "image/gif";
|
|
@@ -16812,7 +17396,7 @@ function stringFromBytes(bytes, start, end) {
|
|
|
16812
17396
|
for (let index = start; index < end; index += 1) output += String.fromCharCode(bytes[index]);
|
|
16813
17397
|
return output;
|
|
16814
17398
|
}
|
|
16815
|
-
function toolResultEvent(item, turn, step,
|
|
17399
|
+
function toolResultEvent(item, turn, step, id4, resultText) {
|
|
16816
17400
|
const type = string(item.type) ?? "unknown";
|
|
16817
17401
|
return {
|
|
16818
17402
|
type: "tool/result",
|
|
@@ -16820,23 +17404,23 @@ function toolResultEvent(item, turn, step, id2, resultText) {
|
|
|
16820
17404
|
turn,
|
|
16821
17405
|
step,
|
|
16822
17406
|
message: {
|
|
16823
|
-
id: `${
|
|
17407
|
+
id: `${id4}:result`,
|
|
16824
17408
|
role: "user",
|
|
16825
17409
|
content: [{
|
|
16826
17410
|
type: "tool-result",
|
|
16827
|
-
toolCallId:
|
|
17411
|
+
toolCallId: id4,
|
|
16828
17412
|
content: [{ type: "text", text: resultText }],
|
|
16829
17413
|
...item.status === "failed" || item.success === false ? { isError: true } : {}
|
|
16830
17414
|
}],
|
|
16831
|
-
source: { kind: "tool", callId:
|
|
17415
|
+
source: { kind: "tool", callId: id4 }
|
|
16832
17416
|
}
|
|
16833
17417
|
},
|
|
16834
17418
|
view: { for: "result", view: toolResultView(item, type, resultText) }
|
|
16835
17419
|
};
|
|
16836
17420
|
}
|
|
16837
17421
|
function toolResultContentReplacement(event) {
|
|
16838
|
-
const
|
|
16839
|
-
const message = record2(
|
|
17422
|
+
const data2 = record2(event.data);
|
|
17423
|
+
const message = record2(data2.message);
|
|
16840
17424
|
const content = array(message.content);
|
|
16841
17425
|
const result = record2(content[0]);
|
|
16842
17426
|
if (result.isError === void 0) return event;
|
|
@@ -16844,7 +17428,7 @@ function toolResultContentReplacement(event) {
|
|
|
16844
17428
|
return {
|
|
16845
17429
|
...event,
|
|
16846
17430
|
data: {
|
|
16847
|
-
...
|
|
17431
|
+
...data2,
|
|
16848
17432
|
message: { ...message, content: [stableResult] }
|
|
16849
17433
|
}
|
|
16850
17434
|
};
|
|
@@ -16978,12 +17562,12 @@ function projectWorkspace(project) {
|
|
|
16978
17562
|
}
|
|
16979
17563
|
function projectCodexProject(value, fallbackPosition) {
|
|
16980
17564
|
const source = record2(value);
|
|
16981
|
-
const
|
|
16982
|
-
if (
|
|
17565
|
+
const id4 = string(source.id);
|
|
17566
|
+
if (id4 === void 0) return void 0;
|
|
16983
17567
|
const roots = array(source.roots).map((root) => string(record2(root).path)).filter((path) => path !== void 0 && path.length > 0).map((path) => ({ path }));
|
|
16984
17568
|
if (roots.length === 0) return void 0;
|
|
16985
17569
|
return {
|
|
16986
|
-
id:
|
|
17570
|
+
id: id4,
|
|
16987
17571
|
name: string(source.name) ?? basename(roots[0].path),
|
|
16988
17572
|
roots,
|
|
16989
17573
|
position: finiteNumber(source.position) ?? fallbackPosition,
|
|
@@ -17409,6 +17993,105 @@ var AsyncValueQueue2 = class {
|
|
|
17409
17993
|
// src/server-api.ts
|
|
17410
17994
|
import { platform } from "node:os";
|
|
17411
17995
|
|
|
17996
|
+
// src/server-credentials.ts
|
|
17997
|
+
import { chmod, mkdir, readFile as readFile2, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
17998
|
+
import { dirname as dirname2, join as join2 } from "node:path";
|
|
17999
|
+
var credentialSchema = external_exports.object({
|
|
18000
|
+
schemaVersion: external_exports.literal(1),
|
|
18001
|
+
serverUrl: external_exports.string().url(),
|
|
18002
|
+
deviceId: external_exports.string().min(1),
|
|
18003
|
+
authorizationMethod: external_exports.enum(["account", "host_registration_code", "owned_device"]),
|
|
18004
|
+
account: external_exports.string().min(1).max(254).optional(),
|
|
18005
|
+
accessToken: external_exports.string().min(16),
|
|
18006
|
+
accessTokenExpiresAt: external_exports.number().int().positive(),
|
|
18007
|
+
refreshToken: external_exports.string().min(16),
|
|
18008
|
+
refreshTokenExpiresAt: external_exports.number().int().positive()
|
|
18009
|
+
}).strict();
|
|
18010
|
+
var ServerCredentialStore = class {
|
|
18011
|
+
path;
|
|
18012
|
+
constructor(directory) {
|
|
18013
|
+
this.path = join2(directory, "server-credentials.json");
|
|
18014
|
+
}
|
|
18015
|
+
/** Serialize the complete read/refresh/write transaction across processes.
|
|
18016
|
+
* Never steal an old lock: a suspended owner may still consume a one-use token.
|
|
18017
|
+
* After a crash, stop all instances before removing the orphaned lock.
|
|
18018
|
+
*/
|
|
18019
|
+
async withRefreshLock(operation) {
|
|
18020
|
+
const lock = `${this.path}.refresh-lock`;
|
|
18021
|
+
await mkdir(dirname2(this.path), { recursive: true, mode: 448 });
|
|
18022
|
+
const deadline = Date.now() + 15e3;
|
|
18023
|
+
for (; ; ) {
|
|
18024
|
+
try {
|
|
18025
|
+
await mkdir(lock, { mode: 448 });
|
|
18026
|
+
break;
|
|
18027
|
+
} catch (error) {
|
|
18028
|
+
if (!(error instanceof Error) || !("code" in error) || error.code !== "EEXIST") throw error;
|
|
18029
|
+
if (Date.now() >= deadline) throw new ServerCredentialsBusyError();
|
|
18030
|
+
await new Promise((resolve3) => setTimeout(resolve3, 50));
|
|
18031
|
+
}
|
|
18032
|
+
}
|
|
18033
|
+
try {
|
|
18034
|
+
return await operation();
|
|
18035
|
+
} finally {
|
|
18036
|
+
await rm(lock, { recursive: true });
|
|
18037
|
+
}
|
|
18038
|
+
}
|
|
18039
|
+
async load(serverUrl, deviceId) {
|
|
18040
|
+
if (!await exists(this.path)) return void 0;
|
|
18041
|
+
await assertPrivateMode(this.path);
|
|
18042
|
+
let parsed;
|
|
18043
|
+
try {
|
|
18044
|
+
parsed = credentialSchema.parse(JSON.parse(await readFile2(this.path, "utf8")));
|
|
18045
|
+
} catch (error) {
|
|
18046
|
+
throw new ServerCredentialsInvalidError(`server credentials are invalid: ${safeMessage(error)}`);
|
|
18047
|
+
}
|
|
18048
|
+
return parsed.serverUrl === serverUrl && parsed.deviceId === deviceId ? parsed : void 0;
|
|
18049
|
+
}
|
|
18050
|
+
async save(credentials) {
|
|
18051
|
+
const record6 = credentialSchema.parse({ schemaVersion: 1, ...credentials });
|
|
18052
|
+
await atomicWrite(this.path, `${JSON.stringify(record6, null, 2)}
|
|
18053
|
+
`);
|
|
18054
|
+
return record6;
|
|
18055
|
+
}
|
|
18056
|
+
async clear() {
|
|
18057
|
+
await rm(this.path, { force: true });
|
|
18058
|
+
}
|
|
18059
|
+
};
|
|
18060
|
+
var ServerCredentialsInvalidError = class extends Error {
|
|
18061
|
+
code = "SERVER_CREDENTIALS_INVALID";
|
|
18062
|
+
};
|
|
18063
|
+
var ServerCredentialsBusyError = class extends Error {
|
|
18064
|
+
code = "SERVER_CREDENTIALS_BUSY";
|
|
18065
|
+
constructor() {
|
|
18066
|
+
super("Credential refresh is locked. Stop other instances; after a crash, stop all instances before removing server-credentials.json.refresh-lock and authorizing again.");
|
|
18067
|
+
}
|
|
18068
|
+
};
|
|
18069
|
+
async function atomicWrite(path, contents) {
|
|
18070
|
+
await mkdir(dirname2(path), { recursive: true, mode: 448 });
|
|
18071
|
+
const temporary = `${path}.${process.pid}.${uuidV7()}.tmp`;
|
|
18072
|
+
await writeFile(temporary, contents, { encoding: "utf8", mode: 384, flag: "wx" });
|
|
18073
|
+
await chmod(temporary, 384);
|
|
18074
|
+
await rename(temporary, path);
|
|
18075
|
+
await chmod(path, 384);
|
|
18076
|
+
}
|
|
18077
|
+
async function assertPrivateMode(path) {
|
|
18078
|
+
if (process.platform === "win32") return;
|
|
18079
|
+
const mode = (await stat(path)).mode & 511;
|
|
18080
|
+
if ((mode & 63) !== 0) throw new ServerCredentialsInvalidError("server credentials permissions must be 0600");
|
|
18081
|
+
}
|
|
18082
|
+
async function exists(path) {
|
|
18083
|
+
try {
|
|
18084
|
+
await stat(path);
|
|
18085
|
+
return true;
|
|
18086
|
+
} catch (error) {
|
|
18087
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") return false;
|
|
18088
|
+
throw error;
|
|
18089
|
+
}
|
|
18090
|
+
}
|
|
18091
|
+
function safeMessage(error) {
|
|
18092
|
+
return error instanceof Error ? error.message : "invalid credential data";
|
|
18093
|
+
}
|
|
18094
|
+
|
|
17412
18095
|
// src/config.ts
|
|
17413
18096
|
import { hostname } from "node:os";
|
|
17414
18097
|
import s from "@deepseek-ai/schemastery";
|
|
@@ -17418,6 +18101,8 @@ var Config = s.object({
|
|
|
17418
18101
|
role: s.union(["host", "client", "both"]),
|
|
17419
18102
|
serverUrl: s.string(),
|
|
17420
18103
|
deviceName: s.string(),
|
|
18104
|
+
terminal: s.object({ enabled: s.boolean() }),
|
|
18105
|
+
loopback: s.object({ ports: s.array(s.number()) }),
|
|
17421
18106
|
forceRelay: s.boolean(),
|
|
17422
18107
|
logLevel: s.union(["debug", "info", "warn", "error"]),
|
|
17423
18108
|
reconnect: s.union([
|
|
@@ -17447,6 +18132,8 @@ var configSchema = external_exports.object({
|
|
|
17447
18132
|
role: external_exports.enum(["host", "client", "both"]).optional(),
|
|
17448
18133
|
serverUrl: external_exports.string().url().optional(),
|
|
17449
18134
|
deviceName: external_exports.string().trim().min(1).max(80).optional(),
|
|
18135
|
+
terminal: external_exports.object({ enabled: external_exports.boolean().optional() }).strict().optional(),
|
|
18136
|
+
loopback: external_exports.object({ ports: external_exports.array(external_exports.number().int().min(1024).max(65535)).max(16).optional() }).strict().optional(),
|
|
17450
18137
|
forceRelay: external_exports.boolean().optional(),
|
|
17451
18138
|
logLevel: external_exports.enum(["debug", "info", "warn", "error"]).optional(),
|
|
17452
18139
|
reconnect: reconnectSchema.optional(),
|
|
@@ -17471,6 +18158,8 @@ function resolveConfig(input2 = {}, env = process.env) {
|
|
|
17471
18158
|
role: parsed.role ?? "host",
|
|
17472
18159
|
...serverUrl === void 0 ? {} : { serverUrl },
|
|
17473
18160
|
deviceName: parsed.deviceName ?? hostname(),
|
|
18161
|
+
terminal: { enabled: parsed.terminal?.enabled ?? false },
|
|
18162
|
+
loopback: { ports: [...new Set(parsed.loopback?.ports ?? [])] },
|
|
17474
18163
|
forceRelay: parsed.forceRelay ?? false,
|
|
17475
18164
|
logLevel: parsed.logLevel ?? "info",
|
|
17476
18165
|
reconnect: {
|
|
@@ -17483,10 +18172,10 @@ function resolveConfig(input2 = {}, env = process.env) {
|
|
|
17483
18172
|
enabled: parsed.codex?.enabled ?? true,
|
|
17484
18173
|
binary: parsed.codex?.binary ?? "codex"
|
|
17485
18174
|
},
|
|
17486
|
-
acp: { enabled: parsed.acp?.enabled ?? true, backends: [.../* @__PURE__ */ new Set(["codex", "cursor", "kimi", ...parsed.acp?.backends?.map((item) => item.id) ?? []])].map((
|
|
17487
|
-
const d = parsed.acp?.backends?.find((x) => x.id ===
|
|
17488
|
-
const legacy = parsed.acp?.backend ===
|
|
17489
|
-
return { id:
|
|
18175
|
+
acp: { enabled: parsed.acp?.enabled ?? true, backends: [.../* @__PURE__ */ new Set(["codex", "cursor", "kimi", ...parsed.acp?.backends?.map((item) => item.id) ?? []])].map((id4) => {
|
|
18176
|
+
const d = parsed.acp?.backends?.find((x) => x.id === id4);
|
|
18177
|
+
const legacy = parsed.acp?.backend === id4 ? parsed.acp : void 0;
|
|
18178
|
+
return { id: id4, enabled: d?.enabled ?? legacy?.enabled ?? true, command: d?.command ?? legacy?.command ?? { codex: "codex", cursor: "agent", kimi: "kimi" }[id4] ?? id4, args: d?.args ?? legacy?.args ?? ["acp"], ...d?.cwd ?? legacy?.cwd ? { cwd: d?.cwd ?? legacy?.cwd } : {} };
|
|
17490
18179
|
}) }
|
|
17491
18180
|
};
|
|
17492
18181
|
}
|
|
@@ -17509,7 +18198,7 @@ function normalizeServerUrl(value) {
|
|
|
17509
18198
|
}
|
|
17510
18199
|
|
|
17511
18200
|
// src/version.ts
|
|
17512
|
-
var PLUGIN_VERSION = "0.4.
|
|
18201
|
+
var PLUGIN_VERSION = "0.4.15";
|
|
17513
18202
|
|
|
17514
18203
|
// src/server-api.ts
|
|
17515
18204
|
var TERMINAL_CONTROL_CHARACTERS = /[\u0000-\u001f\u007f-\u009f]/u;
|
|
@@ -17668,22 +18357,48 @@ var HostServerApi = class {
|
|
|
17668
18357
|
this.credentials = await this.credentialsPromise;
|
|
17669
18358
|
return this.credentials;
|
|
17670
18359
|
}
|
|
17671
|
-
async refreshCredentials() {
|
|
18360
|
+
async refreshCredentials(rejectedAccessToken = this.credentials?.accessToken) {
|
|
17672
18361
|
const identity = this.requireIdentity();
|
|
17673
|
-
|
|
17674
|
-
|
|
17675
|
-
|
|
17676
|
-
|
|
17677
|
-
|
|
18362
|
+
this.credentials = await this.withRefreshLock(async () => {
|
|
18363
|
+
const stored = await this.store.load(this.baseUrl, identity.deviceId);
|
|
18364
|
+
if (stored === void 0 || stored.refreshTokenExpiresAt <= Date.now()) return this.register(identity);
|
|
18365
|
+
if (stored.accessToken !== rejectedAccessToken && stored.accessTokenExpiresAt > Date.now() + 3e4) {
|
|
18366
|
+
return stored;
|
|
18367
|
+
}
|
|
18368
|
+
return this.rotateCredentials(identity, stored);
|
|
17678
18369
|
});
|
|
17679
|
-
this.credentials
|
|
18370
|
+
return this.credentials;
|
|
18371
|
+
}
|
|
18372
|
+
async withRefreshLock(operation) {
|
|
18373
|
+
try {
|
|
18374
|
+
return await this.store.withRefreshLock(operation);
|
|
18375
|
+
} catch (error) {
|
|
18376
|
+
if (error instanceof ServerCredentialsBusyError) {
|
|
18377
|
+
throw new ServerApiError(error.code, error.message, false);
|
|
18378
|
+
}
|
|
18379
|
+
throw error;
|
|
18380
|
+
}
|
|
18381
|
+
}
|
|
18382
|
+
async rotateCredentials(identity, stored) {
|
|
18383
|
+
let tokens;
|
|
18384
|
+
try {
|
|
18385
|
+
tokens = await this.publicRequest("/api/v1/auth/refresh", {
|
|
18386
|
+
method: "POST",
|
|
18387
|
+
body: JSON.stringify({ deviceId: identity.deviceId, refreshToken: stored.refreshToken })
|
|
18388
|
+
});
|
|
18389
|
+
} catch (error) {
|
|
18390
|
+
if (error instanceof ServerApiError) {
|
|
18391
|
+
throw new ServerApiError(error.code, error.message, error.retryable, error.status, "credential_refresh");
|
|
18392
|
+
}
|
|
18393
|
+
throw error;
|
|
18394
|
+
}
|
|
18395
|
+
return this.store.save({
|
|
17680
18396
|
serverUrl: this.baseUrl,
|
|
17681
18397
|
deviceId: identity.deviceId,
|
|
17682
18398
|
authorizationMethod: stored.authorizationMethod,
|
|
17683
18399
|
...stored.account === void 0 ? {} : { account: stored.account },
|
|
17684
18400
|
...validateTokens(tokens)
|
|
17685
18401
|
});
|
|
17686
|
-
return this.credentials;
|
|
17687
18402
|
}
|
|
17688
18403
|
async listDevices() {
|
|
17689
18404
|
const result = await this.request("/api/v1/devices");
|
|
@@ -17709,21 +18424,13 @@ var HostServerApi = class {
|
|
|
17709
18424
|
return { online: result.online, ...typeof result.lastSeenAt === "number" ? { lastSeenAt: result.lastSeenAt } : {} };
|
|
17710
18425
|
}
|
|
17711
18426
|
async loadOrIssue(identity) {
|
|
17712
|
-
|
|
17713
|
-
|
|
17714
|
-
|
|
17715
|
-
|
|
17716
|
-
|
|
17717
|
-
|
|
17718
|
-
|
|
17719
|
-
body: JSON.stringify({ deviceId: identity.deviceId, refreshToken: stored.refreshToken })
|
|
17720
|
-
});
|
|
17721
|
-
return this.store.save({
|
|
17722
|
-
serverUrl: this.baseUrl,
|
|
17723
|
-
deviceId: identity.deviceId,
|
|
17724
|
-
authorizationMethod: stored.authorizationMethod,
|
|
17725
|
-
...stored.account === void 0 ? {} : { account: stored.account },
|
|
17726
|
-
...validateTokens(tokens)
|
|
18427
|
+
return this.withRefreshLock(async () => {
|
|
18428
|
+
const stored = await this.store.load(this.baseUrl, identity.deviceId);
|
|
18429
|
+
if (stored === void 0 || stored.refreshTokenExpiresAt <= Date.now() + 3e4) {
|
|
18430
|
+
return this.register(identity);
|
|
18431
|
+
}
|
|
18432
|
+
if (stored.accessTokenExpiresAt > Date.now() + 3e4) return stored;
|
|
18433
|
+
return this.rotateCredentials(identity, stored);
|
|
17727
18434
|
});
|
|
17728
18435
|
}
|
|
17729
18436
|
async register(identity, authorization) {
|
|
@@ -17816,11 +18523,12 @@ var ClientServerApi = class extends HostServerApi {
|
|
|
17816
18523
|
}
|
|
17817
18524
|
};
|
|
17818
18525
|
var ServerApiError = class extends Error {
|
|
17819
|
-
constructor(code, message, retryable, status2) {
|
|
18526
|
+
constructor(code, message, retryable, status2, phase) {
|
|
17820
18527
|
super(message);
|
|
17821
18528
|
this.code = code;
|
|
17822
18529
|
this.retryable = retryable;
|
|
17823
18530
|
this.status = status2;
|
|
18531
|
+
this.phase = phase;
|
|
17824
18532
|
}
|
|
17825
18533
|
};
|
|
17826
18534
|
function validateTokens(value) {
|
|
@@ -17991,12 +18699,12 @@ var TypertGatewaySwitch = class {
|
|
|
17991
18699
|
}
|
|
17992
18700
|
this.installed = true;
|
|
17993
18701
|
}
|
|
17994
|
-
selectRemote(remote, support = ALL_REMOTE_COMMANDS,
|
|
18702
|
+
selectRemote(remote, support = ALL_REMOTE_COMMANDS, target2) {
|
|
17995
18703
|
if (!this.installed) throw new Error("The Typert gateway switch is not installed.");
|
|
17996
18704
|
this.remoteInvoke = typeof remote === "function" ? remote : (request) => remote.invoke(request);
|
|
17997
18705
|
this.remoteTarget = typeof remote === "function" ? void 0 : remote;
|
|
17998
18706
|
this.remoteSupport = { ...support };
|
|
17999
|
-
this.target =
|
|
18707
|
+
this.target = target2 === void 0 ? void 0 : { ...target2 };
|
|
18000
18708
|
}
|
|
18001
18709
|
selectLocal() {
|
|
18002
18710
|
this.remoteInvoke = void 0;
|
|
@@ -18063,7 +18771,7 @@ import { networkInterfaces } from "node:os";
|
|
|
18063
18771
|
// src/native-rtc-helper.ts
|
|
18064
18772
|
import { spawn, spawnSync } from "node:child_process";
|
|
18065
18773
|
import { existsSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
18066
|
-
import { delimiter, dirname as
|
|
18774
|
+
import { delimiter, dirname as dirname3, join as join3 } from "node:path";
|
|
18067
18775
|
import { fileURLToPath } from "node:url";
|
|
18068
18776
|
var cachedExternalFactory;
|
|
18069
18777
|
var cachedExternalFactoryResolved = false;
|
|
@@ -18114,21 +18822,21 @@ function nodeBinaryCandidates() {
|
|
|
18114
18822
|
add3(process.env.DSH_REMOTE_NODE);
|
|
18115
18823
|
add3(process.env.NODE);
|
|
18116
18824
|
add3(process.execPath);
|
|
18117
|
-
for (const part of (process.env.PATH ?? "").split(delimiter)) add3(
|
|
18825
|
+
for (const part of (process.env.PATH ?? "").split(delimiter)) add3(join3(part, process.platform === "win32" ? "node.exe" : "node"));
|
|
18118
18826
|
add3("/opt/homebrew/bin/node");
|
|
18119
18827
|
add3("/usr/local/bin/node");
|
|
18120
18828
|
add3("/usr/bin/node");
|
|
18121
|
-
add3(
|
|
18122
|
-
add3(
|
|
18123
|
-
add3(
|
|
18829
|
+
add3(join3(process.env.HOME ?? "", ".volta", "bin", process.platform === "win32" ? "node.exe" : "node"));
|
|
18830
|
+
add3(join3(process.env.HOME ?? "", ".asdf", "shims", process.platform === "win32" ? "node.exe" : "node"));
|
|
18831
|
+
add3(join3(process.env.HOME ?? "", ".local", "bin", process.platform === "win32" ? "node.exe" : "node"));
|
|
18124
18832
|
for (const nvmNode of nvmNodeCandidates()) add3(nvmNode);
|
|
18125
18833
|
return candidates;
|
|
18126
18834
|
}
|
|
18127
18835
|
function nvmNodeCandidates() {
|
|
18128
|
-
const root =
|
|
18836
|
+
const root = join3(process.env.HOME ?? "", ".nvm", "versions", "node");
|
|
18129
18837
|
if (root === "" || !existsSync(root)) return [];
|
|
18130
18838
|
try {
|
|
18131
|
-
return readdirSync(root).map((version) =>
|
|
18839
|
+
return readdirSync(root).map((version) => join3(root, version, "bin", process.platform === "win32" ? "node.exe" : "node")).sort((left, right) => right.localeCompare(left, "en", { numeric: true }));
|
|
18132
18840
|
} catch {
|
|
18133
18841
|
return [];
|
|
18134
18842
|
}
|
|
@@ -18150,7 +18858,7 @@ function isUsableExternalNode(candidate, requireFrom) {
|
|
|
18150
18858
|
"console.log(`node:${process.versions.node}`);"
|
|
18151
18859
|
].join("")
|
|
18152
18860
|
], {
|
|
18153
|
-
cwd:
|
|
18861
|
+
cwd: dirname3(requireFrom),
|
|
18154
18862
|
env,
|
|
18155
18863
|
encoding: "utf8",
|
|
18156
18864
|
timeout: 3e3
|
|
@@ -18172,7 +18880,7 @@ var ExternalNativePeerConnection = class {
|
|
|
18172
18880
|
this.nodeBinary = nodeBinary;
|
|
18173
18881
|
this.requireFrom = requireFrom;
|
|
18174
18882
|
this.child = spawn(this.nodeBinary, ["--input-type=module", "--eval", HELPER_SOURCE], {
|
|
18175
|
-
cwd:
|
|
18883
|
+
cwd: dirname3(this.requireFrom),
|
|
18176
18884
|
env: { ...process.env, DSH_REMOTE_RTC_HELPER_REQUIRE_FROM: this.requireFrom },
|
|
18177
18885
|
stdio: ["pipe", "pipe", "pipe"]
|
|
18178
18886
|
});
|
|
@@ -18241,8 +18949,8 @@ var ExternalNativePeerConnection = class {
|
|
|
18241
18949
|
}, 1e3);
|
|
18242
18950
|
timer.unref?.();
|
|
18243
18951
|
}
|
|
18244
|
-
sendChannelData(channelId,
|
|
18245
|
-
const payload = typeof
|
|
18952
|
+
sendChannelData(channelId, data2) {
|
|
18953
|
+
const payload = typeof data2 === "string" ? { channelId, text: data2 } : { channelId, base64: Buffer.from(data2).toString("base64") };
|
|
18246
18954
|
this.notify("channelSend", payload);
|
|
18247
18955
|
}
|
|
18248
18956
|
closeChannel(channelId) {
|
|
@@ -18250,11 +18958,11 @@ var ExternalNativePeerConnection = class {
|
|
|
18250
18958
|
}
|
|
18251
18959
|
request(method, payload) {
|
|
18252
18960
|
if (this.closed) return Promise.reject(new Error("native rtc helper is closed"));
|
|
18253
|
-
const
|
|
18961
|
+
const id4 = this.nextRequestId++;
|
|
18254
18962
|
const promise = new Promise((resolve3, reject) => {
|
|
18255
|
-
this.pending.set(
|
|
18963
|
+
this.pending.set(id4, { resolve: resolve3, reject });
|
|
18256
18964
|
});
|
|
18257
|
-
this.write({ id:
|
|
18965
|
+
this.write({ id: id4, method, payload });
|
|
18258
18966
|
return promise;
|
|
18259
18967
|
}
|
|
18260
18968
|
notify(method, payload) {
|
|
@@ -18358,8 +19066,8 @@ var ExternalNativeDataChannel = class {
|
|
|
18358
19066
|
onclose = null;
|
|
18359
19067
|
onerror = null;
|
|
18360
19068
|
onbufferedamountlow = null;
|
|
18361
|
-
send(
|
|
18362
|
-
this.owner.sendChannelData(this.channelId,
|
|
19069
|
+
send(data2) {
|
|
19070
|
+
this.owner.sendChannelData(this.channelId, data2);
|
|
18363
19071
|
}
|
|
18364
19072
|
close() {
|
|
18365
19073
|
this.owner.closeChannel(this.channelId);
|
|
@@ -18372,8 +19080,8 @@ var ExternalNativeDataChannel = class {
|
|
|
18372
19080
|
if (previous !== "closed" && readyState === "closed") this.onclose?.();
|
|
18373
19081
|
if (bufferedAmount === 0) this.onbufferedamountlow?.();
|
|
18374
19082
|
}
|
|
18375
|
-
emitMessage(
|
|
18376
|
-
this.onmessage?.({ data });
|
|
19083
|
+
emitMessage(data2) {
|
|
19084
|
+
this.onmessage?.({ data: data2 });
|
|
18377
19085
|
}
|
|
18378
19086
|
emitError() {
|
|
18379
19087
|
this.onerror?.();
|
|
@@ -18850,10 +19558,10 @@ function adaptDataChannel2(raw) {
|
|
|
18850
19558
|
return raw.onerror;
|
|
18851
19559
|
},
|
|
18852
19560
|
onbufferedamountlow: null,
|
|
18853
|
-
send(
|
|
18854
|
-
const bytes = typeof
|
|
19561
|
+
send(data2) {
|
|
19562
|
+
const bytes = typeof data2 === "string" ? Buffer.byteLength(data2) : data2.byteLength;
|
|
18855
19563
|
try {
|
|
18856
|
-
raw.send(typeof
|
|
19564
|
+
raw.send(typeof data2 === "string" ? data2 : Buffer.from(data2));
|
|
18857
19565
|
} catch (error) {
|
|
18858
19566
|
console.error("[werift-send-error] bytes=" + bytes, error instanceof Error ? error.message : error);
|
|
18859
19567
|
throw error;
|
|
@@ -18916,17 +19624,17 @@ function adaptNativeDataChannel(raw) {
|
|
|
18916
19624
|
get onbufferedamountlow() {
|
|
18917
19625
|
return raw.onbufferedamountlow;
|
|
18918
19626
|
},
|
|
18919
|
-
send(
|
|
18920
|
-
raw.send(typeof
|
|
19627
|
+
send(data2) {
|
|
19628
|
+
raw.send(typeof data2 === "string" ? data2 : Buffer.from(data2));
|
|
18921
19629
|
},
|
|
18922
19630
|
close() {
|
|
18923
19631
|
raw.close();
|
|
18924
19632
|
}
|
|
18925
19633
|
};
|
|
18926
19634
|
}
|
|
18927
|
-
function toArrayBuffer2(
|
|
18928
|
-
if (typeof
|
|
18929
|
-
return
|
|
19635
|
+
function toArrayBuffer2(data2) {
|
|
19636
|
+
if (typeof data2 === "string") return data2;
|
|
19637
|
+
return data2.buffer.slice(data2.byteOffset, data2.byteOffset + data2.byteLength);
|
|
18930
19638
|
}
|
|
18931
19639
|
function normalizeNativeCandidate(candidate) {
|
|
18932
19640
|
const json = typeof candidate.toJSON === "function" ? candidate.toJSON() : {
|
|
@@ -18940,11 +19648,11 @@ function normalizeNativeCandidate(candidate) {
|
|
|
18940
19648
|
sdpMLineIndex: normalizeSdpMLineIndex(json.sdpMLineIndex)
|
|
18941
19649
|
};
|
|
18942
19650
|
}
|
|
18943
|
-
function normalizeNativeMessageData(
|
|
18944
|
-
if (typeof
|
|
18945
|
-
if (
|
|
18946
|
-
if (ArrayBuffer.isView(
|
|
18947
|
-
return
|
|
19651
|
+
function normalizeNativeMessageData(data2) {
|
|
19652
|
+
if (typeof data2 === "string") return data2;
|
|
19653
|
+
if (data2 instanceof ArrayBuffer) return data2;
|
|
19654
|
+
if (ArrayBuffer.isView(data2)) {
|
|
19655
|
+
return data2.buffer.slice(data2.byteOffset, data2.byteOffset + data2.byteLength);
|
|
18948
19656
|
}
|
|
18949
19657
|
return new Uint8Array().buffer;
|
|
18950
19658
|
}
|
|
@@ -18974,8 +19682,8 @@ function detectHostIpv4Candidates(interfaces, preferredHostIpv4Candidates = [])
|
|
|
18974
19682
|
return [...new Set(candidates.map((candidate) => candidate.ip))];
|
|
18975
19683
|
}
|
|
18976
19684
|
async function detectRouteHostIpv4Candidates(targets, timeoutMs = 500) {
|
|
18977
|
-
const detected = await Promise.all(targets.map(async (
|
|
18978
|
-
const routeTarget = parseRouteTarget(
|
|
19685
|
+
const detected = await Promise.all(targets.map(async (target2) => {
|
|
19686
|
+
const routeTarget = parseRouteTarget(target2);
|
|
18979
19687
|
return routeTarget === void 0 ? void 0 : await detectRouteHostIpv4(routeTarget, timeoutMs).catch(() => void 0);
|
|
18980
19688
|
}));
|
|
18981
19689
|
return [...new Set(detected.filter((ip) => ip !== void 0 && isUsableIpv4(ip)))];
|
|
@@ -19043,7 +19751,7 @@ function parseRouteTarget(value) {
|
|
|
19043
19751
|
return void 0;
|
|
19044
19752
|
}
|
|
19045
19753
|
}
|
|
19046
|
-
async function detectRouteHostIpv4(
|
|
19754
|
+
async function detectRouteHostIpv4(target2, timeoutMs) {
|
|
19047
19755
|
const socket = createSocket("udp4");
|
|
19048
19756
|
try {
|
|
19049
19757
|
return await new Promise((resolve3, reject) => {
|
|
@@ -19060,7 +19768,7 @@ async function detectRouteHostIpv4(target, timeoutMs) {
|
|
|
19060
19768
|
reject(error);
|
|
19061
19769
|
};
|
|
19062
19770
|
socket.once("error", onError);
|
|
19063
|
-
socket.connect(
|
|
19771
|
+
socket.connect(target2.port, target2.host, () => {
|
|
19064
19772
|
cleanup();
|
|
19065
19773
|
const address = socket.address();
|
|
19066
19774
|
resolve3(typeof address === "string" ? void 0 : address.address);
|
|
@@ -19135,6 +19843,7 @@ var ClientModeRuntime = class {
|
|
|
19135
19843
|
this.proxySwitch = apiProxy === void 0 ? void 0 : new ApiProxySwitch(apiProxy);
|
|
19136
19844
|
this.gatewaySwitch = new TypertGatewaySwitch(typertGateway);
|
|
19137
19845
|
}
|
|
19846
|
+
preview;
|
|
19138
19847
|
identity;
|
|
19139
19848
|
connected;
|
|
19140
19849
|
pendingWorkspaceSelection;
|
|
@@ -19186,6 +19895,11 @@ var ClientModeRuntime = class {
|
|
|
19186
19895
|
...this.host === void 0 ? {} : { host: this.host.hostStatus() }
|
|
19187
19896
|
};
|
|
19188
19897
|
}
|
|
19898
|
+
async closePreview() {
|
|
19899
|
+
const preview = this.preview;
|
|
19900
|
+
this.preview = void 0;
|
|
19901
|
+
await preview?.close();
|
|
19902
|
+
}
|
|
19189
19903
|
async detailedStatus() {
|
|
19190
19904
|
const connected = this.connected;
|
|
19191
19905
|
if (connected === void 0 || this.identity === void 0) return this.status();
|
|
@@ -19251,6 +19965,7 @@ var ClientModeRuntime = class {
|
|
|
19251
19965
|
this.pendingWorkspaceSelection = void 0;
|
|
19252
19966
|
await this.closeCodexVirtual();
|
|
19253
19967
|
this.proxySwitch?.selectLocal();
|
|
19968
|
+
await this.closePreview();
|
|
19254
19969
|
this.gatewaySwitch.selectLocal();
|
|
19255
19970
|
await this.closeCodexStreams(previous?.client);
|
|
19256
19971
|
await previous?.client.close().catch(() => void 0);
|
|
@@ -19272,6 +19987,7 @@ var ClientModeRuntime = class {
|
|
|
19272
19987
|
if (mode === "local") {
|
|
19273
19988
|
await this.closeCodexVirtual();
|
|
19274
19989
|
this.proxySwitch?.selectLocal();
|
|
19990
|
+
await this.closePreview();
|
|
19275
19991
|
this.gatewaySwitch.selectLocal();
|
|
19276
19992
|
const previous2 = this.connected;
|
|
19277
19993
|
this.connected = void 0;
|
|
@@ -19294,6 +20010,7 @@ var ClientModeRuntime = class {
|
|
|
19294
20010
|
throw error;
|
|
19295
20011
|
}
|
|
19296
20012
|
const previous = this.connected;
|
|
20013
|
+
await this.closePreview();
|
|
19297
20014
|
this.connected = next;
|
|
19298
20015
|
this.clearConnectionProgress(next.progressRunId);
|
|
19299
20016
|
this.pendingWorkspaceSelection = void 0;
|
|
@@ -19429,9 +20146,11 @@ var ClientModeRuntime = class {
|
|
|
19429
20146
|
return this.status();
|
|
19430
20147
|
}
|
|
19431
20148
|
async close() {
|
|
20149
|
+
await this.closePreview();
|
|
19432
20150
|
if (this.closed) return;
|
|
19433
20151
|
this.closed = true;
|
|
19434
20152
|
this.proxySwitch?.selectLocal();
|
|
20153
|
+
await this.closePreview();
|
|
19435
20154
|
this.gatewaySwitch.selectLocal();
|
|
19436
20155
|
this.pendingWorkspaceSelection = void 0;
|
|
19437
20156
|
await this.closeCodexVirtual();
|
|
@@ -19514,24 +20233,24 @@ var ClientModeRuntime = class {
|
|
|
19514
20233
|
this.codexStreams.set(value.streamId, stream);
|
|
19515
20234
|
return { opened: true, streamId: value.streamId, threadId: value.threadId };
|
|
19516
20235
|
}
|
|
19517
|
-
publishLocalCodexFrame = async (event,
|
|
19518
|
-
const streamId =
|
|
20236
|
+
publishLocalCodexFrame = async (event, data2) => {
|
|
20237
|
+
const streamId = data2.streamId;
|
|
19519
20238
|
const stream = this.codexStreams.get(streamId);
|
|
19520
20239
|
if (stream === void 0 || stream.target.kind !== "local") return;
|
|
19521
20240
|
if (event === "codex.app.frame") {
|
|
19522
|
-
this.appendCodexFrame(stream,
|
|
20241
|
+
this.appendCodexFrame(stream, data2);
|
|
19523
20242
|
return;
|
|
19524
20243
|
}
|
|
19525
|
-
const closed =
|
|
20244
|
+
const closed = data2;
|
|
19526
20245
|
stream.closed = typeof closed.reason === "string" ? closed.reason : "closed";
|
|
19527
20246
|
stream.wake();
|
|
19528
20247
|
};
|
|
19529
|
-
appendCodexFrame(stream,
|
|
19530
|
-
if (!isRecord7(
|
|
20248
|
+
appendCodexFrame(stream, data2) {
|
|
20249
|
+
if (!isRecord7(data2) || !isRecord7(data2.frame) || typeof data2.frame.method !== "string") return;
|
|
19531
20250
|
if (stream.frames.length >= 256) {
|
|
19532
20251
|
stream.closed = "overflow";
|
|
19533
20252
|
} else {
|
|
19534
|
-
stream.frames.push({ method:
|
|
20253
|
+
stream.frames.push({ method: data2.frame.method, params: data2.frame.params });
|
|
19535
20254
|
}
|
|
19536
20255
|
stream.wake();
|
|
19537
20256
|
}
|
|
@@ -19585,16 +20304,16 @@ var ClientModeRuntime = class {
|
|
|
19585
20304
|
}));
|
|
19586
20305
|
}
|
|
19587
20306
|
selectRemoteTarget(remote, transport = this.selectHarnessRemoteTransport(remote)) {
|
|
19588
|
-
const
|
|
20307
|
+
const target2 = { deviceId: remote.target.deviceId, name: remote.target.name };
|
|
19589
20308
|
if (transport === "remoteGateway") {
|
|
19590
|
-
this.gatewaySwitch.selectRemote(this.remoteTypertGateway(remote), void 0,
|
|
20309
|
+
this.gatewaySwitch.selectRemote(this.remoteTypertGateway(remote), void 0, target2);
|
|
19591
20310
|
return;
|
|
19592
20311
|
}
|
|
19593
|
-
this.proxySwitch?.selectRemote(new RemoteHarnessApiProxy(remote.client).api,
|
|
20312
|
+
this.proxySwitch?.selectRemote(new RemoteHarnessApiProxy(remote.client).api, target2);
|
|
19594
20313
|
this.gatewaySwitch.selectRemote((request) => invokeRemoteCommand(remote.client, request), {
|
|
19595
20314
|
execute: true,
|
|
19596
20315
|
list: remote.features.commandList
|
|
19597
|
-
},
|
|
20316
|
+
}, target2);
|
|
19598
20317
|
}
|
|
19599
20318
|
remoteTypertGateway(remote) {
|
|
19600
20319
|
const localSessionGeneration = harnessSessionGeneration(this.host?.localHarnessVersion?.());
|
|
@@ -19604,13 +20323,13 @@ var ClientModeRuntime = class {
|
|
|
19604
20323
|
);
|
|
19605
20324
|
}
|
|
19606
20325
|
selectCodexTarget(virtual, remote) {
|
|
19607
|
-
const
|
|
20326
|
+
const target2 = { deviceId: remote.target.deviceId, name: remote.target.name };
|
|
19608
20327
|
if (this.gatewaySwitch.supportsCarrier()) {
|
|
19609
|
-
this.gatewaySwitch.selectRemote(virtual, void 0,
|
|
20328
|
+
this.gatewaySwitch.selectRemote(virtual, void 0, target2);
|
|
19610
20329
|
return;
|
|
19611
20330
|
}
|
|
19612
|
-
this.proxySwitch.selectRemote(virtual.api,
|
|
19613
|
-
this.gatewaySwitch.selectRemote((request) => virtual.invoke(request), { execute: true, list: true },
|
|
20331
|
+
this.proxySwitch.selectRemote(virtual.api, target2);
|
|
20332
|
+
this.gatewaySwitch.selectRemote((request) => virtual.invoke(request), { execute: true, list: true }, target2);
|
|
19614
20333
|
}
|
|
19615
20334
|
async closeCodexVirtual() {
|
|
19616
20335
|
const virtual = this.codexVirtual;
|
|
@@ -19656,14 +20375,14 @@ var ClientModeRuntime = class {
|
|
|
19656
20375
|
throw new ClientModeError("MEMBERSHIP_REQUIRED", "The selected Host is not authorized for this account.");
|
|
19657
20376
|
}
|
|
19658
20377
|
this.updateConnectionProgress(progressRunId, "authorizing-peer");
|
|
19659
|
-
const
|
|
20378
|
+
const target2 = await this.authorizeHostPeer(serverDevice);
|
|
19660
20379
|
const presence = await this.server.presenceFor(targetDeviceId);
|
|
19661
20380
|
if (!presence.online) throw new ClientModeError("HOST_OFFLINE", "The selected Host is offline.", true);
|
|
19662
20381
|
const credentials = await this.server.authenticate(identity);
|
|
19663
20382
|
const rtcFactory = this.config.forceRelay ? void 0 : await this.rtcFactoryProvider({ routeTargets: [this.server.baseUrl] }).catch(() => void 0);
|
|
19664
20383
|
if (!this.config.forceRelay && rtcFactory === void 0) {
|
|
19665
20384
|
this.logger.warn("remote Harness WebRTC backend unavailable; using relay", {
|
|
19666
|
-
targetDeviceId: shortId(
|
|
20385
|
+
targetDeviceId: shortId(target2.deviceId)
|
|
19667
20386
|
});
|
|
19668
20387
|
}
|
|
19669
20388
|
let webRtcFallback = false;
|
|
@@ -19682,13 +20401,13 @@ var ClientModeRuntime = class {
|
|
|
19682
20401
|
onWebRtcFallback: (error, diagnostics) => {
|
|
19683
20402
|
webRtcFallback = true;
|
|
19684
20403
|
this.logger.warn(attempt === "direct" ? "remote Harness direct WebRTC failed; trying TURN" : "remote Harness TURN WebRTC failed; using relay", {
|
|
19685
|
-
targetDeviceId: shortId(
|
|
20404
|
+
targetDeviceId: shortId(target2.deviceId),
|
|
19686
20405
|
attempt,
|
|
19687
20406
|
reason: diagnosticReason(error)
|
|
19688
20407
|
});
|
|
19689
20408
|
if (diagnostics !== void 0) {
|
|
19690
20409
|
this.logger.debug("remote Harness WebRTC fallback diagnostics", {
|
|
19691
|
-
targetDeviceId: shortId(
|
|
20410
|
+
targetDeviceId: shortId(target2.deviceId),
|
|
19692
20411
|
attempt,
|
|
19693
20412
|
...webrtcDiagnosticsLogFields(diagnostics)
|
|
19694
20413
|
});
|
|
@@ -19703,7 +20422,7 @@ var ClientModeRuntime = class {
|
|
|
19703
20422
|
const stopProgressTimer = this.beginAttemptProgress(progressRunId, attempt);
|
|
19704
20423
|
try {
|
|
19705
20424
|
transport = createTransport(attempt);
|
|
19706
|
-
client = new RemoteClientCore(new ClientSecureTransport(transport, identity,
|
|
20425
|
+
client = new RemoteClientCore(new ClientSecureTransport(transport, identity, target2), 6e4);
|
|
19707
20426
|
await client.connect();
|
|
19708
20427
|
signal?.throwIfAborted();
|
|
19709
20428
|
} finally {
|
|
@@ -19715,7 +20434,7 @@ var ClientModeRuntime = class {
|
|
|
19715
20434
|
transport = void 0;
|
|
19716
20435
|
if (attempt === "turn") {
|
|
19717
20436
|
this.logger.info("remote Harness relay fallback re-established", {
|
|
19718
|
-
targetDeviceId: shortId(
|
|
20437
|
+
targetDeviceId: shortId(target2.deviceId)
|
|
19719
20438
|
});
|
|
19720
20439
|
}
|
|
19721
20440
|
}
|
|
@@ -19732,6 +20451,7 @@ var ClientModeRuntime = class {
|
|
|
19732
20451
|
);
|
|
19733
20452
|
connectedClient.onClose(() => {
|
|
19734
20453
|
if (this.connected?.client !== connectedClient) return;
|
|
20454
|
+
void this.closePreview();
|
|
19735
20455
|
this.connected = void 0;
|
|
19736
20456
|
this.connectionProgress = void 0;
|
|
19737
20457
|
this.pendingWorkspaceSelection = void 0;
|
|
@@ -19740,12 +20460,12 @@ var ClientModeRuntime = class {
|
|
|
19740
20460
|
this.gatewaySwitch.selectLocal();
|
|
19741
20461
|
void connectedClient.close().catch(() => void 0);
|
|
19742
20462
|
this.logger.warn("remote Harness transport closed; falling back to local mode", {
|
|
19743
|
-
targetDeviceId: shortId(
|
|
20463
|
+
targetDeviceId: shortId(target2.deviceId)
|
|
19744
20464
|
});
|
|
19745
20465
|
});
|
|
19746
20466
|
const connectionDetails = await connectedTransport.connectionDetails().catch(() => void 0);
|
|
19747
20467
|
this.logger.info("remote Harness transport ready", {
|
|
19748
|
-
targetDeviceId: shortId(
|
|
20468
|
+
targetDeviceId: shortId(target2.deviceId),
|
|
19749
20469
|
transport: connectedClient.getStats().mode,
|
|
19750
20470
|
...connectionDetails === void 0 ? {} : {
|
|
19751
20471
|
preferredTransports: connectionDetails.preferredTransports,
|
|
@@ -19755,14 +20475,14 @@ var ClientModeRuntime = class {
|
|
|
19755
20475
|
});
|
|
19756
20476
|
if (connectionDetails?.webRtc?.diagnostics !== void 0) {
|
|
19757
20477
|
this.logger.debug("remote Harness transport diagnostics", {
|
|
19758
|
-
targetDeviceId: shortId(
|
|
20478
|
+
targetDeviceId: shortId(target2.deviceId),
|
|
19759
20479
|
...webrtcDiagnosticsLogFields(connectionDetails.webRtc.diagnostics)
|
|
19760
20480
|
});
|
|
19761
20481
|
}
|
|
19762
20482
|
const features = await probeRemoteHostFeatures(connectedClient, serverDevice.clientVersion);
|
|
19763
20483
|
return {
|
|
19764
20484
|
client: connectedClient,
|
|
19765
|
-
target,
|
|
20485
|
+
target: target2,
|
|
19766
20486
|
transport: connectedTransport,
|
|
19767
20487
|
features,
|
|
19768
20488
|
progressRunId,
|
|
@@ -19779,6 +20499,7 @@ var ClientModeRuntime = class {
|
|
|
19779
20499
|
if (this.connected?.target.deviceId === targetDeviceId) return this.connected;
|
|
19780
20500
|
const next = await this.connect(targetDeviceId, signal);
|
|
19781
20501
|
const previous = this.connected;
|
|
20502
|
+
await this.closePreview();
|
|
19782
20503
|
this.connected = next;
|
|
19783
20504
|
this.clearConnectionProgress(next.progressRunId);
|
|
19784
20505
|
await previous?.client.close().catch(() => void 0);
|
|
@@ -19835,6 +20556,12 @@ var ClientModeRuntime = class {
|
|
|
19835
20556
|
}
|
|
19836
20557
|
return ok2(await this.pollClientOAuthQrLogin(value.qrId));
|
|
19837
20558
|
}
|
|
20559
|
+
if (endpoint === "preview.open") {
|
|
20560
|
+
const remote = this.activeRemote();
|
|
20561
|
+
if (remote === void 0) throw new ClientModeError("TRANSPORT_CLOSED", "Connect to a Remote Host first.");
|
|
20562
|
+
this.preview ??= new LoopbackPreview(remote.client);
|
|
20563
|
+
return ok2(await this.preview.open(record3(payload).port));
|
|
20564
|
+
}
|
|
19838
20565
|
if (endpoint === "directory.list") {
|
|
19839
20566
|
const value = record3(payload);
|
|
19840
20567
|
if (typeof value.targetDeviceId !== "string") throw new ClientModeError("INVALID_MESSAGE", "A Host is required.");
|
|
@@ -20099,6 +20826,7 @@ function remoteHostFeatures(clientVersion) {
|
|
|
20099
20826
|
return {
|
|
20100
20827
|
commandList: isVersionAtLeast(clientVersion, REMOTE_COMMAND_LIST_MIN_VERSION),
|
|
20101
20828
|
fileViewer: isVersionAtLeast(clientVersion, REMOTE_FILE_VIEWER_MIN_VERSION),
|
|
20829
|
+
terminal: false,
|
|
20102
20830
|
apiProxy: true,
|
|
20103
20831
|
remoteGateway: false,
|
|
20104
20832
|
codex: false
|
|
@@ -20120,6 +20848,7 @@ async function probeRemoteHostFeatures(client, clientVersion) {
|
|
|
20120
20848
|
const apiProxy = capabilities.has("harness.api.v1");
|
|
20121
20849
|
const remoteV1 = capabilities.has("harness.remote.v1");
|
|
20122
20850
|
const remoteV3 = capabilities.has("harness.remote.v3");
|
|
20851
|
+
const terminal = capabilities.has("harness.terminal.v1");
|
|
20123
20852
|
const codex = capabilities.has("codex.appserver.v1");
|
|
20124
20853
|
if (remoteV1 && remoteV3) {
|
|
20125
20854
|
throw new ClientModeError("INVALID_MESSAGE", "The remote Host advertised conflicting Harness Session formats.");
|
|
@@ -20132,6 +20861,7 @@ async function probeRemoteHostFeatures(client, clientVersion) {
|
|
|
20132
20861
|
return {
|
|
20133
20862
|
commandList: remoteGateway || apiProxy && fallback.commandList,
|
|
20134
20863
|
fileViewer: capabilities.has("fileviewer.read.v1"),
|
|
20864
|
+
terminal,
|
|
20135
20865
|
apiProxy,
|
|
20136
20866
|
remoteGateway,
|
|
20137
20867
|
...sessionFormat === void 0 ? {} : { sessionFormat },
|
|
@@ -20202,9 +20932,9 @@ import { execFileSync } from "node:child_process";
|
|
|
20202
20932
|
|
|
20203
20933
|
// src/identity-store.ts
|
|
20204
20934
|
import { createHash } from "node:crypto";
|
|
20205
|
-
import { chmod, mkdir, readFile as
|
|
20935
|
+
import { chmod as chmod2, mkdir as mkdir2, readFile as readFile3, rename as rename2, rm as rm2, stat as stat2, writeFile as writeFile2 } from "node:fs/promises";
|
|
20206
20936
|
import { homedir } from "node:os";
|
|
20207
|
-
import { dirname as
|
|
20937
|
+
import { dirname as dirname4, join as join4 } from "node:path";
|
|
20208
20938
|
var identitySchema = external_exports.object({
|
|
20209
20939
|
schemaVersion: external_exports.literal(1),
|
|
20210
20940
|
deviceId: external_exports.string().uuid(),
|
|
@@ -20233,15 +20963,15 @@ var IdentityStore = class {
|
|
|
20233
20963
|
peers = /* @__PURE__ */ new Map();
|
|
20234
20964
|
constructor(options = {}) {
|
|
20235
20965
|
const env = options.env ?? process.env;
|
|
20236
|
-
const dshHome = env.DSH_HOME ||
|
|
20237
|
-
this.directory = options.directory ??
|
|
20966
|
+
const dshHome = env.DSH_HOME || join4(options.homeDirectory ?? homedir(), ".dsh");
|
|
20967
|
+
this.directory = options.directory ?? join4(dshHome, "remote");
|
|
20238
20968
|
}
|
|
20239
20969
|
async loadOrCreate(deviceName) {
|
|
20240
|
-
await
|
|
20241
|
-
await
|
|
20242
|
-
const devicePath =
|
|
20243
|
-
const keyPath =
|
|
20244
|
-
const [hasDevice, hasKey] = await Promise.all([
|
|
20970
|
+
await mkdir2(this.directory, { recursive: true, mode: 448 });
|
|
20971
|
+
await chmod2(this.directory, 448);
|
|
20972
|
+
const devicePath = join4(this.directory, "device.json");
|
|
20973
|
+
const keyPath = join4(this.directory, "device.key");
|
|
20974
|
+
const [hasDevice, hasKey] = await Promise.all([exists2(devicePath), exists2(keyPath)]);
|
|
20245
20975
|
if (hasDevice !== hasKey) {
|
|
20246
20976
|
throw new IdentityInvalidError("device identity is incomplete; repair it explicitly before reconnecting");
|
|
20247
20977
|
}
|
|
@@ -20252,10 +20982,10 @@ var IdentityStore = class {
|
|
|
20252
20982
|
await atomicTextWrite(keyPath, `${keys.privateKey}
|
|
20253
20983
|
`, 384);
|
|
20254
20984
|
}
|
|
20255
|
-
await
|
|
20985
|
+
await assertPrivateMode2(keyPath);
|
|
20256
20986
|
try {
|
|
20257
|
-
let record6 = identitySchema.parse(JSON.parse(await
|
|
20258
|
-
const privateKey = (await
|
|
20987
|
+
let record6 = identitySchema.parse(JSON.parse(await readFile3(devicePath, "utf8")));
|
|
20988
|
+
const privateKey = (await readFile3(keyPath, "utf8")).trim();
|
|
20259
20989
|
const regenerated = generateKeyPair(fromBase64Url2(privateKey));
|
|
20260
20990
|
if (regenerated.publicKey !== record6.publicKey) {
|
|
20261
20991
|
throw new IdentityInvalidError("device public and private keys do not match");
|
|
@@ -20277,7 +21007,7 @@ var IdentityStore = class {
|
|
|
20277
21007
|
return this.identity;
|
|
20278
21008
|
}
|
|
20279
21009
|
async reset(deviceName) {
|
|
20280
|
-
await
|
|
21010
|
+
await rm2(this.directory, { recursive: true, force: true });
|
|
20281
21011
|
this.identity = void 0;
|
|
20282
21012
|
this.peers.clear();
|
|
20283
21013
|
return this.loadOrCreate(deviceName);
|
|
@@ -20309,11 +21039,11 @@ var IdentityStore = class {
|
|
|
20309
21039
|
return removed;
|
|
20310
21040
|
}
|
|
20311
21041
|
async loadPeers() {
|
|
20312
|
-
const path =
|
|
20313
|
-
if (!await
|
|
21042
|
+
const path = join4(this.directory, "trusted-peers.json");
|
|
21043
|
+
if (!await exists2(path)) {
|
|
20314
21044
|
await atomicJsonWrite(path, { schemaVersion: 1, peers: [] }, 384);
|
|
20315
21045
|
}
|
|
20316
|
-
const parsed = trustedPeersSchema.parse(JSON.parse(await
|
|
21046
|
+
const parsed = trustedPeersSchema.parse(JSON.parse(await readFile3(path, "utf8")));
|
|
20317
21047
|
const peers = /* @__PURE__ */ new Map();
|
|
20318
21048
|
for (const peer of parsed.peers) {
|
|
20319
21049
|
if (peer.fingerprint !== fingerprint(peer.publicKey)) {
|
|
@@ -20325,7 +21055,7 @@ var IdentityStore = class {
|
|
|
20325
21055
|
this.peers = peers;
|
|
20326
21056
|
}
|
|
20327
21057
|
async savePeers() {
|
|
20328
|
-
await atomicJsonWrite(
|
|
21058
|
+
await atomicJsonWrite(join4(this.directory, "trusted-peers.json"), {
|
|
20329
21059
|
schemaVersion: 1,
|
|
20330
21060
|
peers: [...this.peers.values()]
|
|
20331
21061
|
}, 384);
|
|
@@ -20334,15 +21064,15 @@ var IdentityStore = class {
|
|
|
20334
21064
|
function serverStorageDirectory(root, serverUrl, role) {
|
|
20335
21065
|
const origin = new URL(serverUrl).origin;
|
|
20336
21066
|
const scope = createHash("sha256").update(origin).digest("hex").slice(0, 24);
|
|
20337
|
-
return
|
|
21067
|
+
return join4(root, "servers", scope, role);
|
|
20338
21068
|
}
|
|
20339
21069
|
function fingerprint(publicKey) {
|
|
20340
21070
|
const compact = createHash("sha256").update(fromBase64Url2(publicKey)).digest("hex").slice(0, 12).toUpperCase();
|
|
20341
21071
|
return compact.match(/.{1,4}/g).join(" ");
|
|
20342
21072
|
}
|
|
20343
|
-
async function
|
|
21073
|
+
async function assertPrivateMode2(path) {
|
|
20344
21074
|
if (process.platform === "win32") return;
|
|
20345
|
-
const mode = (await
|
|
21075
|
+
const mode = (await stat2(path)).mode & 511;
|
|
20346
21076
|
if ((mode & 63) !== 0) {
|
|
20347
21077
|
throw new IdentityInvalidError(`private key permissions must be 0600, got ${mode.toString(8).padStart(3, "0")}`);
|
|
20348
21078
|
}
|
|
@@ -20352,16 +21082,16 @@ async function atomicJsonWrite(path, value, mode) {
|
|
|
20352
21082
|
`, mode);
|
|
20353
21083
|
}
|
|
20354
21084
|
async function atomicTextWrite(path, value, mode) {
|
|
20355
|
-
await
|
|
21085
|
+
await mkdir2(dirname4(path), { recursive: true, mode: 448 });
|
|
20356
21086
|
const temporary = `${path}.${process.pid}.${uuidV7()}.tmp`;
|
|
20357
|
-
await
|
|
20358
|
-
await
|
|
20359
|
-
await
|
|
20360
|
-
await
|
|
21087
|
+
await writeFile2(temporary, value, { encoding: "utf8", mode, flag: "wx" });
|
|
21088
|
+
await chmod2(temporary, mode);
|
|
21089
|
+
await rename2(temporary, path);
|
|
21090
|
+
await chmod2(path, mode);
|
|
20361
21091
|
}
|
|
20362
|
-
async function
|
|
21092
|
+
async function exists2(path) {
|
|
20363
21093
|
try {
|
|
20364
|
-
await
|
|
21094
|
+
await stat2(path);
|
|
20365
21095
|
return true;
|
|
20366
21096
|
} catch (error) {
|
|
20367
21097
|
if (isNodeError(error) && error.code === "ENOENT") return false;
|
|
@@ -20375,74 +21105,148 @@ function safeErrorMessage(error) {
|
|
|
20375
21105
|
return error instanceof Error ? error.message : "invalid identity data";
|
|
20376
21106
|
}
|
|
20377
21107
|
|
|
20378
|
-
// src/
|
|
20379
|
-
|
|
20380
|
-
|
|
20381
|
-
var
|
|
20382
|
-
|
|
20383
|
-
|
|
20384
|
-
|
|
20385
|
-
|
|
20386
|
-
|
|
20387
|
-
|
|
20388
|
-
|
|
20389
|
-
|
|
20390
|
-
|
|
20391
|
-
|
|
20392
|
-
|
|
20393
|
-
|
|
20394
|
-
|
|
20395
|
-
|
|
21108
|
+
// src/control-stream.ts
|
|
21109
|
+
var STATUS_STREAM_SAMPLE_INTERVAL_MS = 1500;
|
|
21110
|
+
var STATUS_STREAM_HEARTBEAT_INTERVAL_MS = 15e3;
|
|
21111
|
+
var STATUS_STREAM_RETRY_MS = 3e3;
|
|
21112
|
+
var KEEP_ALIVE_FRAME = ": keep-alive\n\n";
|
|
21113
|
+
var ControlStatusStream = class {
|
|
21114
|
+
/**
|
|
21115
|
+
* @param readStatus - reads the current status value, the same value the unary control endpoint returns.
|
|
21116
|
+
* @param options - sampling, keep-alive, and reconnect periods.
|
|
21117
|
+
*/
|
|
21118
|
+
constructor(readStatus, options = {}) {
|
|
21119
|
+
this.readStatus = readStatus;
|
|
21120
|
+
this.sampleIntervalMs = options.sampleIntervalMs ?? STATUS_STREAM_SAMPLE_INTERVAL_MS;
|
|
21121
|
+
this.heartbeatIntervalMs = options.heartbeatIntervalMs ?? STATUS_STREAM_HEARTBEAT_INTERVAL_MS;
|
|
21122
|
+
this.retryMs = options.retryMs ?? STATUS_STREAM_RETRY_MS;
|
|
21123
|
+
}
|
|
21124
|
+
subscribers = /* @__PURE__ */ new Set();
|
|
21125
|
+
sampleIntervalMs;
|
|
21126
|
+
heartbeatIntervalMs;
|
|
21127
|
+
retryMs;
|
|
21128
|
+
timer;
|
|
21129
|
+
sampling = false;
|
|
21130
|
+
payload;
|
|
21131
|
+
writtenAt = 0;
|
|
21132
|
+
closed = false;
|
|
21133
|
+
/**
|
|
21134
|
+
* Write the SSE response head, the current status as its first frame, and keep
|
|
21135
|
+
* pushing until the client disconnects or {@link close} runs.
|
|
21136
|
+
* @param response - the loopback route response, owned for the connection lifetime.
|
|
21137
|
+
*/
|
|
21138
|
+
async handle(response) {
|
|
21139
|
+
if (this.closed) {
|
|
21140
|
+
response.writeHead(503);
|
|
21141
|
+
response.end();
|
|
21142
|
+
return;
|
|
21143
|
+
}
|
|
21144
|
+
response.writeHead(200, {
|
|
21145
|
+
"content-type": "text/event-stream",
|
|
21146
|
+
"cache-control": "no-store",
|
|
21147
|
+
connection: "keep-alive"
|
|
21148
|
+
});
|
|
21149
|
+
try {
|
|
21150
|
+
response.write(`retry: ${String(this.retryMs)}
|
|
21151
|
+
|
|
21152
|
+
`);
|
|
21153
|
+
} catch {
|
|
21154
|
+
return;
|
|
21155
|
+
}
|
|
21156
|
+
const subscriber = { response, payload: void 0 };
|
|
21157
|
+
this.subscribers.add(subscriber);
|
|
21158
|
+
const detach = () => {
|
|
21159
|
+
this.detach(subscriber);
|
|
21160
|
+
};
|
|
21161
|
+
response.on("close", detach);
|
|
21162
|
+
response.on("error", detach);
|
|
21163
|
+
this.startSampling();
|
|
21164
|
+
this.write(await this.readSnapshot() ?? this.payload);
|
|
20396
21165
|
}
|
|
20397
|
-
|
|
20398
|
-
|
|
20399
|
-
|
|
20400
|
-
|
|
21166
|
+
/** End every open stream and stop sampling. */
|
|
21167
|
+
close() {
|
|
21168
|
+
this.closed = true;
|
|
21169
|
+
this.stopSampling();
|
|
21170
|
+
for (const subscriber of [...this.subscribers]) {
|
|
21171
|
+
try {
|
|
21172
|
+
subscriber.response.end();
|
|
21173
|
+
} catch {
|
|
21174
|
+
}
|
|
21175
|
+
}
|
|
21176
|
+
this.subscribers.clear();
|
|
21177
|
+
this.payload = void 0;
|
|
21178
|
+
}
|
|
21179
|
+
/** Forget one subscriber; the last one to leave also stops the sampler. */
|
|
21180
|
+
detach(subscriber) {
|
|
21181
|
+
this.subscribers.delete(subscriber);
|
|
21182
|
+
if (this.subscribers.size === 0) this.stopSampling();
|
|
21183
|
+
}
|
|
21184
|
+
startSampling() {
|
|
21185
|
+
if (this.closed || this.timer !== void 0) return;
|
|
21186
|
+
this.timer = setInterval(() => {
|
|
21187
|
+
void this.sample();
|
|
21188
|
+
}, this.sampleIntervalMs);
|
|
21189
|
+
}
|
|
21190
|
+
stopSampling() {
|
|
21191
|
+
if (this.timer === void 0) return;
|
|
21192
|
+
clearInterval(this.timer);
|
|
21193
|
+
this.timer = void 0;
|
|
21194
|
+
}
|
|
21195
|
+
async sample() {
|
|
21196
|
+
if (this.sampling || this.closed) return;
|
|
21197
|
+
this.sampling = true;
|
|
20401
21198
|
try {
|
|
20402
|
-
|
|
20403
|
-
|
|
20404
|
-
|
|
21199
|
+
const snapshot = await this.readSnapshot();
|
|
21200
|
+
if (snapshot !== void 0 && !this.closed) this.write(snapshot);
|
|
21201
|
+
} finally {
|
|
21202
|
+
this.sampling = false;
|
|
20405
21203
|
}
|
|
20406
|
-
return parsed.serverUrl === serverUrl && parsed.deviceId === deviceId ? parsed : void 0;
|
|
20407
21204
|
}
|
|
20408
|
-
async
|
|
20409
|
-
|
|
20410
|
-
|
|
20411
|
-
|
|
20412
|
-
|
|
21205
|
+
async readSnapshot() {
|
|
21206
|
+
try {
|
|
21207
|
+
const value = await this.readStatus();
|
|
21208
|
+
return value === void 0 ? void 0 : JSON.stringify(value) ?? void 0;
|
|
21209
|
+
} catch {
|
|
21210
|
+
return void 0;
|
|
21211
|
+
}
|
|
20413
21212
|
}
|
|
20414
|
-
|
|
20415
|
-
|
|
21213
|
+
/**
|
|
21214
|
+
* Write the payload to every subscriber that has not seen it, or one
|
|
21215
|
+
* keep-alive comment when the whole connection set is idle.
|
|
21216
|
+
*/
|
|
21217
|
+
write(snapshot) {
|
|
21218
|
+
if (snapshot === void 0 || this.subscribers.size === 0) return;
|
|
21219
|
+
const now = Date.now();
|
|
21220
|
+
const fresh = [...this.subscribers].filter((subscriber) => subscriber.payload !== snapshot);
|
|
21221
|
+
if (fresh.length > 0) {
|
|
21222
|
+
const frame = `data: ${snapshot}
|
|
21223
|
+
|
|
21224
|
+
`;
|
|
21225
|
+
for (const subscriber of fresh) {
|
|
21226
|
+
if (this.writeChunk(subscriber, frame)) subscriber.payload = snapshot;
|
|
21227
|
+
else this.detach(subscriber);
|
|
21228
|
+
}
|
|
21229
|
+
this.payload = snapshot;
|
|
21230
|
+
this.writtenAt = now;
|
|
21231
|
+
return;
|
|
21232
|
+
}
|
|
21233
|
+
if (now - this.writtenAt < this.heartbeatIntervalMs) return;
|
|
21234
|
+
for (const subscriber of [...this.subscribers]) {
|
|
21235
|
+
if (!this.writeChunk(subscriber, KEEP_ALIVE_FRAME)) this.detach(subscriber);
|
|
21236
|
+
}
|
|
21237
|
+
this.writtenAt = now;
|
|
20416
21238
|
}
|
|
20417
|
-
|
|
20418
|
-
|
|
20419
|
-
|
|
20420
|
-
|
|
20421
|
-
|
|
20422
|
-
|
|
20423
|
-
|
|
20424
|
-
|
|
20425
|
-
|
|
20426
|
-
await rename2(temporary, path);
|
|
20427
|
-
await chmod2(path, 384);
|
|
20428
|
-
}
|
|
20429
|
-
async function assertPrivateMode2(path) {
|
|
20430
|
-
if (process.platform === "win32") return;
|
|
20431
|
-
const mode = (await stat2(path)).mode & 511;
|
|
20432
|
-
if ((mode & 63) !== 0) throw new ServerCredentialsInvalidError("server credentials permissions must be 0600");
|
|
20433
|
-
}
|
|
20434
|
-
async function exists2(path) {
|
|
20435
|
-
try {
|
|
20436
|
-
await stat2(path);
|
|
20437
|
-
return true;
|
|
20438
|
-
} catch (error) {
|
|
20439
|
-
if (error instanceof Error && "code" in error && error.code === "ENOENT") return false;
|
|
20440
|
-
throw error;
|
|
21239
|
+
/** @returns true when the chunk reached a live response. */
|
|
21240
|
+
writeChunk(subscriber, chunk) {
|
|
21241
|
+
if (subscriber.response.destroyed || subscriber.response.writableEnded) return false;
|
|
21242
|
+
try {
|
|
21243
|
+
subscriber.response.write(chunk);
|
|
21244
|
+
return true;
|
|
21245
|
+
} catch {
|
|
21246
|
+
return false;
|
|
21247
|
+
}
|
|
20441
21248
|
}
|
|
20442
|
-
}
|
|
20443
|
-
function safeMessage(error) {
|
|
20444
|
-
return error instanceof Error ? error.message : "invalid credential data";
|
|
20445
|
-
}
|
|
21249
|
+
};
|
|
20446
21250
|
|
|
20447
21251
|
// src/control-runtime.ts
|
|
20448
21252
|
var PluginControlRuntime = class {
|
|
@@ -20454,10 +21258,27 @@ var PluginControlRuntime = class {
|
|
|
20454
21258
|
this.host = host;
|
|
20455
21259
|
}
|
|
20456
21260
|
register(connection, webServer) {
|
|
20457
|
-
|
|
21261
|
+
const statusStream = new ControlStatusStream(() => this.streamStatus());
|
|
21262
|
+
return registerControlRoute(
|
|
21263
|
+
connection,
|
|
21264
|
+
(endpoint, payload, signal) => this.handle(endpoint, payload, signal),
|
|
21265
|
+
webServer,
|
|
21266
|
+
statusStream
|
|
21267
|
+
);
|
|
21268
|
+
}
|
|
21269
|
+
/**
|
|
21270
|
+
* Read the value the status event stream pushes. It resolves through the same
|
|
21271
|
+
* endpoint handler as the unary `status` control call, so a pushed status and
|
|
21272
|
+
* a polled one can never diverge.
|
|
21273
|
+
*/
|
|
21274
|
+
async streamStatus() {
|
|
21275
|
+
const result = await this.handle("status", {}, new AbortController().signal);
|
|
21276
|
+
if (!result.ok) throw new ClientModeError("STATUS_UNAVAILABLE", result.error.message);
|
|
21277
|
+
return result.value;
|
|
20458
21278
|
}
|
|
20459
21279
|
async handle(endpoint, payload, signal) {
|
|
20460
21280
|
try {
|
|
21281
|
+
if (endpoint === "settings.development.set") return ok3(await this.setDevelopment(payload));
|
|
20461
21282
|
if (endpoint === "settings.get") return ok3(await this.settingsView());
|
|
20462
21283
|
if (endpoint === "settings.configure") return ok3(await this.configure(payload));
|
|
20463
21284
|
if (endpoint === "settings.server.set") return ok3(await this.setServer(payload));
|
|
@@ -20561,6 +21382,23 @@ var PluginControlRuntime = class {
|
|
|
20561
21382
|
await this.settings.replace({ ...current, role });
|
|
20562
21383
|
return this.settingsView();
|
|
20563
21384
|
}
|
|
21385
|
+
async setDevelopment(payload) {
|
|
21386
|
+
if (this.settings === void 0) throw new ClientModeError("SETTINGS_UNAVAILABLE", "DSH user settings are unavailable in this profile.");
|
|
21387
|
+
const value = record4(payload);
|
|
21388
|
+
if (value.terminalEnabled !== void 0 && typeof value.terminalEnabled !== "boolean") throw new ClientModeError("INVALID_MESSAGE", "The terminal switch must be a boolean.");
|
|
21389
|
+
if (value.ports !== void 0 && (!Array.isArray(value.ports) || value.ports.some((port) => !Number.isInteger(port)))) throw new ClientModeError("INVALID_MESSAGE", "Loopback ports must be integers.");
|
|
21390
|
+
if (value.terminalEnabled === void 0 && value.ports === void 0) throw new ClientModeError("INVALID_MESSAGE", "A terminal switch or loopback ports are required.");
|
|
21391
|
+
const current = editableConfig(resolveConfig(this.settings.get()));
|
|
21392
|
+
const next = resolveConfig({
|
|
21393
|
+
...current,
|
|
21394
|
+
terminal: { enabled: value.terminalEnabled === void 0 ? current.terminal?.enabled ?? false : value.terminalEnabled },
|
|
21395
|
+
loopback: { ports: value.ports === void 0 ? current.loopback?.ports ?? [] : value.ports }
|
|
21396
|
+
});
|
|
21397
|
+
await this.settings.replace(editableConfig(next));
|
|
21398
|
+
if (value.terminalEnabled !== void 0) this.host?.setTerminalEnabled?.(value.terminalEnabled);
|
|
21399
|
+
if (value.ports !== void 0) this.host?.setLoopbackPorts?.(value.ports);
|
|
21400
|
+
return this.settingsView();
|
|
21401
|
+
}
|
|
20564
21402
|
async setCodex(payload) {
|
|
20565
21403
|
if (this.settings === void 0) {
|
|
20566
21404
|
throw new ClientModeError("SETTINGS_UNAVAILABLE", "DSH user settings are unavailable in this profile.");
|
|
@@ -20601,10 +21439,10 @@ var PluginControlRuntime = class {
|
|
|
20601
21439
|
}
|
|
20602
21440
|
async removeAcp(payload) {
|
|
20603
21441
|
if (this.settings === void 0) throw new ClientModeError("SETTINGS_UNAVAILABLE", "DSH user settings are unavailable in this profile.");
|
|
20604
|
-
const
|
|
20605
|
-
if (typeof
|
|
21442
|
+
const id4 = record4(payload).id;
|
|
21443
|
+
if (typeof id4 !== "string" || ["codex", "cursor", "kimi"].includes(id4)) throw new ClientModeError("INVALID_MESSAGE", "Only custom ACP backends can be removed.");
|
|
20606
21444
|
const current = resolveConfig(this.settings.get());
|
|
20607
|
-
const backends = (current.acp?.backends ?? []).filter((item) => item.id !==
|
|
21445
|
+
const backends = (current.acp?.backends ?? []).filter((item) => item.id !== id4);
|
|
20608
21446
|
await this.settings.replace(editableConfig({ ...current, acp: { enabled: current.acp?.enabled ?? true, backends } }));
|
|
20609
21447
|
return this.settingsView();
|
|
20610
21448
|
}
|
|
@@ -20698,6 +21536,8 @@ function editableConfig(config) {
|
|
|
20698
21536
|
enabled: config.enabled,
|
|
20699
21537
|
role: config.role,
|
|
20700
21538
|
...config.serverUrl === void 0 ? {} : { serverUrl: config.serverUrl },
|
|
21539
|
+
terminal: config.terminal,
|
|
21540
|
+
loopback: config.loopback,
|
|
20701
21541
|
forceRelay: config.forceRelay,
|
|
20702
21542
|
logLevel: config.logLevel,
|
|
20703
21543
|
reconnect: config.reconnect.enabled ? {
|
|
@@ -20768,8 +21608,70 @@ function redact(value, key = "") {
|
|
|
20768
21608
|
return Object.fromEntries(Object.entries(value).map(([childKey, child]) => [childKey, redact(child, childKey)]));
|
|
20769
21609
|
}
|
|
20770
21610
|
|
|
21611
|
+
// src/terminal-policy.ts
|
|
21612
|
+
var TERMINAL_CALLS = /* @__PURE__ */ new Set([
|
|
21613
|
+
"terminal/environment",
|
|
21614
|
+
"terminal/shells",
|
|
21615
|
+
"terminal/list",
|
|
21616
|
+
"terminal/create",
|
|
21617
|
+
"terminal/write",
|
|
21618
|
+
"terminal/resize",
|
|
21619
|
+
"terminal/rename",
|
|
21620
|
+
"terminal/close"
|
|
21621
|
+
]);
|
|
21622
|
+
var TERMINAL_STREAMS = /* @__PURE__ */ new Set(["terminal/follow", "terminal/retain"]);
|
|
21623
|
+
var id2 = external_exports.string().regex(/^[\w-]{1,128}$/);
|
|
21624
|
+
var TerminalPolicy = class {
|
|
21625
|
+
constructor(enabled, deviceId, owners) {
|
|
21626
|
+
this.enabled = enabled;
|
|
21627
|
+
this.deviceId = deviceId;
|
|
21628
|
+
this.owners = owners;
|
|
21629
|
+
}
|
|
21630
|
+
attachments = /* @__PURE__ */ new Map();
|
|
21631
|
+
check(endpoint, payload) {
|
|
21632
|
+
if (!(typeof this.enabled === "function" ? this.enabled() : this.enabled)) throw new RpcError(
|
|
21633
|
+
"TERMINAL_DISABLED",
|
|
21634
|
+
"Remote terminal is disabled on this Host. Enable Remote terminal in the Host Remote settings; the switch saves and applies immediately. / \u8FDC\u7A0B\u7EC8\u7AEF\u672A\u5F00\u542F\uFF0C\u8BF7\u5728 Host \u7684 Remote \u8BBE\u7F6E\u4E2D\u5F00\u542F\u300C\u8FDC\u7A0B\u7EC8\u7AEF\u300D\uFF0C\u5F00\u5173\u5207\u6362\u540E\u7ACB\u5373\u4FDD\u5B58\u5E76\u751F\u6548\u3002"
|
|
21635
|
+
);
|
|
21636
|
+
const args = external_exports.object({ args: external_exports.record(external_exports.unknown()) }).strict().parse(payload).args;
|
|
21637
|
+
const sessionId = id2.parse(args.agentId ?? args.sessionId);
|
|
21638
|
+
if (endpoint === "terminal/environment" || endpoint === "terminal/shells" || endpoint === "terminal/list") return {};
|
|
21639
|
+
const request = endpoint === "terminal/create" ? external_exports.object({ id: id2 }).passthrough().parse(args.request) : void 0;
|
|
21640
|
+
const terminalId = id2.parse(request?.id ?? args.id);
|
|
21641
|
+
const key = `${sessionId}/${terminalId}`;
|
|
21642
|
+
if (endpoint === "terminal/create") {
|
|
21643
|
+
if (this.owners.has(key) && this.owners.get(key) !== this.deviceId) this.deny();
|
|
21644
|
+
if (!this.owners.has(key) && this.owners.size >= 256) throw new RpcError("RATE_LIMITED", "Too many retained remote terminals.");
|
|
21645
|
+
const created = !this.owners.has(key);
|
|
21646
|
+
this.owners.set(key, this.deviceId);
|
|
21647
|
+
return { key, created };
|
|
21648
|
+
}
|
|
21649
|
+
if (this.owners.get(key) !== this.deviceId) this.deny();
|
|
21650
|
+
if (endpoint === "terminal/follow") this.attachments.set(key, id2.parse(args.attachmentId));
|
|
21651
|
+
if (endpoint === "terminal/write" || endpoint === "terminal/resize") {
|
|
21652
|
+
if (this.attachments.get(key) !== id2.parse(args.attachmentId)) this.deny();
|
|
21653
|
+
}
|
|
21654
|
+
return { key };
|
|
21655
|
+
}
|
|
21656
|
+
result(endpoint, payload, result, reservation) {
|
|
21657
|
+
if (reservation.key !== void 0 && (!result.ok && reservation.created || result.ok && endpoint === "terminal/close")) {
|
|
21658
|
+
this.owners.delete(reservation.key);
|
|
21659
|
+
this.attachments.delete(reservation.key);
|
|
21660
|
+
}
|
|
21661
|
+
if (endpoint === "terminal/list" && result.ok && Array.isArray(result.value)) {
|
|
21662
|
+
const args = payload.args;
|
|
21663
|
+
const session = args.sessionId ?? args.agentId;
|
|
21664
|
+
return { ok: true, value: result.value.filter((value) => typeof value?.id === "string" && this.owners.get(`${session}/${value.id}`) === this.deviceId) };
|
|
21665
|
+
}
|
|
21666
|
+
return result;
|
|
21667
|
+
}
|
|
21668
|
+
deny() {
|
|
21669
|
+
throw new RpcError("PERMISSION_DENIED", "This terminal or input attachment belongs to another connection or device.");
|
|
21670
|
+
}
|
|
21671
|
+
};
|
|
21672
|
+
|
|
20771
21673
|
// src/service.ts
|
|
20772
|
-
import { randomUUID as
|
|
21674
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
20773
21675
|
|
|
20774
21676
|
// src/connection-controller.ts
|
|
20775
21677
|
var ConnectionController = class {
|
|
@@ -20953,21 +21855,6 @@ function shortId2(value) {
|
|
|
20953
21855
|
return value.length <= 12 ? value : `${value.slice(0, 8)}\u2026${value.slice(-4)}`;
|
|
20954
21856
|
}
|
|
20955
21857
|
|
|
20956
|
-
// src/safe-error.ts
|
|
20957
|
-
var RpcError = class extends Error {
|
|
20958
|
-
constructor(code, message, details, retryable = false) {
|
|
20959
|
-
super(message);
|
|
20960
|
-
this.code = code;
|
|
20961
|
-
this.details = details;
|
|
20962
|
-
this.retryable = retryable;
|
|
20963
|
-
}
|
|
20964
|
-
};
|
|
20965
|
-
function safeErrorCode(error) {
|
|
20966
|
-
if (error instanceof RpcError) return error.code;
|
|
20967
|
-
if (error instanceof external_exports.ZodError) return "INVALID_MESSAGE";
|
|
20968
|
-
return "INTERNAL_ERROR";
|
|
20969
|
-
}
|
|
20970
|
-
|
|
20971
21858
|
// src/rpc-router.ts
|
|
20972
21859
|
var wireRequestSchema = external_exports.object({ method: external_exports.string().min(1), params: external_exports.unknown() }).strict();
|
|
20973
21860
|
var emptyParamsSchema = external_exports.object({}).strict();
|
|
@@ -20990,6 +21877,7 @@ var apiMethods = /* @__PURE__ */ new Set([
|
|
|
20990
21877
|
"harness.remote.transfer.close",
|
|
20991
21878
|
"harness.remote.stream.open",
|
|
20992
21879
|
"harness.remote.stream.close",
|
|
21880
|
+
"loopback.call",
|
|
20993
21881
|
"fileviewer.call",
|
|
20994
21882
|
"codex.app.call",
|
|
20995
21883
|
"codex.app.respond",
|
|
@@ -21019,7 +21907,7 @@ var HOST_CAPABILITIES = [
|
|
|
21019
21907
|
"agent.acp.v1"
|
|
21020
21908
|
];
|
|
21021
21909
|
var RpcRouter = class {
|
|
21022
|
-
constructor(harnessApi, maxPending = 128, logger, fileViewer, harnessRemote, capabilities = () => HOST_CAPABILITIES, codex, acp) {
|
|
21910
|
+
constructor(harnessApi, maxPending = 128, logger, fileViewer, harnessRemote, capabilities = () => HOST_CAPABILITIES, codex, acp, loopback) {
|
|
21023
21911
|
this.harnessApi = harnessApi;
|
|
21024
21912
|
this.maxPending = maxPending;
|
|
21025
21913
|
this.logger = logger;
|
|
@@ -21028,9 +21916,11 @@ var RpcRouter = class {
|
|
|
21028
21916
|
this.capabilities = capabilities;
|
|
21029
21917
|
this.codex = codex;
|
|
21030
21918
|
this.acp = acp;
|
|
21919
|
+
this.loopback = loopback;
|
|
21031
21920
|
}
|
|
21032
21921
|
active = 0;
|
|
21033
21922
|
async closePeerStreams() {
|
|
21923
|
+
this.loopback?.closeAll();
|
|
21034
21924
|
await Promise.all([
|
|
21035
21925
|
this.harnessApi?.closeAll(),
|
|
21036
21926
|
this.harnessRemote?.closeAll(),
|
|
@@ -21112,6 +22002,10 @@ var RpcRouter = class {
|
|
|
21112
22002
|
return this.requireRemoteGateway().openStream(params);
|
|
21113
22003
|
case "harness.remote.stream.close":
|
|
21114
22004
|
return this.requireRemoteGateway().closeStream(params);
|
|
22005
|
+
case "loopback.call": {
|
|
22006
|
+
if (this.loopback === void 0) throw new RpcError("FEATURE_NOT_SUPPORTED", "Loopback preview is unavailable on this Host.");
|
|
22007
|
+
return this.loopback.call(params);
|
|
22008
|
+
}
|
|
21115
22009
|
case "fileviewer.call": {
|
|
21116
22010
|
if (this.fileViewer === void 0) {
|
|
21117
22011
|
throw new RpcError("FILE_VIEWER_UNAVAILABLE", "The Remote Host does not have DSH File Viewer available.");
|
|
@@ -21332,6 +22226,7 @@ var HostServerConnection = class {
|
|
|
21332
22226
|
lastActiveAt;
|
|
21333
22227
|
reconnectRequested = false;
|
|
21334
22228
|
resumeQueued = false;
|
|
22229
|
+
authRecoveryAttempted = false;
|
|
21335
22230
|
rtcFactory;
|
|
21336
22231
|
negotiatedCapabilities = ["transport.relay"];
|
|
21337
22232
|
controlFrameLimits = {};
|
|
@@ -21395,14 +22290,23 @@ var HostServerConnection = class {
|
|
|
21395
22290
|
}
|
|
21396
22291
|
async run() {
|
|
21397
22292
|
let delayMs = this.config.reconnect.initialDelayMs;
|
|
22293
|
+
this.authRecoveryAttempted = false;
|
|
21398
22294
|
while (!this.stopped) {
|
|
21399
22295
|
try {
|
|
21400
22296
|
await this.connectOnce();
|
|
21401
22297
|
delayMs = this.config.reconnect.initialDelayMs;
|
|
21402
22298
|
} catch (error) {
|
|
21403
22299
|
const code = errorCode2(error);
|
|
22300
|
+
if (code === "CREDENTIALS_REFRESHED") continue;
|
|
21404
22301
|
this.terminalError = code;
|
|
21405
|
-
this.logger.warn("server control connection failed", {
|
|
22302
|
+
this.logger.warn("server control connection failed", {
|
|
22303
|
+
code,
|
|
22304
|
+
retryable: isRetryable(error),
|
|
22305
|
+
...error instanceof ServerApiError && error.phase !== void 0 ? { phase: error.phase } : {}
|
|
22306
|
+
});
|
|
22307
|
+
if (TERMINAL_AUTH_ERRORS.has(code)) {
|
|
22308
|
+
this.logger.warn(code === "CONNECTION_REPLACED" ? "Another instance is using this Host identity. Stop it or use a separate DSH_HOME; automatic reconnect is paused." : code === "SERVER_CREDENTIALS_BUSY" ? "Credential refresh is locked. Stop other instances; after a crash, stop all instances before removing server-credentials.json.refresh-lock and authorizing again." : "Host authorization failed. Run /remote login or authorize this Host again in Remote settings.");
|
|
22309
|
+
}
|
|
21406
22310
|
if (TERMINAL_AUTH_ERRORS.has(code) || !this.config.reconnect.enabled) return;
|
|
21407
22311
|
}
|
|
21408
22312
|
if (this.stopped) return;
|
|
@@ -21458,6 +22362,7 @@ var HostServerConnection = class {
|
|
|
21458
22362
|
this.lastActiveAt = Date.now();
|
|
21459
22363
|
if (frame.type === "hello.ack") {
|
|
21460
22364
|
const payload = requireHelloAck(frame.payload);
|
|
22365
|
+
this.authRecoveryAttempted = false;
|
|
21461
22366
|
this.controlFrameLimits = {
|
|
21462
22367
|
maxControlFrameBytes: payload.maxControlFrameBytes,
|
|
21463
22368
|
maxRelayFrameBytes: payload.maxRelayFrameBytes
|
|
@@ -21494,13 +22399,28 @@ var HostServerConnection = class {
|
|
|
21494
22399
|
socket.onclose = (event) => {
|
|
21495
22400
|
const close = async () => {
|
|
21496
22401
|
await messageQueue.catch(() => void 0);
|
|
22402
|
+
if (this.stopped) {
|
|
22403
|
+
finish();
|
|
22404
|
+
return;
|
|
22405
|
+
}
|
|
22406
|
+
if (event.code === 4003) {
|
|
22407
|
+
finish(new ControlConnectionError("CONNECTION_REPLACED", "Another instance connected with this Host identity."));
|
|
22408
|
+
return;
|
|
22409
|
+
}
|
|
21497
22410
|
if (event.code === 4002) {
|
|
22411
|
+
if (this.authRecoveryAttempted) {
|
|
22412
|
+
finish(new ControlConnectionError("AUTH_INVALID", "Server rejected refreshed credentials."));
|
|
22413
|
+
return;
|
|
22414
|
+
}
|
|
21498
22415
|
try {
|
|
21499
|
-
await this.api.refreshCredentials();
|
|
22416
|
+
await this.api.refreshCredentials(credentials.accessToken);
|
|
21500
22417
|
} catch (error) {
|
|
21501
22418
|
finish(asError2(error));
|
|
21502
22419
|
return;
|
|
21503
22420
|
}
|
|
22421
|
+
this.authRecoveryAttempted = true;
|
|
22422
|
+
finish(new ControlConnectionError("CREDENTIALS_REFRESHED", "Retry hello with refreshed credentials."));
|
|
22423
|
+
return;
|
|
21504
22424
|
}
|
|
21505
22425
|
if (event.code === 4004) {
|
|
21506
22426
|
finish(new ControlConnectionError("DEVICE_REVOKED", "The Server revoked this Host device."));
|
|
@@ -21748,7 +22668,7 @@ var HostServerConnection = class {
|
|
|
21748
22668
|
}
|
|
21749
22669
|
});
|
|
21750
22670
|
tunnel.rtc = rtc;
|
|
21751
|
-
rtc.onMessage((
|
|
22671
|
+
rtc.onMessage((data2) => tunnel.channel?.receive(void 0, data2));
|
|
21752
22672
|
rtc.onClose(() => {
|
|
21753
22673
|
void this.handleRtcFailed(tunnel, rtc, new Error("WebRTC data channel closed."));
|
|
21754
22674
|
});
|
|
@@ -21905,6 +22825,10 @@ var HostServerConnection = class {
|
|
|
21905
22825
|
async sendRelay(tunnel, ciphertext) {
|
|
21906
22826
|
const counter = Number(tunnel.noise.sendingCounter() - 1n);
|
|
21907
22827
|
if (!Number.isSafeInteger(counter) || counter < 0) throw new ControlConnectionError("FRAME_TOO_LARGE", "Noise transport counter overflowed.");
|
|
22828
|
+
const socket = this.socket;
|
|
22829
|
+
if (socket === void 0) throw new Error("Relay transport closed");
|
|
22830
|
+
await waitForRelayCapacity(socket);
|
|
22831
|
+
if (this.socket !== socket) throw new Error("Relay transport replaced");
|
|
21908
22832
|
this.sendControl("relay", {
|
|
21909
22833
|
connectionId: tunnel.connectionId,
|
|
21910
22834
|
targetDeviceId: tunnel.peer.deviceId,
|
|
@@ -21970,6 +22894,8 @@ var HostServerConnection = class {
|
|
|
21970
22894
|
}
|
|
21971
22895
|
};
|
|
21972
22896
|
var TERMINAL_AUTH_ERRORS = /* @__PURE__ */ new Set([
|
|
22897
|
+
"CONNECTION_REPLACED",
|
|
22898
|
+
"SERVER_CREDENTIALS_BUSY",
|
|
21973
22899
|
"ACCOUNT_AUTH_REQUIRED",
|
|
21974
22900
|
"AUTH_INVALID",
|
|
21975
22901
|
"DEVICE_OWNERSHIP_REQUIRED",
|
|
@@ -21990,6 +22916,7 @@ var ServerNoiseChannel = class {
|
|
|
21990
22916
|
this.peerDeviceId = tunnel.peer.deviceId;
|
|
21991
22917
|
this.peerIdentityKey = tunnel.peer.publicKey;
|
|
21992
22918
|
}
|
|
22919
|
+
sends = new SerialSend();
|
|
21993
22920
|
security;
|
|
21994
22921
|
peerDeviceId;
|
|
21995
22922
|
peerIdentityKey;
|
|
@@ -22000,11 +22927,12 @@ var ServerNoiseChannel = class {
|
|
|
22000
22927
|
closed = false;
|
|
22001
22928
|
async send(message) {
|
|
22002
22929
|
if (this.closed) throw new Error("secure channel is closed");
|
|
22003
|
-
const
|
|
22930
|
+
const encoded = encodeMessage(message);
|
|
22004
22931
|
try {
|
|
22005
|
-
|
|
22006
|
-
|
|
22007
|
-
|
|
22932
|
+
await this.sends.run(encoded.byteLength, async () => {
|
|
22933
|
+
if (this.closed) throw new Error("Secure channel closed");
|
|
22934
|
+
for (const plaintext of this.outgoing.encode(encoded)) await this.transmit(this.tunnel.noise.encrypt(plaintext));
|
|
22935
|
+
});
|
|
22008
22936
|
} catch (error) {
|
|
22009
22937
|
await this.close().catch(() => void 0);
|
|
22010
22938
|
throw error;
|
|
@@ -22049,10 +22977,10 @@ function websocketUrl2(baseUrl) {
|
|
|
22049
22977
|
url.pathname = `${url.pathname.replace(/\/$/, "")}/ws/v1/connect`;
|
|
22050
22978
|
return url.toString();
|
|
22051
22979
|
}
|
|
22052
|
-
function decodeControl(
|
|
22053
|
-
if (typeof
|
|
22980
|
+
function decodeControl(data2, limits) {
|
|
22981
|
+
if (typeof data2 !== "string") throw new ControlConnectionError("INVALID_MESSAGE", "Server control frames must be text JSON.");
|
|
22054
22982
|
try {
|
|
22055
|
-
return decodeControlFrame(
|
|
22983
|
+
return decodeControlFrame(data2, limits);
|
|
22056
22984
|
} catch {
|
|
22057
22985
|
throw new ControlConnectionError("INVALID_MESSAGE", "Server sent an invalid control frame.");
|
|
22058
22986
|
}
|
|
@@ -22158,10 +23086,11 @@ function errorCode2(error) {
|
|
|
22158
23086
|
return error instanceof ServerApiError || error instanceof ControlConnectionError ? error.code : "CONNECTION_FAILED";
|
|
22159
23087
|
}
|
|
22160
23088
|
function isRetryable(error) {
|
|
22161
|
-
return error instanceof ServerApiError
|
|
23089
|
+
return !TERMINAL_AUTH_ERRORS.has(errorCode2(error)) && (!(error instanceof ServerApiError) || error.retryable);
|
|
22162
23090
|
}
|
|
22163
23091
|
function closeCode(code) {
|
|
22164
23092
|
if (code === 4002) return "AUTH_INVALID";
|
|
23093
|
+
if (code === 4003) return "CONNECTION_REPLACED";
|
|
22165
23094
|
if (code === 4004) return "DEVICE_REVOKED";
|
|
22166
23095
|
if (code === 4007) return "RATE_LIMITED";
|
|
22167
23096
|
if (code === 4011) return "UNSUPPORTED_VERSION";
|
|
@@ -22176,13 +23105,13 @@ var MAX_ENTRIES = 500;
|
|
|
22176
23105
|
async function listRemoteDirectory(path, signal) {
|
|
22177
23106
|
signal?.throwIfAborted();
|
|
22178
23107
|
const home = resolve(homedir2());
|
|
22179
|
-
const
|
|
22180
|
-
if (!isAbsolute2(
|
|
22181
|
-
const rows = await readdir(
|
|
23108
|
+
const target2 = path === void 0 || path.trim() === "" ? home : resolve(path);
|
|
23109
|
+
if (!isAbsolute2(target2)) throw new Error("The remote directory path must be absolute.");
|
|
23110
|
+
const rows = await readdir(target2, { withFileTypes: true });
|
|
22182
23111
|
const directories = [];
|
|
22183
23112
|
for (const row of rows) {
|
|
22184
23113
|
signal?.throwIfAborted();
|
|
22185
|
-
const child = resolve(
|
|
23114
|
+
const child = resolve(target2, row.name);
|
|
22186
23115
|
let directory = row.isDirectory();
|
|
22187
23116
|
if (!directory && row.isSymbolicLink()) directory = await stat3(child).then((value) => value.isDirectory()).catch(() => false);
|
|
22188
23117
|
if (!directory) continue;
|
|
@@ -22190,9 +23119,9 @@ async function listRemoteDirectory(path, signal) {
|
|
|
22190
23119
|
}
|
|
22191
23120
|
directories.sort((left, right) => left.name.localeCompare(right.name, void 0, { sensitivity: "base" }));
|
|
22192
23121
|
return {
|
|
22193
|
-
path:
|
|
23122
|
+
path: target2,
|
|
22194
23123
|
home,
|
|
22195
|
-
crumbs: crumbs(
|
|
23124
|
+
crumbs: crumbs(target2),
|
|
22196
23125
|
entries: directories.slice(0, MAX_ENTRIES),
|
|
22197
23126
|
truncated: directories.length > MAX_ENTRIES
|
|
22198
23127
|
};
|
|
@@ -22899,6 +23828,9 @@ function disableRemoteSettingsDocument(response) {
|
|
|
22899
23828
|
}
|
|
22900
23829
|
var EMPTY_SETTINGS_NAMESPACES = /* @__PURE__ */ new Set();
|
|
22901
23830
|
async function assertRegisteredSettingsNamespace(api, ns) {
|
|
23831
|
+
if (ns === "ds-harness-remote" || ns === "dsh-remote") {
|
|
23832
|
+
throw new RpcError("PERMISSION_DENIED", "Remote access settings can only be changed locally on the Host.");
|
|
23833
|
+
}
|
|
22902
23834
|
const allowed = await registeredSettingsNamespaces(api);
|
|
22903
23835
|
if (!allowed.has(ns)) throw deniedSettingsNamespace(ns);
|
|
22904
23836
|
}
|
|
@@ -23124,6 +24056,15 @@ var HARNESS_REMOTE_ALLOWLIST = [
|
|
|
23124
24056
|
"subagents/interruptByParent",
|
|
23125
24057
|
"subagents/list",
|
|
23126
24058
|
"subagents/prompt",
|
|
24059
|
+
"workspaceFiles/list",
|
|
24060
|
+
"workspaceFiles/stat",
|
|
24061
|
+
"workspaceFiles/read",
|
|
24062
|
+
"workspaceFiles/readBytes",
|
|
24063
|
+
"workspaceFiles/readAll",
|
|
24064
|
+
"workspaceFiles/readRelated",
|
|
24065
|
+
"workspaceFiles/changes",
|
|
24066
|
+
"officeToPdf/render",
|
|
24067
|
+
"officeToPdf/generation",
|
|
23127
24068
|
"workspace/archiveSession",
|
|
23128
24069
|
"workspace/create",
|
|
23129
24070
|
"workspace/delete",
|
|
@@ -23134,11 +24075,12 @@ var HARNESS_REMOTE_ALLOWLIST = [
|
|
|
23134
24075
|
];
|
|
23135
24076
|
var allowedEndpoints = new Set(HARNESS_REMOTE_ALLOWLIST);
|
|
23136
24077
|
var HarnessRemoteBridge = class {
|
|
23137
|
-
constructor(gateway, publish, logger, harnessVersion) {
|
|
24078
|
+
constructor(gateway, publish, logger, harnessVersion, terminal = new TerminalPolicy(() => false, "", /* @__PURE__ */ new Map())) {
|
|
23138
24079
|
this.gateway = gateway;
|
|
23139
24080
|
this.publish = publish;
|
|
23140
24081
|
this.logger = logger;
|
|
23141
24082
|
this.harnessVersion = harnessVersion;
|
|
24083
|
+
this.terminal = terminal;
|
|
23142
24084
|
}
|
|
23143
24085
|
streams = /* @__PURE__ */ new Map();
|
|
23144
24086
|
incomingTransfers = /* @__PURE__ */ new Map();
|
|
@@ -23146,6 +24088,14 @@ var HarnessRemoteBridge = class {
|
|
|
23146
24088
|
async call(input2) {
|
|
23147
24089
|
const params = callSchema3.parse(input2);
|
|
23148
24090
|
this.assertAllowed(params.endpoint);
|
|
24091
|
+
if (TERMINAL_STREAMS.has(params.endpoint)) throw new RpcError("METHOD_NOT_ALLOWED", "Use a stream for this terminal endpoint.");
|
|
24092
|
+
if (["settings/update", "settings/replace", "settings/mutate"].includes(params.endpoint)) {
|
|
24093
|
+
const args = requestArgs(params.payload);
|
|
24094
|
+
if (args.ns === "ds-harness-remote" || args.ns === "dsh-remote") {
|
|
24095
|
+
throw new RpcError("PERMISSION_DENIED", "Remote access settings can only be changed locally on the Host.");
|
|
24096
|
+
}
|
|
24097
|
+
}
|
|
24098
|
+
const reservation = params.endpoint.startsWith("terminal/") ? this.terminal.check(params.endpoint, params.payload) : void 0;
|
|
23149
24099
|
if (params.endpoint === "session/canOpenWorkspacePath") {
|
|
23150
24100
|
return { ok: true, value: true };
|
|
23151
24101
|
}
|
|
@@ -23158,8 +24108,9 @@ var HarnessRemoteBridge = class {
|
|
|
23158
24108
|
endpoint: params.endpoint,
|
|
23159
24109
|
durationMs: Math.round(performance.now() - startedAt)
|
|
23160
24110
|
});
|
|
23161
|
-
return result;
|
|
24111
|
+
return reservation === void 0 ? result : this.terminal.result(params.endpoint, params.payload, result, reservation);
|
|
23162
24112
|
} catch (error) {
|
|
24113
|
+
if (reservation !== void 0) this.terminal.result(params.endpoint, params.payload, { ok: false, error: { code: "FAILED", message: "", details: {} } }, reservation);
|
|
23163
24114
|
if (params.endpoint === "directoryPicker/list") {
|
|
23164
24115
|
const result = await this.directoryList(params.payload, signal);
|
|
23165
24116
|
this.logger?.debug("harness remote call ok", {
|
|
@@ -23286,13 +24237,24 @@ var HarnessRemoteBridge = class {
|
|
|
23286
24237
|
async openStream(input2) {
|
|
23287
24238
|
const params = streamOpenSchema2.parse(input2);
|
|
23288
24239
|
this.assertAllowed(params.endpoint);
|
|
24240
|
+
if (params.endpoint.startsWith("settings/")) throw new RpcError("METHOD_NOT_ALLOWED", "Settings endpoints are not streams.");
|
|
24241
|
+
if (TERMINAL_CALLS.has(params.endpoint)) throw new RpcError("METHOD_NOT_ALLOWED", "This terminal endpoint is not a stream.");
|
|
24242
|
+
if (TERMINAL_STREAMS.has(params.endpoint)) this.terminal.check(params.endpoint, params.payload);
|
|
23289
24243
|
if (this.streams.has(params.streamId)) throw new RpcError("REQUEST_CONFLICT", "The Harness Remote stream is already open.");
|
|
23290
24244
|
if (this.streams.size >= MAX_ACTIVE_STREAMS) {
|
|
23291
24245
|
throw new RpcError("RATE_LIMITED", "Too many Harness Remote streams are open.", void 0, true);
|
|
23292
24246
|
}
|
|
23293
24247
|
const controller = new AbortController();
|
|
23294
|
-
const source = await this.gateway.open(params.endpoint, params.payload, controller.signal);
|
|
23295
24248
|
this.streams.set(params.streamId, { controller });
|
|
24249
|
+
let source;
|
|
24250
|
+
try {
|
|
24251
|
+
source = await this.gateway.open(params.endpoint, params.payload, controller.signal);
|
|
24252
|
+
controller.signal.throwIfAborted();
|
|
24253
|
+
} catch (error) {
|
|
24254
|
+
this.streams.delete(params.streamId);
|
|
24255
|
+
controller.abort();
|
|
24256
|
+
throw error;
|
|
24257
|
+
}
|
|
23296
24258
|
void this.pump(params.streamId, source, controller.signal);
|
|
23297
24259
|
return { opened: true, streamId: params.streamId };
|
|
23298
24260
|
}
|
|
@@ -23313,7 +24275,7 @@ var HarnessRemoteBridge = class {
|
|
|
23313
24275
|
for (const [, stream] of streams) stream.controller.abort(reason);
|
|
23314
24276
|
}
|
|
23315
24277
|
assertAllowed(endpoint) {
|
|
23316
|
-
if (!allowedEndpoints.has(endpoint)) {
|
|
24278
|
+
if (!allowedEndpoints.has(endpoint) && !TERMINAL_CALLS.has(endpoint) && !TERMINAL_STREAMS.has(endpoint)) {
|
|
23317
24279
|
throw new RpcError("METHOD_NOT_ALLOWED", "The requested Harness Remote endpoint is not allowed.");
|
|
23318
24280
|
}
|
|
23319
24281
|
if (endpoint === "$events" && !this.gateway.supportsCarrier) {
|
|
@@ -23347,8 +24309,8 @@ var HarnessRemoteBridge = class {
|
|
|
23347
24309
|
}
|
|
23348
24310
|
pruneTransfers() {
|
|
23349
24311
|
const cutoff = Date.now() - TRANSFER_IDLE_MS;
|
|
23350
|
-
for (const [
|
|
23351
|
-
for (const [
|
|
24312
|
+
for (const [id4, transfer] of this.incomingTransfers) if (transfer.touchedAt < cutoff) this.incomingTransfers.delete(id4);
|
|
24313
|
+
for (const [id4, transfer] of this.outgoingTransfers) if (transfer.touchedAt < cutoff) this.outgoingTransfers.delete(id4);
|
|
23352
24314
|
}
|
|
23353
24315
|
};
|
|
23354
24316
|
async function dispatchCommandForHost(gateway, payload, signal, harnessVersion) {
|
|
@@ -23407,7 +24369,7 @@ function isRecord9(value) {
|
|
|
23407
24369
|
}
|
|
23408
24370
|
|
|
23409
24371
|
// src/codex/domain.ts
|
|
23410
|
-
import { randomUUID } from "node:crypto";
|
|
24372
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
23411
24373
|
import { readdir as readdir2, realpath, stat as stat4 } from "node:fs/promises";
|
|
23412
24374
|
import { homedir as homedir3 } from "node:os";
|
|
23413
24375
|
import { basename as basename3, isAbsolute as isAbsolute3, join as join5, relative, resolve as resolve2 } from "node:path";
|
|
@@ -23461,11 +24423,11 @@ var CodexAppServerClient = class {
|
|
|
23461
24423
|
if (!this.ready) throw new CodexAppServerError("CODEX_UNAVAILABLE", "Codex App Server is not ready.");
|
|
23462
24424
|
return this.request(method, params, timeoutMs);
|
|
23463
24425
|
}
|
|
23464
|
-
async respond(
|
|
23465
|
-
this.write({ id:
|
|
24426
|
+
async respond(id4, result) {
|
|
24427
|
+
this.write({ id: id4, result });
|
|
23466
24428
|
}
|
|
23467
|
-
async respondError(
|
|
23468
|
-
this.write({ id:
|
|
24429
|
+
async respondError(id4, code, message) {
|
|
24430
|
+
this.write({ id: id4, error: { code, message } });
|
|
23469
24431
|
}
|
|
23470
24432
|
onInbound(handler) {
|
|
23471
24433
|
this.inboundHandlers.add(handler);
|
|
@@ -23546,19 +24508,19 @@ var CodexAppServerClient = class {
|
|
|
23546
24508
|
}
|
|
23547
24509
|
}
|
|
23548
24510
|
request(method, params, timeoutMs) {
|
|
23549
|
-
const
|
|
24511
|
+
const id4 = this.nextId++;
|
|
23550
24512
|
const result = new Promise((resolve3, reject) => {
|
|
23551
24513
|
const timer = setTimeout(() => {
|
|
23552
|
-
this.pending.delete(
|
|
24514
|
+
this.pending.delete(id4);
|
|
23553
24515
|
reject(new CodexAppServerError("CODEX_REQUEST_TIMEOUT", "Codex App Server request timed out."));
|
|
23554
24516
|
}, timeoutMs);
|
|
23555
24517
|
timer.unref?.();
|
|
23556
|
-
this.pending.set(
|
|
24518
|
+
this.pending.set(id4, { resolve: resolve3, reject, timer });
|
|
23557
24519
|
});
|
|
23558
24520
|
try {
|
|
23559
|
-
this.write({ id:
|
|
24521
|
+
this.write({ id: id4, method, params });
|
|
23560
24522
|
} catch (error) {
|
|
23561
|
-
const pending = this.takePending(
|
|
24523
|
+
const pending = this.takePending(id4);
|
|
23562
24524
|
pending?.reject(error instanceof Error ? error : new Error("Codex App Server write failed."));
|
|
23563
24525
|
}
|
|
23564
24526
|
return result;
|
|
@@ -23629,15 +24591,15 @@ var CodexAppServerClient = class {
|
|
|
23629
24591
|
this.failureNotified = true;
|
|
23630
24592
|
for (const handler of this.unavailableHandlers) handler(code);
|
|
23631
24593
|
}
|
|
23632
|
-
takePending(
|
|
23633
|
-
const pending = this.pending.get(
|
|
24594
|
+
takePending(id4) {
|
|
24595
|
+
const pending = this.pending.get(id4);
|
|
23634
24596
|
if (pending === void 0) return void 0;
|
|
23635
|
-
this.pending.delete(
|
|
24597
|
+
this.pending.delete(id4);
|
|
23636
24598
|
clearTimeout(pending.timer);
|
|
23637
24599
|
return pending;
|
|
23638
24600
|
}
|
|
23639
24601
|
failPending(error) {
|
|
23640
|
-
for (const
|
|
24602
|
+
for (const id4 of [...this.pending.keys()]) this.takePending(id4)?.reject(error);
|
|
23641
24603
|
}
|
|
23642
24604
|
};
|
|
23643
24605
|
function safeUpstreamError(value) {
|
|
@@ -23651,7 +24613,7 @@ function isRecord10(value) {
|
|
|
23651
24613
|
}
|
|
23652
24614
|
|
|
23653
24615
|
// src/codex/method-policy.ts
|
|
23654
|
-
var
|
|
24616
|
+
var id3 = external_exports.string().min(1).max(256);
|
|
23655
24617
|
var cursor = external_exports.string().min(1).max(4096).nullable().optional();
|
|
23656
24618
|
var textInput = external_exports.object({
|
|
23657
24619
|
type: external_exports.literal("text"),
|
|
@@ -23700,9 +24662,9 @@ var schemas = {
|
|
|
23700
24662
|
useStateDbOnly: external_exports.boolean().optional(),
|
|
23701
24663
|
searchTerm: external_exports.string().max(1024).optional()
|
|
23702
24664
|
}).strict(),
|
|
23703
|
-
"thread/read": external_exports.object({ threadId:
|
|
24665
|
+
"thread/read": external_exports.object({ threadId: id3, includeTurns: external_exports.boolean().optional() }).strict(),
|
|
23704
24666
|
"dsh/sessionHistory": external_exports.object({
|
|
23705
|
-
threadId:
|
|
24667
|
+
threadId: id3,
|
|
23706
24668
|
beforeSeq: external_exports.number().int().nonnegative().optional(),
|
|
23707
24669
|
throughSeq: external_exports.number().int().min(-1).optional(),
|
|
23708
24670
|
maxMessages: external_exports.number().int().min(1).max(200).optional()
|
|
@@ -23717,21 +24679,21 @@ var schemas = {
|
|
|
23717
24679
|
permissionPreset: permissionPreset.optional()
|
|
23718
24680
|
}).strict(),
|
|
23719
24681
|
"thread/resume": external_exports.object({
|
|
23720
|
-
threadId:
|
|
24682
|
+
threadId: id3,
|
|
23721
24683
|
model: external_exports.string().min(1).max(128).optional(),
|
|
23722
24684
|
permissionPreset: permissionPreset.optional()
|
|
23723
24685
|
}).strict(),
|
|
23724
24686
|
"thread/fork": external_exports.object({
|
|
23725
|
-
threadId:
|
|
23726
|
-
lastTurnId:
|
|
24687
|
+
threadId: id3,
|
|
24688
|
+
lastTurnId: id3.optional(),
|
|
23727
24689
|
permissionPreset: permissionPreset.optional()
|
|
23728
24690
|
}).strict(),
|
|
23729
|
-
"thread/name/set": external_exports.object({ threadId:
|
|
23730
|
-
"thread/archive": external_exports.object({ threadId:
|
|
23731
|
-
"thread/unarchive": external_exports.object({ threadId:
|
|
23732
|
-
"thread/unsubscribe": external_exports.object({ threadId:
|
|
24691
|
+
"thread/name/set": external_exports.object({ threadId: id3, name: external_exports.string().trim().min(1).max(256) }).strict(),
|
|
24692
|
+
"thread/archive": external_exports.object({ threadId: id3 }).strict(),
|
|
24693
|
+
"thread/unarchive": external_exports.object({ threadId: id3 }).strict(),
|
|
24694
|
+
"thread/unsubscribe": external_exports.object({ threadId: id3 }).strict(),
|
|
23733
24695
|
"turn/start": external_exports.object({
|
|
23734
|
-
threadId:
|
|
24696
|
+
threadId: id3,
|
|
23735
24697
|
input,
|
|
23736
24698
|
model: external_exports.string().min(1).max(128).optional(),
|
|
23737
24699
|
effort: external_exports.enum(["none", "minimal", "low", "medium", "high", "xhigh", "max", "ultra"]).optional(),
|
|
@@ -23739,8 +24701,8 @@ var schemas = {
|
|
|
23739
24701
|
personality: external_exports.string().min(1).max(64).optional(),
|
|
23740
24702
|
permissionPreset: permissionPreset.optional()
|
|
23741
24703
|
}).strict(),
|
|
23742
|
-
"turn/steer": external_exports.object({ threadId:
|
|
23743
|
-
"turn/interrupt": external_exports.object({ threadId:
|
|
24704
|
+
"turn/steer": external_exports.object({ threadId: id3, input, expectedTurnId: id3 }).strict(),
|
|
24705
|
+
"turn/interrupt": external_exports.object({ threadId: id3, turnId: id3 }).strict()
|
|
23744
24706
|
};
|
|
23745
24707
|
var CODEX_APP_ALLOWLIST = Object.freeze(Object.keys(schemas));
|
|
23746
24708
|
function parseCodexCall(method, params) {
|
|
@@ -23958,14 +24920,14 @@ var CodexPeerBridge = class {
|
|
|
23958
24920
|
if (this.closed) return;
|
|
23959
24921
|
const streamIds = [...this.streams.entries()].filter(([, targetThreadId]) => targetThreadId === threadId).map(([streamId]) => streamId);
|
|
23960
24922
|
for (const streamId of streamIds) {
|
|
23961
|
-
const
|
|
23962
|
-
if (new TextEncoder().encode(JSON.stringify(
|
|
24923
|
+
const data2 = { streamId, frame };
|
|
24924
|
+
if (new TextEncoder().encode(JSON.stringify(data2)).byteLength > MAX_SECURE_MESSAGE_BYTES) {
|
|
23963
24925
|
this.streams.delete(streamId);
|
|
23964
24926
|
await this.publish("codex.app.stream.closed", { streamId, reason: "failed" });
|
|
23965
24927
|
this.logger?.warn("Codex stream closed after oversized frame", { streamId });
|
|
23966
24928
|
continue;
|
|
23967
24929
|
}
|
|
23968
|
-
await this.publish("codex.app.frame",
|
|
24930
|
+
await this.publish("codex.app.frame", data2);
|
|
23969
24931
|
}
|
|
23970
24932
|
}
|
|
23971
24933
|
async failStreams(reason = "failed") {
|
|
@@ -23994,11 +24956,11 @@ var CodexPeerBridge = class {
|
|
|
23994
24956
|
}
|
|
23995
24957
|
pruneTransfers() {
|
|
23996
24958
|
const staleBefore = Date.now() - TRANSFER_IDLE_MS;
|
|
23997
|
-
for (const [
|
|
23998
|
-
if (transfer.touchedAt < staleBefore) this.incomingTransfers.delete(
|
|
24959
|
+
for (const [id4, transfer] of this.incomingTransfers) {
|
|
24960
|
+
if (transfer.touchedAt < staleBefore) this.incomingTransfers.delete(id4);
|
|
23999
24961
|
}
|
|
24000
|
-
for (const [
|
|
24001
|
-
if (transfer.touchedAt < staleBefore) this.outgoingTransfers.delete(
|
|
24962
|
+
for (const [id4, transfer] of this.outgoingTransfers) {
|
|
24963
|
+
if (transfer.touchedAt < staleBefore) this.outgoingTransfers.delete(id4);
|
|
24002
24964
|
}
|
|
24003
24965
|
}
|
|
24004
24966
|
requireOpen() {
|
|
@@ -24465,7 +25427,7 @@ var CodexRemoteDomain = class {
|
|
|
24465
25427
|
await appServer.respond(message.id, { decision: "decline" });
|
|
24466
25428
|
return;
|
|
24467
25429
|
}
|
|
24468
|
-
const requestHandle =
|
|
25430
|
+
const requestHandle = randomUUID2();
|
|
24469
25431
|
this.approvals.set(requestHandle, {
|
|
24470
25432
|
upstreamId: message.id,
|
|
24471
25433
|
connectionId: owner.connectionId,
|
|
@@ -24773,14 +25735,14 @@ var CodexRemoteDomain = class {
|
|
|
24773
25735
|
}
|
|
24774
25736
|
}
|
|
24775
25737
|
async listCodexDirectory(path) {
|
|
24776
|
-
const
|
|
24777
|
-
const rows = await readdir2(
|
|
25738
|
+
const target2 = await this.resolveCodexDirectory(path);
|
|
25739
|
+
const rows = await readdir2(target2.path, { withFileTypes: true }).catch(() => void 0);
|
|
24778
25740
|
if (rows === void 0) {
|
|
24779
25741
|
throw new RpcError("CODEX_PATH_NOT_ALLOWED", "The CodeX working directory is not available as a Workspace.");
|
|
24780
25742
|
}
|
|
24781
25743
|
const entries = [];
|
|
24782
25744
|
for (const row of rows) {
|
|
24783
|
-
const child = resolve2(
|
|
25745
|
+
const child = resolve2(target2.path, row.name);
|
|
24784
25746
|
let directory = row.isDirectory();
|
|
24785
25747
|
if (!directory && row.isSymbolicLink()) {
|
|
24786
25748
|
directory = await stat4(child).then((value) => value.isDirectory()).catch(() => false);
|
|
@@ -24789,7 +25751,7 @@ var CodexRemoteDomain = class {
|
|
|
24789
25751
|
try {
|
|
24790
25752
|
const canonicalChild = await realpath(child);
|
|
24791
25753
|
if (!(await stat4(canonicalChild)).isDirectory()) continue;
|
|
24792
|
-
if (!containsCodexPath(
|
|
25754
|
+
if (!containsCodexPath(target2.canonicalRoot, canonicalChild)) continue;
|
|
24793
25755
|
} catch {
|
|
24794
25756
|
continue;
|
|
24795
25757
|
}
|
|
@@ -24801,9 +25763,9 @@ var CodexRemoteDomain = class {
|
|
|
24801
25763
|
}
|
|
24802
25764
|
entries.sort((left, right) => left.name.localeCompare(right.name, void 0, { sensitivity: "base" }));
|
|
24803
25765
|
return {
|
|
24804
|
-
path:
|
|
24805
|
-
home:
|
|
24806
|
-
crumbs: codexDirectoryCrumbs(
|
|
25766
|
+
path: target2.path,
|
|
25767
|
+
home: target2.root,
|
|
25768
|
+
crumbs: codexDirectoryCrumbs(target2.root, target2.path),
|
|
24807
25769
|
entries: entries.slice(0, CODEX_DIRECTORY_ENTRY_LIMIT),
|
|
24808
25770
|
truncated: entries.length > CODEX_DIRECTORY_ENTRY_LIMIT
|
|
24809
25771
|
};
|
|
@@ -24883,7 +25845,7 @@ function sanitizeThreadList(result) {
|
|
|
24883
25845
|
if (!isRecord12(result) || !Array.isArray(result.data)) {
|
|
24884
25846
|
throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned an invalid thread list.");
|
|
24885
25847
|
}
|
|
24886
|
-
const
|
|
25848
|
+
const data2 = result.data.flatMap((value) => {
|
|
24887
25849
|
if (!isRecord12(value) || typeof value.id !== "string") return [];
|
|
24888
25850
|
return [{
|
|
24889
25851
|
id: value.id,
|
|
@@ -24900,7 +25862,7 @@ function sanitizeThreadList(result) {
|
|
|
24900
25862
|
}];
|
|
24901
25863
|
});
|
|
24902
25864
|
return {
|
|
24903
|
-
data,
|
|
25865
|
+
data: data2,
|
|
24904
25866
|
...typeof result.nextCursor === "string" && result.nextCursor.length > 0 ? { nextCursor: result.nextCursor } : { nextCursor: null },
|
|
24905
25867
|
...typeof result.backwardsCursor === "string" && result.backwardsCursor.length > 0 ? { backwardsCursor: result.backwardsCursor } : {}
|
|
24906
25868
|
};
|
|
@@ -24934,9 +25896,9 @@ function sanitizeProjectList(result) {
|
|
|
24934
25896
|
if (!isRecord12(result) || !Array.isArray(result.data)) {
|
|
24935
25897
|
throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned an invalid project list.");
|
|
24936
25898
|
}
|
|
24937
|
-
const
|
|
25899
|
+
const data2 = result.data.flatMap((value) => sanitizeProject(value) ?? []);
|
|
24938
25900
|
return {
|
|
24939
|
-
data,
|
|
25901
|
+
data: data2,
|
|
24940
25902
|
...typeof result.nextCursor === "string" && result.nextCursor.length > 0 ? { nextCursor: result.nextCursor } : { nextCursor: null }
|
|
24941
25903
|
};
|
|
24942
25904
|
}
|
|
@@ -25163,8 +26125,8 @@ var StdioAcpAdapter = class {
|
|
|
25163
26125
|
}
|
|
25164
26126
|
call(method, params, onNotification) {
|
|
25165
26127
|
const c = this.ensure();
|
|
25166
|
-
const
|
|
25167
|
-
c.stdin.write(JSON.stringify({ jsonrpc: "2.0", id:
|
|
26128
|
+
const id4 = this.nextId++;
|
|
26129
|
+
c.stdin.write(JSON.stringify({ jsonrpc: "2.0", id: id4, method, params }) + "\n");
|
|
25168
26130
|
return new Promise((resolve3, reject) => {
|
|
25169
26131
|
let buf = "";
|
|
25170
26132
|
const onData = async (d) => {
|
|
@@ -25174,7 +26136,7 @@ var StdioAcpAdapter = class {
|
|
|
25174
26136
|
for (const l of lines) {
|
|
25175
26137
|
try {
|
|
25176
26138
|
const m = JSON.parse(l);
|
|
25177
|
-
if (m.id ===
|
|
26139
|
+
if (m.id === id4) {
|
|
25178
26140
|
c.stdout.off("data", onData);
|
|
25179
26141
|
m.error ? reject(new Error(m.error.message ?? "ACP error")) : resolve3(m.result);
|
|
25180
26142
|
} else if (m.method && onNotification) await onNotification(m.params);
|
|
@@ -25198,11 +26160,13 @@ var HostPluginRuntime = class {
|
|
|
25198
26160
|
this.logger = logger;
|
|
25199
26161
|
this.localGateway = localGateway;
|
|
25200
26162
|
this.fileViewerHost = fileViewerHost;
|
|
26163
|
+
this.terminalEnabled = config.terminal.enabled;
|
|
26164
|
+
this.loopback = new LoopbackHost(config.loopback.ports);
|
|
25201
26165
|
this.codex = new CodexRemoteDomain(config.codex, logger);
|
|
25202
26166
|
this.connections = new ConnectionController(this.identities, (context, send) => {
|
|
25203
26167
|
const harnessApi = this.apiProxy === void 0 ? void 0 : new HarnessApiBridge(
|
|
25204
26168
|
this.apiProxy,
|
|
25205
|
-
(event,
|
|
26169
|
+
(event, data2) => send(createEvent(event, data2)),
|
|
25206
26170
|
void 0,
|
|
25207
26171
|
this.logger,
|
|
25208
26172
|
this.localGateway,
|
|
@@ -25210,9 +26174,10 @@ var HostPluginRuntime = class {
|
|
|
25210
26174
|
);
|
|
25211
26175
|
const harnessRemote = this.localGateway?.supportsCarrier === true ? new HarnessRemoteBridge(
|
|
25212
26176
|
this.localGateway,
|
|
25213
|
-
(event,
|
|
26177
|
+
(event, data2) => send(createEvent(event, data2)),
|
|
25214
26178
|
this.logger,
|
|
25215
|
-
this.harnessVersion
|
|
26179
|
+
this.harnessVersion,
|
|
26180
|
+
new TerminalPolicy(() => this.terminalEnabled, context.peerDeviceId, this.terminalOwners)
|
|
25216
26181
|
) : void 0;
|
|
25217
26182
|
const fileViewer = new RemoteFileViewerBridge(
|
|
25218
26183
|
() => this.fileViewerHost?.(),
|
|
@@ -25220,7 +26185,7 @@ var HostPluginRuntime = class {
|
|
|
25220
26185
|
);
|
|
25221
26186
|
const codex = this.codex.createPeer(
|
|
25222
26187
|
context,
|
|
25223
|
-
(event,
|
|
26188
|
+
(event, data2) => send(createEvent(event, data2))
|
|
25224
26189
|
);
|
|
25225
26190
|
const adapters = (config.acp?.backends ?? []).filter((item) => item.enabled && this.acpAvailable(item.command)).map((item) => new StdioAcpAdapter(item));
|
|
25226
26191
|
const acp = config.acp?.enabled && adapters.length > 0 ? new AcpGateway(adapters) : void 0;
|
|
@@ -25232,7 +26197,8 @@ var HostPluginRuntime = class {
|
|
|
25232
26197
|
harnessRemote,
|
|
25233
26198
|
() => this.hostCapabilities(),
|
|
25234
26199
|
codex,
|
|
25235
|
-
acp
|
|
26200
|
+
acp,
|
|
26201
|
+
this.loopback
|
|
25236
26202
|
);
|
|
25237
26203
|
}, this.logger);
|
|
25238
26204
|
if (config.serverUrl !== void 0) {
|
|
@@ -25240,6 +26206,9 @@ var HostPluginRuntime = class {
|
|
|
25240
26206
|
}
|
|
25241
26207
|
}
|
|
25242
26208
|
connections;
|
|
26209
|
+
terminalOwners = /* @__PURE__ */ new Map();
|
|
26210
|
+
terminalEnabled;
|
|
26211
|
+
loopback;
|
|
25243
26212
|
identity;
|
|
25244
26213
|
serverApi;
|
|
25245
26214
|
serverConnection;
|
|
@@ -25248,6 +26217,12 @@ var HostPluginRuntime = class {
|
|
|
25248
26217
|
codex;
|
|
25249
26218
|
localCodexPeer;
|
|
25250
26219
|
localCodexPublish = async () => void 0;
|
|
26220
|
+
setTerminalEnabled(enabled) {
|
|
26221
|
+
this.terminalEnabled = enabled;
|
|
26222
|
+
}
|
|
26223
|
+
setLoopbackPorts(ports) {
|
|
26224
|
+
this.loopback.setPorts(ports);
|
|
26225
|
+
}
|
|
25251
26226
|
async start() {
|
|
25252
26227
|
if (this.closed) throw new Error("remote runtime is closed");
|
|
25253
26228
|
this.identity = await this.identities.loadOrCreate(this.config.deviceName);
|
|
@@ -25455,7 +26430,7 @@ var HostPluginRuntime = class {
|
|
|
25455
26430
|
let reportedVersion;
|
|
25456
26431
|
let errorCode5;
|
|
25457
26432
|
try {
|
|
25458
|
-
const response = await this.apiProxy?.host.describe({ rpcId:
|
|
26433
|
+
const response = await this.apiProxy?.host.describe({ rpcId: randomUUID3(), payload: {} });
|
|
25459
26434
|
if (response === void 0) throw new Error("ApiProxy is unavailable");
|
|
25460
26435
|
if (!response.result.ok) {
|
|
25461
26436
|
errorCode5 = response.result.error.code;
|
|
@@ -25472,12 +26447,14 @@ var HostPluginRuntime = class {
|
|
|
25472
26447
|
}
|
|
25473
26448
|
hostCapabilities() {
|
|
25474
26449
|
const capabilities = [];
|
|
26450
|
+
if (this.loopback.hasPorts()) capabilities.push("loopback.http-ws.v1");
|
|
25475
26451
|
if (this.localGateway?.supportsCarrier === true) {
|
|
25476
26452
|
capabilities.push(
|
|
25477
26453
|
harnessSessionGeneration(this.harnessVersion) === "v3" ? "harness.remote.v3" : "harness.remote.v1",
|
|
25478
26454
|
"harness.remote.transfer.v1"
|
|
25479
26455
|
);
|
|
25480
26456
|
}
|
|
26457
|
+
if (this.localGateway?.supportsCarrier && this.terminalEnabled) capabilities.push("harness.terminal.v1");
|
|
25481
26458
|
if (this.apiProxy !== void 0) {
|
|
25482
26459
|
capabilities.push("harness.api.v1", "harness.api.transfer.v1");
|
|
25483
26460
|
}
|
|
@@ -25505,7 +26482,7 @@ var HostPluginRuntime = class {
|
|
|
25505
26482
|
const peer = this.codex.createPeer({
|
|
25506
26483
|
connectionId: `loopback:${identity.deviceId}`,
|
|
25507
26484
|
peerDeviceId: identity.deviceId
|
|
25508
|
-
}, (event,
|
|
26485
|
+
}, (event, data2) => this.localCodexPublish(event, data2));
|
|
25509
26486
|
if (peer === void 0) {
|
|
25510
26487
|
throw new RpcError("CODEX_UNAVAILABLE", "Local CodeX is disabled or unavailable on this Host.");
|
|
25511
26488
|
}
|
|
@@ -25763,11 +26740,11 @@ function helpText() {
|
|
|
25763
26740
|
""
|
|
25764
26741
|
].join("\n");
|
|
25765
26742
|
}
|
|
25766
|
-
function write(
|
|
25767
|
-
|
|
26743
|
+
function write(target2, value) {
|
|
26744
|
+
target2.write(value);
|
|
25768
26745
|
}
|
|
25769
|
-
function terminalLink(url,
|
|
25770
|
-
if (
|
|
26746
|
+
function terminalLink(url, target2) {
|
|
26747
|
+
if (target2.isTTY !== true) return url;
|
|
25771
26748
|
return `\x1B]8;;${url}\x07${url}\x1B]8;;\x07`;
|
|
25772
26749
|
}
|
|
25773
26750
|
async function exists3(path) {
|
|
@@ -25828,10 +26805,10 @@ function installTuiRemoteCommand(ctx, resolveTarget) {
|
|
|
25828
26805
|
return { kind: "success" };
|
|
25829
26806
|
}
|
|
25830
26807
|
if (command === "logout" && args.length === 1) {
|
|
25831
|
-
const
|
|
25832
|
-
if (
|
|
26808
|
+
const target2 = resolveTarget();
|
|
26809
|
+
if (target2 === void 0) return remoteNotReady();
|
|
25833
26810
|
try {
|
|
25834
|
-
await
|
|
26811
|
+
await target2.runtime.clearHostAuthorization();
|
|
25835
26812
|
return {
|
|
25836
26813
|
kind: "success",
|
|
25837
26814
|
text: "Remote Host logged out and its local device identity was rotated."
|
|
@@ -25928,11 +26905,11 @@ function remoteCommandChildren(canonicalPath) {
|
|
|
25928
26905
|
}
|
|
25929
26906
|
return [];
|
|
25930
26907
|
}
|
|
25931
|
-
function remoteStatusLines(
|
|
25932
|
-
if (
|
|
26908
|
+
function remoteStatusLines(target2) {
|
|
26909
|
+
if (target2 === void 0) {
|
|
25933
26910
|
return ["Remote Host is disabled or still starting. Check the ds-harness-remote settings and retry."];
|
|
25934
26911
|
}
|
|
25935
|
-
const { runtime, config } =
|
|
26912
|
+
const { runtime, config } = target2;
|
|
25936
26913
|
const status2 = runtime.hostStatus();
|
|
25937
26914
|
const diagnostics = runtime.diagnostics();
|
|
25938
26915
|
const codex = runtime.codexStatus();
|
|
@@ -25944,6 +26921,8 @@ function remoteStatusLines(target) {
|
|
|
25944
26921
|
`Device: ${status2.deviceId ?? "not initialized"}`,
|
|
25945
26922
|
`Authorization: ${status2.authorized ? status2.account === void 0 ? "logged in" : `logged in (${status2.account})` : "logged out"}`,
|
|
25946
26923
|
`Server connection: ${connection}`,
|
|
26924
|
+
...status2.error === void 0 ? [] : [`Connection error: ${status2.error}`],
|
|
26925
|
+
...status2.error === "CONNECTION_REPLACED" ? ["Another instance is using this Host identity. Stop it or use a separate DSH_HOME before reconnecting."] : status2.error === "SERVER_CREDENTIALS_BUSY" ? ["Credential refresh is locked. Stop all instances before removing an orphaned server-credentials.json.refresh-lock and authorizing again."] : status2.accountRequired ? ["Authorize again with /remote login [github|zhihu]."] : [],
|
|
25947
26926
|
`Harness Remote API: ${capabilities.has("harness.api.v1") ? "available (ApiProxy)" : capabilities.has("harness.remote.v3") ? "available (Typert Remote Session V3)" : capabilities.has("harness.remote.v1") ? "available (Typert Remote)" : "unavailable"}`,
|
|
25948
26927
|
`Remote clients: ${diagnostics.activeConnections}`,
|
|
25949
26928
|
`Codex Remote: ${codex.enabled ? codex.state : "disabled"}`,
|
|
@@ -25951,8 +26930,8 @@ function remoteStatusLines(target) {
|
|
|
25951
26930
|
"Commands: /remote login [github|zhihu] \xB7 /remote status \xB7 /remote logout"
|
|
25952
26931
|
];
|
|
25953
26932
|
}
|
|
25954
|
-
function formatRemoteStatusInline(
|
|
25955
|
-
return remoteStatusLines(
|
|
26933
|
+
function formatRemoteStatusInline(target2) {
|
|
26934
|
+
return remoteStatusLines(target2).filter(Boolean).join(" \xB7 ");
|
|
25956
26935
|
}
|
|
25957
26936
|
function remoteCommandUsage() {
|
|
25958
26937
|
return [
|
|
@@ -25993,16 +26972,16 @@ var RemoteLoginController = class {
|
|
|
25993
26972
|
}
|
|
25994
26973
|
async run(attempt, provider) {
|
|
25995
26974
|
try {
|
|
25996
|
-
const
|
|
25997
|
-
if (
|
|
25998
|
-
const session = await
|
|
26975
|
+
const target2 = this.resolveTarget();
|
|
26976
|
+
if (target2 === void 0) throw new Error("REMOTE_NOT_READY");
|
|
26977
|
+
const session = await target2.runtime.startHostOAuthQrLogin(provider);
|
|
25999
26978
|
const [qr, compactQr] = await Promise.all([
|
|
26000
26979
|
renderTerminalQr(session.scanUrl),
|
|
26001
26980
|
renderCompactTerminalQr(session.scanUrl)
|
|
26002
26981
|
]);
|
|
26003
26982
|
if (attempt !== this.attempt) return;
|
|
26004
26983
|
this.update({ phase: "waiting", provider, qr, compactQr, scanUrl: session.scanUrl });
|
|
26005
|
-
await this.poll(attempt, provider, session,
|
|
26984
|
+
await this.poll(attempt, provider, session, target2.runtime);
|
|
26006
26985
|
} catch (error) {
|
|
26007
26986
|
if (attempt !== this.attempt) return;
|
|
26008
26987
|
this.update({ phase: "error", provider, error: errorCode4(error) });
|