ds-harness-remote 0.4.13 → 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/acp.d.ts +75 -0
- package/dist/acp.d.ts.map +1 -0
- package/dist/acp.js +61 -0
- package/dist/acp.js.map +1 -0
- package/dist/client-runtime.d.ts +11 -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 +474 -76
- package/dist/client.js +474 -76
- package/dist/client.js.map +1 -1
- package/dist/codex/method-policy.d.ts +10 -10
- package/dist/config.d.ts +36 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +9 -0
- package/dist/config.js.map +1 -1
- package/dist/connection-controller.d.ts +5 -0
- package/dist/connection-controller.d.ts.map +1 -1
- package/dist/connection-controller.js +16 -1
- package/dist/connection-controller.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 +12 -1
- package/dist/control-runtime.d.ts.map +1 -1
- package/dist/control-runtime.js +95 -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.d.ts.map +1 -1
- package/dist/harness-api-bridge.js +32 -8
- package/dist/harness-api-bridge.js.map +1 -1
- package/dist/harness-remote-bridge.d.ts +5 -2
- package/dist/harness-remote-bridge.d.ts.map +1 -1
- package/dist/harness-remote-bridge.js +85 -5
- package/dist/harness-remote-bridge.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1872 -588
- 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 +7 -2
- package/dist/rpc-router.d.ts.map +1 -1
- package/dist/rpc-router.js +27 -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 +14 -0
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +48 -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 +44 -24
- 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;
|
|
@@ -4066,6 +4093,8 @@ var MAX_RPC_TEXT_INPUT_BYTES = 64 * 1024;
|
|
|
4066
4093
|
var MAX_FILEVIEWER_RANGE_BYTES = 512 * 1024;
|
|
4067
4094
|
var MIN_REPLAY_WINDOW_MS = 15 * 6e4;
|
|
4068
4095
|
var MAX_ALPHA_STREAMS_PER_CONNECTION = 16;
|
|
4096
|
+
var MAX_ACP_PROMPT_BYTES = 64 * 1024;
|
|
4097
|
+
var MAX_ACP_UPDATE_BYTES = 512 * 1024;
|
|
4069
4098
|
var SECURE_FRAGMENT_MAGIC = new Uint8Array([68, 83, 72, 70]);
|
|
4070
4099
|
var SECURE_FRAGMENT_VERSION = 1;
|
|
4071
4100
|
var SECURE_FRAGMENT_HEADER_BYTES = 17;
|
|
@@ -4112,6 +4141,7 @@ var rpcMethods = [
|
|
|
4112
4141
|
"harness.remote.transfer.close",
|
|
4113
4142
|
"harness.remote.stream.open",
|
|
4114
4143
|
"harness.remote.stream.close",
|
|
4144
|
+
"loopback.call",
|
|
4115
4145
|
"fileviewer.call",
|
|
4116
4146
|
"codex.app.call",
|
|
4117
4147
|
"codex.app.respond",
|
|
@@ -4121,7 +4151,14 @@ var rpcMethods = [
|
|
|
4121
4151
|
"codex.app.transfer.chunk",
|
|
4122
4152
|
"codex.app.transfer.commit",
|
|
4123
4153
|
"codex.app.transfer.read",
|
|
4124
|
-
"codex.app.transfer.close"
|
|
4154
|
+
"codex.app.transfer.close",
|
|
4155
|
+
"acp.initialize",
|
|
4156
|
+
"acp.session.new",
|
|
4157
|
+
"acp.session.load",
|
|
4158
|
+
"acp.session.prompt",
|
|
4159
|
+
"acp.session.respond_permission",
|
|
4160
|
+
"acp.session.cancel",
|
|
4161
|
+
"acp.session.set_mode"
|
|
4125
4162
|
];
|
|
4126
4163
|
var errorCodes = [
|
|
4127
4164
|
// Protocol / Version
|
|
@@ -4395,26 +4432,26 @@ var rpcErrorPayloadSchema = external_exports.object({
|
|
|
4395
4432
|
retryable: external_exports.boolean().optional(),
|
|
4396
4433
|
details: external_exports.unknown().optional()
|
|
4397
4434
|
});
|
|
4398
|
-
function createMessage(type, payload,
|
|
4435
|
+
function createMessage(type, payload, id4 = cryptoRandomId()) {
|
|
4399
4436
|
return {
|
|
4400
4437
|
v: PROTOCOL_VERSION,
|
|
4401
|
-
id:
|
|
4438
|
+
id: id4,
|
|
4402
4439
|
type,
|
|
4403
4440
|
timestamp: Date.now(),
|
|
4404
4441
|
payload
|
|
4405
4442
|
};
|
|
4406
4443
|
}
|
|
4407
|
-
function createControlFrame(type, payload,
|
|
4444
|
+
function createControlFrame(type, payload, id4 = cryptoRandomId()) {
|
|
4408
4445
|
return {
|
|
4409
4446
|
v: PROTOCOL_VERSION,
|
|
4410
|
-
id:
|
|
4447
|
+
id: id4,
|
|
4411
4448
|
type,
|
|
4412
4449
|
timestamp: Date.now(),
|
|
4413
4450
|
payload
|
|
4414
4451
|
};
|
|
4415
4452
|
}
|
|
4416
|
-
function createRpcRequest(method, params,
|
|
4417
|
-
return createMessage("rpc.request", { method, params },
|
|
4453
|
+
function createRpcRequest(method, params, id4) {
|
|
4454
|
+
return createMessage("rpc.request", { method, params }, id4);
|
|
4418
4455
|
}
|
|
4419
4456
|
function createRpcResponse(requestId, result) {
|
|
4420
4457
|
return createMessage("rpc.response", { requestId, result });
|
|
@@ -4422,8 +4459,8 @@ function createRpcResponse(requestId, result) {
|
|
|
4422
4459
|
function createRpcError(requestId, code, message, details, retryable) {
|
|
4423
4460
|
return createMessage("rpc.error", { requestId, code, message, details, retryable });
|
|
4424
4461
|
}
|
|
4425
|
-
function createEvent(event,
|
|
4426
|
-
return createMessage("event", { event, data, ...options });
|
|
4462
|
+
function createEvent(event, data2, options = {}) {
|
|
4463
|
+
return createMessage("event", { event, data: data2, ...options });
|
|
4427
4464
|
}
|
|
4428
4465
|
function parseRemoteMessage(input2) {
|
|
4429
4466
|
return remoteMessageSchema.parse(input2);
|
|
@@ -4442,11 +4479,11 @@ function encodeControlFrame(frame, limits = {}) {
|
|
|
4442
4479
|
assertControlFrameSize(new TextEncoder().encode(encoded).byteLength, parsed.type, limits);
|
|
4443
4480
|
return encoded;
|
|
4444
4481
|
}
|
|
4445
|
-
function decodeControlFrame(
|
|
4446
|
-
const encoded = typeof
|
|
4482
|
+
function decodeControlFrame(data2, limits = {}) {
|
|
4483
|
+
const encoded = typeof data2 === "string" ? new TextEncoder().encode(data2) : data2;
|
|
4447
4484
|
if (encoded.byteLength > MAX_RELAY_FRAME_BYTES)
|
|
4448
4485
|
throw new Error("Control frame exceeds the Relay frame limit.");
|
|
4449
|
-
const text = typeof
|
|
4486
|
+
const text = typeof data2 === "string" ? data2 : new TextDecoder("utf-8", { fatal: true }).decode(data2);
|
|
4450
4487
|
const frame = parseControlFrame(JSON.parse(text));
|
|
4451
4488
|
assertControlFrameSize(encoded.byteLength, frame.type, limits);
|
|
4452
4489
|
return frame;
|
|
@@ -4454,8 +4491,8 @@ function decodeControlFrame(data, limits = {}) {
|
|
|
4454
4491
|
function encodeMessage(message) {
|
|
4455
4492
|
return new TextEncoder().encode(JSON.stringify(message));
|
|
4456
4493
|
}
|
|
4457
|
-
function decodeMessage(
|
|
4458
|
-
const text = typeof
|
|
4494
|
+
function decodeMessage(data2) {
|
|
4495
|
+
const text = typeof data2 === "string" ? data2 : new TextDecoder().decode(data2);
|
|
4459
4496
|
return parseRemoteMessage(JSON.parse(text));
|
|
4460
4497
|
}
|
|
4461
4498
|
var SecureMessageCodec = class {
|
|
@@ -4562,6 +4599,504 @@ function cryptoRandomId() {
|
|
|
4562
4599
|
return `msg_${Date.now().toString(36)}_${Math.random().toString(36).slice(2)}`;
|
|
4563
4600
|
}
|
|
4564
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
|
+
|
|
4565
5100
|
// ../client-core/dist/remote-gateway.js
|
|
4566
5101
|
var DIRECT_REMOTE_CALL_BYTES = 2 * 1024 * 1024;
|
|
4567
5102
|
var RemoteGatewayError = class extends Error {
|
|
@@ -4778,12 +5313,12 @@ function deriveCodexCwdWorkspaces(values) {
|
|
|
4778
5313
|
continue;
|
|
4779
5314
|
}
|
|
4780
5315
|
const baseId = `cwd-${hashWorkspacePath(key)}`;
|
|
4781
|
-
let
|
|
4782
|
-
for (let suffix = 2; usedIds.has(
|
|
4783
|
-
|
|
4784
|
-
usedIds.add(
|
|
5316
|
+
let id4 = baseId;
|
|
5317
|
+
for (let suffix = 2; usedIds.has(id4); suffix += 1)
|
|
5318
|
+
id4 = `${baseId}-${suffix}`;
|
|
5319
|
+
usedIds.add(id4);
|
|
4785
5320
|
byPath.set(key, {
|
|
4786
|
-
id:
|
|
5321
|
+
id: id4,
|
|
4787
5322
|
name: workspaceBasename(path),
|
|
4788
5323
|
path,
|
|
4789
5324
|
position: byPath.size,
|
|
@@ -4957,16 +5492,16 @@ function parseDataImageUrl(value) {
|
|
|
4957
5492
|
if (match === null)
|
|
4958
5493
|
return void 0;
|
|
4959
5494
|
const mediaType = match[1];
|
|
4960
|
-
const
|
|
4961
|
-
if (!CODEX_IMAGE_MEDIA_TYPES.has(mediaType) || !isCanonicalBase64(
|
|
5495
|
+
const data2 = match[2];
|
|
5496
|
+
if (!CODEX_IMAGE_MEDIA_TYPES.has(mediaType) || !isCanonicalBase64(data2))
|
|
4962
5497
|
return void 0;
|
|
4963
|
-
return { url: `data:${mediaType};base64,${
|
|
5498
|
+
return { url: `data:${mediaType};base64,${data2}`, mediaType, data: data2 };
|
|
4964
5499
|
}
|
|
4965
5500
|
function isCanonicalBase64(value) {
|
|
4966
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);
|
|
4967
5502
|
}
|
|
4968
|
-
function sniffImageMediaType(
|
|
4969
|
-
const bytes = base64PrefixBytes(
|
|
5503
|
+
function sniffImageMediaType(data2) {
|
|
5504
|
+
const bytes = base64PrefixBytes(data2, 32);
|
|
4970
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)
|
|
4971
5506
|
return "image/png";
|
|
4972
5507
|
if (bytes.length >= 3 && bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255)
|
|
@@ -5172,7 +5707,7 @@ var RemoteClientCore = class {
|
|
|
5172
5707
|
if (this.unsubscribeTransport !== void 0)
|
|
5173
5708
|
return;
|
|
5174
5709
|
this.closeNotified = false;
|
|
5175
|
-
this.unsubscribeTransport = this.transport.onMessage((
|
|
5710
|
+
this.unsubscribeTransport = this.transport.onMessage((data2) => this.handleMessage(data2));
|
|
5176
5711
|
this.unsubscribeClose = this.transport.onClose?.(() => this.handleTransportClose());
|
|
5177
5712
|
try {
|
|
5178
5713
|
await this.transport.connect();
|
|
@@ -5243,8 +5778,8 @@ var RemoteClientCore = class {
|
|
|
5243
5778
|
this.rejectAllPending((pending) => new RemoteClientError("TRANSPORT_CLOSED", `RPC ${pending.method} terminated because the remote transport closed`));
|
|
5244
5779
|
this.notifyClose();
|
|
5245
5780
|
}
|
|
5246
|
-
handleMessage(
|
|
5247
|
-
const message = decodeMessage(
|
|
5781
|
+
handleMessage(data2) {
|
|
5782
|
+
const message = decodeMessage(data2);
|
|
5248
5783
|
if (message.type === "rpc.response")
|
|
5249
5784
|
this.handleResponse(message);
|
|
5250
5785
|
if (message.type === "rpc.error")
|
|
@@ -5305,6 +5840,18 @@ function transportSendError(error) {
|
|
|
5305
5840
|
return error instanceof Error ? error : new Error("remote transport send failed", { cause: error });
|
|
5306
5841
|
}
|
|
5307
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
|
+
|
|
5308
5855
|
// ../webrtc/dist/transport.js
|
|
5309
5856
|
var BaseTransport = class {
|
|
5310
5857
|
handlers = /* @__PURE__ */ new Set();
|
|
@@ -5317,9 +5864,9 @@ var BaseTransport = class {
|
|
|
5317
5864
|
this.closeHandlers.add(cb);
|
|
5318
5865
|
return () => this.closeHandlers.delete(cb);
|
|
5319
5866
|
}
|
|
5320
|
-
emit(
|
|
5867
|
+
emit(data2) {
|
|
5321
5868
|
for (const handler of this.handlers)
|
|
5322
|
-
handler(
|
|
5869
|
+
handler(data2);
|
|
5323
5870
|
}
|
|
5324
5871
|
emitClose() {
|
|
5325
5872
|
for (const handler of this.closeHandlers)
|
|
@@ -5562,8 +6109,8 @@ function adaptDataChannel(raw) {
|
|
|
5562
6109
|
get onbufferedamountlow() {
|
|
5563
6110
|
return raw.onbufferedamountlow;
|
|
5564
6111
|
},
|
|
5565
|
-
send(
|
|
5566
|
-
raw.send(
|
|
6112
|
+
send(data2) {
|
|
6113
|
+
raw.send(data2);
|
|
5567
6114
|
},
|
|
5568
6115
|
close() {
|
|
5569
6116
|
raw.close();
|
|
@@ -5582,19 +6129,19 @@ var MAX_ASSEMBLY_AGE_MS = 3e4;
|
|
|
5582
6129
|
var RtcChunkCodec = class {
|
|
5583
6130
|
nextMessageId = 1;
|
|
5584
6131
|
assemblies = /* @__PURE__ */ new Map();
|
|
5585
|
-
encode(
|
|
5586
|
-
if (
|
|
5587
|
-
return [
|
|
5588
|
-
if (
|
|
6132
|
+
encode(data2) {
|
|
6133
|
+
if (data2.byteLength <= RTC_CHUNK_PAYLOAD_BYTES)
|
|
6134
|
+
return [data2];
|
|
6135
|
+
if (data2.byteLength > RTC_CHUNK_MAX_MESSAGE_BYTES) {
|
|
5589
6136
|
throw new Error("WebRTC transport message exceeds the reassembly limit.");
|
|
5590
6137
|
}
|
|
5591
6138
|
const messageId = this.nextMessageId;
|
|
5592
6139
|
this.nextMessageId = messageId === 4294967295 ? 1 : messageId + 1;
|
|
5593
|
-
const total = Math.ceil(
|
|
6140
|
+
const total = Math.ceil(data2.byteLength / RTC_CHUNK_PAYLOAD_BYTES);
|
|
5594
6141
|
const frames = [];
|
|
5595
6142
|
for (let index = 0; index < total; index += 1) {
|
|
5596
6143
|
const start = index * RTC_CHUNK_PAYLOAD_BYTES;
|
|
5597
|
-
const chunk =
|
|
6144
|
+
const chunk = data2.subarray(start, Math.min(data2.byteLength, start + RTC_CHUNK_PAYLOAD_BYTES));
|
|
5598
6145
|
const frame = new Uint8Array(RTC_CHUNK_HEADER_BYTES + chunk.byteLength);
|
|
5599
6146
|
frame.set(RTC_CHUNK_MAGIC);
|
|
5600
6147
|
const view = new DataView(frame.buffer);
|
|
@@ -5780,11 +6327,11 @@ var RtcDataChannelTransport = class {
|
|
|
5780
6327
|
else
|
|
5781
6328
|
void this.handleIce(signal.candidate);
|
|
5782
6329
|
}
|
|
5783
|
-
async send(
|
|
6330
|
+
async send(data2) {
|
|
5784
6331
|
const channel = this.requireOpenChannel();
|
|
5785
6332
|
if (channel.readyState !== "open")
|
|
5786
6333
|
throw new Error("WebRTC data channel is not open.");
|
|
5787
|
-
for (const frame of this.outgoing.encode(
|
|
6334
|
+
for (const frame of this.outgoing.encode(data2)) {
|
|
5788
6335
|
try {
|
|
5789
6336
|
channel.send(toArrayBuffer(frame));
|
|
5790
6337
|
} catch (error) {
|
|
@@ -5792,7 +6339,7 @@ var RtcDataChannelTransport = class {
|
|
|
5792
6339
|
throw error;
|
|
5793
6340
|
}
|
|
5794
6341
|
}
|
|
5795
|
-
this.bytesSent +=
|
|
6342
|
+
this.bytesSent += data2.byteLength;
|
|
5796
6343
|
this.armWatchdog(channel);
|
|
5797
6344
|
}
|
|
5798
6345
|
onMessage(handler) {
|
|
@@ -5981,8 +6528,8 @@ var RtcDataChannelTransport = class {
|
|
|
5981
6528
|
channel.onmessage = (event) => {
|
|
5982
6529
|
if (this.closed || !this.opened)
|
|
5983
6530
|
return;
|
|
5984
|
-
const
|
|
5985
|
-
const frame = typeof
|
|
6531
|
+
const data2 = event.data;
|
|
6532
|
+
const frame = typeof data2 === "string" ? new TextEncoder().encode(data2) : new Uint8Array(data2);
|
|
5986
6533
|
this.bytesReceived += frame.byteLength;
|
|
5987
6534
|
try {
|
|
5988
6535
|
const message = this.incoming.decode(frame);
|
|
@@ -6173,7 +6720,7 @@ function inspectSelectedPath(stats) {
|
|
|
6173
6720
|
}
|
|
6174
6721
|
}
|
|
6175
6722
|
const pairs = [
|
|
6176
|
-
...[...selectedPairIds].map((
|
|
6723
|
+
...[...selectedPairIds].map((id4) => candidatePairs.get(id4)).filter((pair) => pair !== void 0),
|
|
6177
6724
|
...selectedPairs
|
|
6178
6725
|
];
|
|
6179
6726
|
for (const pair of pairs) {
|
|
@@ -6361,10 +6908,10 @@ function requireSdp(description) {
|
|
|
6361
6908
|
}
|
|
6362
6909
|
return description.sdp;
|
|
6363
6910
|
}
|
|
6364
|
-
function toArrayBuffer(
|
|
6365
|
-
if (
|
|
6366
|
-
return
|
|
6367
|
-
return
|
|
6911
|
+
function toArrayBuffer(data2) {
|
|
6912
|
+
if (data2.byteOffset === 0 && data2.byteLength === data2.buffer.byteLength)
|
|
6913
|
+
return data2.buffer;
|
|
6914
|
+
return data2.slice().buffer;
|
|
6368
6915
|
}
|
|
6369
6916
|
function asError(error) {
|
|
6370
6917
|
return error instanceof Error ? error : new RtcConnectError("RTC_FAILED", "WebRTC negotiation failed.");
|
|
@@ -6436,26 +6983,27 @@ var AdaptiveTransport = class extends BaseTransport {
|
|
|
6436
6983
|
};
|
|
6437
6984
|
});
|
|
6438
6985
|
}
|
|
6439
|
-
async send(
|
|
6986
|
+
async send(data2) {
|
|
6440
6987
|
if (this.connectionId === void 0)
|
|
6441
6988
|
throw new Error("adaptive transport has not been authorized");
|
|
6442
6989
|
if (this.dataMode === "webrtc") {
|
|
6443
6990
|
const rtc = this.rtc;
|
|
6444
6991
|
if (rtc === void 0)
|
|
6445
6992
|
throw new Error("webrtc data channel is not available");
|
|
6446
|
-
this.bytesSent +=
|
|
6447
|
-
await rtc.send(
|
|
6993
|
+
this.bytesSent += data2.byteLength;
|
|
6994
|
+
await rtc.send(data2);
|
|
6448
6995
|
return;
|
|
6449
6996
|
}
|
|
6450
6997
|
if (this.socket?.readyState !== WebSocket.OPEN)
|
|
6451
6998
|
throw new Error("adaptive transport is not connected");
|
|
6999
|
+
await waitForRelayCapacity(this.socket);
|
|
6452
7000
|
this.sendControl("relay", {
|
|
6453
7001
|
connectionId: this.connectionId,
|
|
6454
7002
|
targetDeviceId: this.options.targetDeviceId,
|
|
6455
7003
|
counter: this.relayCounter,
|
|
6456
|
-
ciphertext: toBase64Url(
|
|
7004
|
+
ciphertext: toBase64Url(data2)
|
|
6457
7005
|
});
|
|
6458
|
-
this.bytesSent +=
|
|
7006
|
+
this.bytesSent += data2.byteLength;
|
|
6459
7007
|
this.relayCounter += 1;
|
|
6460
7008
|
}
|
|
6461
7009
|
connectionInfo() {
|
|
@@ -6479,7 +7027,7 @@ var AdaptiveTransport = class extends BaseTransport {
|
|
|
6479
7027
|
...this.rtc === void 0 ? {} : { webRtc: await this.rtc.connectionDetails() }
|
|
6480
7028
|
};
|
|
6481
7029
|
}
|
|
6482
|
-
async sendHandshake(step,
|
|
7030
|
+
async sendHandshake(step, data2) {
|
|
6483
7031
|
if (this.socket?.readyState !== WebSocket.OPEN || this.connectionId === void 0) {
|
|
6484
7032
|
throw new Error("adaptive transport has not been authorized");
|
|
6485
7033
|
}
|
|
@@ -6487,7 +7035,7 @@ var AdaptiveTransport = class extends BaseTransport {
|
|
|
6487
7035
|
connectionId: this.connectionId,
|
|
6488
7036
|
targetDeviceId: this.options.targetDeviceId,
|
|
6489
7037
|
step,
|
|
6490
|
-
data: toBase64Url(
|
|
7038
|
+
data: toBase64Url(data2)
|
|
6491
7039
|
});
|
|
6492
7040
|
}
|
|
6493
7041
|
onHandshake(cb) {
|
|
@@ -6571,9 +7119,9 @@ var AdaptiveTransport = class extends BaseTransport {
|
|
|
6571
7119
|
if (payload.connectionId !== this.connectionId || payload.targetDeviceId !== this.options.deviceId || !Number.isSafeInteger(payload.step) || typeof payload.data !== "string") {
|
|
6572
7120
|
throw new Error("Received a secure handshake frame for an unknown connection");
|
|
6573
7121
|
}
|
|
6574
|
-
const
|
|
7122
|
+
const data2 = fromBase64Url(payload.data);
|
|
6575
7123
|
for (const handler of this.handshakeHandlers)
|
|
6576
|
-
handler(payload.step,
|
|
7124
|
+
handler(payload.step, data2);
|
|
6577
7125
|
return;
|
|
6578
7126
|
}
|
|
6579
7127
|
if (frame.type === "signal.answer") {
|
|
@@ -6602,9 +7150,9 @@ var AdaptiveTransport = class extends BaseTransport {
|
|
|
6602
7150
|
if (payload.connectionId !== this.connectionId || payload.targetDeviceId !== this.options.deviceId || !Number.isSafeInteger(payload.counter) || typeof payload.ciphertext !== "string") {
|
|
6603
7151
|
throw new Error("Received a relay frame for an unknown connection");
|
|
6604
7152
|
}
|
|
6605
|
-
const
|
|
6606
|
-
this.bytesReceived +=
|
|
6607
|
-
this.emit(
|
|
7153
|
+
const data2 = fromBase64Url(payload.ciphertext);
|
|
7154
|
+
this.bytesReceived += data2.byteLength;
|
|
7155
|
+
this.emit(data2);
|
|
6608
7156
|
}
|
|
6609
7157
|
async negotiate() {
|
|
6610
7158
|
if (this.connectionId === void 0)
|
|
@@ -6696,9 +7244,9 @@ var AdaptiveTransport = class extends BaseTransport {
|
|
|
6696
7244
|
}
|
|
6697
7245
|
});
|
|
6698
7246
|
this.rtc = rtc;
|
|
6699
|
-
rtc.onMessage((
|
|
6700
|
-
this.bytesReceived +=
|
|
6701
|
-
this.emit(
|
|
7247
|
+
rtc.onMessage((data2) => {
|
|
7248
|
+
this.bytesReceived += data2.byteLength;
|
|
7249
|
+
this.emit(data2);
|
|
6702
7250
|
});
|
|
6703
7251
|
rtc.onClose(() => {
|
|
6704
7252
|
if (this.rtc === rtc && this.dataMode === "webrtc")
|
|
@@ -6793,6 +7341,27 @@ function negotiateTimeout(serverMs, localMs) {
|
|
|
6793
7341
|
return Math.max(serverMs, localMs);
|
|
6794
7342
|
}
|
|
6795
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
|
+
|
|
6796
7365
|
// src/api-proxy-switch.ts
|
|
6797
7366
|
var SWITCHED_DOMAINS = [
|
|
6798
7367
|
"sessions",
|
|
@@ -6846,10 +7415,10 @@ var ApiProxySwitch = class {
|
|
|
6846
7415
|
});
|
|
6847
7416
|
this.installed = true;
|
|
6848
7417
|
}
|
|
6849
|
-
selectRemote(api,
|
|
7418
|
+
selectRemote(api, target2) {
|
|
6850
7419
|
if (!this.installed) throw new Error("The Harness API switch is not installed.");
|
|
6851
7420
|
this.remote = api;
|
|
6852
|
-
this.target = { ...
|
|
7421
|
+
this.target = { ...target2 };
|
|
6853
7422
|
this.mode = "remote";
|
|
6854
7423
|
}
|
|
6855
7424
|
selectLocal() {
|
|
@@ -6895,7 +7464,7 @@ import * as nc from "node:crypto";
|
|
|
6895
7464
|
var crypto = nc && typeof nc === "object" && "webcrypto" in nc ? nc.webcrypto : nc && typeof nc === "object" && "randomBytes" in nc ? nc : void 0;
|
|
6896
7465
|
|
|
6897
7466
|
// ../../node_modules/.pnpm/@noble+ciphers@1.3.0/node_modules/@noble/ciphers/esm/webcrypto.js
|
|
6898
|
-
function
|
|
7467
|
+
function randomBytes2(bytesLength = 32) {
|
|
6899
7468
|
if (crypto && typeof crypto.getRandomValues === "function") {
|
|
6900
7469
|
return crypto.getRandomValues(new Uint8Array(bytesLength));
|
|
6901
7470
|
}
|
|
@@ -6995,11 +7564,11 @@ function utf8ToBytes(str) {
|
|
|
6995
7564
|
throw new Error("string expected");
|
|
6996
7565
|
return new Uint8Array(new TextEncoder().encode(str));
|
|
6997
7566
|
}
|
|
6998
|
-
function toBytes(
|
|
6999
|
-
if (typeof
|
|
7000
|
-
|
|
7001
|
-
abytes(
|
|
7002
|
-
return
|
|
7567
|
+
function toBytes(data2) {
|
|
7568
|
+
if (typeof data2 === "string")
|
|
7569
|
+
data2 = utf8ToBytes(data2);
|
|
7570
|
+
abytes(data2);
|
|
7571
|
+
return data2;
|
|
7003
7572
|
}
|
|
7004
7573
|
function concatBytes(...arrays) {
|
|
7005
7574
|
let sum = 0;
|
|
@@ -7026,7 +7595,7 @@ function createHasher(hashCons) {
|
|
|
7026
7595
|
hashC.create = () => hashCons();
|
|
7027
7596
|
return hashC;
|
|
7028
7597
|
}
|
|
7029
|
-
function
|
|
7598
|
+
function randomBytes3(bytesLength = 32) {
|
|
7030
7599
|
if (crypto2 && typeof crypto2.getRandomValues === "function") {
|
|
7031
7600
|
return crypto2.getRandomValues(new Uint8Array(bytesLength));
|
|
7032
7601
|
}
|
|
@@ -7063,21 +7632,21 @@ var HashMD = class extends Hash {
|
|
|
7063
7632
|
this.buffer = new Uint8Array(blockLen);
|
|
7064
7633
|
this.view = createView(this.buffer);
|
|
7065
7634
|
}
|
|
7066
|
-
update(
|
|
7635
|
+
update(data2) {
|
|
7067
7636
|
aexists(this);
|
|
7068
|
-
|
|
7069
|
-
abytes(
|
|
7637
|
+
data2 = toBytes(data2);
|
|
7638
|
+
abytes(data2);
|
|
7070
7639
|
const { view, buffer, blockLen } = this;
|
|
7071
|
-
const len =
|
|
7640
|
+
const len = data2.length;
|
|
7072
7641
|
for (let pos = 0; pos < len; ) {
|
|
7073
7642
|
const take = Math.min(blockLen - this.pos, len - pos);
|
|
7074
7643
|
if (take === blockLen) {
|
|
7075
|
-
const dataView = createView(
|
|
7644
|
+
const dataView = createView(data2);
|
|
7076
7645
|
for (; blockLen <= len - pos; pos += blockLen)
|
|
7077
7646
|
this.process(dataView, pos);
|
|
7078
7647
|
continue;
|
|
7079
7648
|
}
|
|
7080
|
-
buffer.set(
|
|
7649
|
+
buffer.set(data2.subarray(pos, pos + take), this.pos);
|
|
7081
7650
|
this.pos += take;
|
|
7082
7651
|
pos += take;
|
|
7083
7652
|
if (this.pos === blockLen) {
|
|
@@ -7085,7 +7654,7 @@ var HashMD = class extends Hash {
|
|
|
7085
7654
|
this.pos = 0;
|
|
7086
7655
|
}
|
|
7087
7656
|
}
|
|
7088
|
-
this.length +=
|
|
7657
|
+
this.length += data2.length;
|
|
7089
7658
|
this.roundClean();
|
|
7090
7659
|
return this;
|
|
7091
7660
|
}
|
|
@@ -8462,13 +9031,13 @@ function eddsa(Point, cHash, eddsaOpts = {}) {
|
|
|
8462
9031
|
});
|
|
8463
9032
|
const { prehash } = eddsaOpts;
|
|
8464
9033
|
const { BASE, Fp: Fp2, Fn: Fn2 } = Point;
|
|
8465
|
-
const
|
|
9034
|
+
const randomBytes8 = eddsaOpts.randomBytes || randomBytes3;
|
|
8466
9035
|
const adjustScalarBytes3 = eddsaOpts.adjustScalarBytes || ((bytes) => bytes);
|
|
8467
|
-
const domain = eddsaOpts.domain || ((
|
|
9036
|
+
const domain = eddsaOpts.domain || ((data2, ctx, phflag) => {
|
|
8468
9037
|
_abool2(phflag, "phflag");
|
|
8469
9038
|
if (ctx.length || phflag)
|
|
8470
9039
|
throw new Error("Contexts/pre-hash are not supported");
|
|
8471
|
-
return
|
|
9040
|
+
return data2;
|
|
8472
9041
|
});
|
|
8473
9042
|
function modN_LE(hash) {
|
|
8474
9043
|
return Fn2.create(bytesToNumberLE(hash));
|
|
@@ -8544,7 +9113,7 @@ function eddsa(Point, cHash, eddsaOpts = {}) {
|
|
|
8544
9113
|
signature: 2 * _size,
|
|
8545
9114
|
seed: _size
|
|
8546
9115
|
};
|
|
8547
|
-
function randomSecretKey(seed =
|
|
9116
|
+
function randomSecretKey(seed = randomBytes8(lengths.seed)) {
|
|
8548
9117
|
return _abytes2(seed, lengths.seed, "seed");
|
|
8549
9118
|
}
|
|
8550
9119
|
function keygen(seed) {
|
|
@@ -8663,7 +9232,7 @@ function montgomery(curveDef) {
|
|
|
8663
9232
|
const is25519 = type === "x25519";
|
|
8664
9233
|
if (!is25519 && type !== "x448")
|
|
8665
9234
|
throw new Error("invalid type");
|
|
8666
|
-
const randomBytes_ = rand ||
|
|
9235
|
+
const randomBytes_ = rand || randomBytes3;
|
|
8667
9236
|
const montgomeryBits = is25519 ? 255 : 448;
|
|
8668
9237
|
const fieldLen = is25519 ? 32 : 56;
|
|
8669
9238
|
const Gu = is25519 ? BigInt(9) : BigInt(5);
|
|
@@ -9312,8 +9881,8 @@ var SymmetricState = class {
|
|
|
9312
9881
|
}
|
|
9313
9882
|
this.ck.set(this.h);
|
|
9314
9883
|
}
|
|
9315
|
-
mixHash(
|
|
9316
|
-
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));
|
|
9317
9886
|
}
|
|
9318
9887
|
mixKey(inputKeyMaterial) {
|
|
9319
9888
|
const [ck, temp] = this.H.hkdf(this.ck, inputKeyMaterial);
|
|
@@ -9336,13 +9905,13 @@ var SymmetricState = class {
|
|
|
9336
9905
|
}
|
|
9337
9906
|
this.mixHash(out);
|
|
9338
9907
|
}
|
|
9339
|
-
decryptAndHash(
|
|
9908
|
+
decryptAndHash(data2, out) {
|
|
9340
9909
|
if (this.cipherstate) {
|
|
9341
|
-
this.cipherstate.decryptWithAd(this.h,
|
|
9910
|
+
this.cipherstate.decryptWithAd(this.h, data2, out);
|
|
9342
9911
|
} else {
|
|
9343
|
-
out.set(
|
|
9912
|
+
out.set(data2);
|
|
9344
9913
|
}
|
|
9345
|
-
this.mixHash(
|
|
9914
|
+
this.mixHash(data2);
|
|
9346
9915
|
}
|
|
9347
9916
|
split() {
|
|
9348
9917
|
if (!this.hasKey()) {
|
|
@@ -9742,7 +10311,7 @@ function createHasher2(hashCons, info = {}) {
|
|
|
9742
10311
|
Object.assign(hashC, info);
|
|
9743
10312
|
return Object.freeze(hashC);
|
|
9744
10313
|
}
|
|
9745
|
-
function
|
|
10314
|
+
function randomBytes4(bytesLength = 32) {
|
|
9746
10315
|
anumber2(bytesLength, "bytesLength");
|
|
9747
10316
|
const cr = typeof globalThis === "object" ? globalThis.crypto : null;
|
|
9748
10317
|
if (typeof cr?.getRandomValues !== "function")
|
|
@@ -9785,22 +10354,22 @@ var HashMD2 = class {
|
|
|
9785
10354
|
this.buffer = new Uint8Array(blockLen);
|
|
9786
10355
|
this.view = createView2(this.buffer);
|
|
9787
10356
|
}
|
|
9788
|
-
update(
|
|
10357
|
+
update(data2) {
|
|
9789
10358
|
aexists2(this);
|
|
9790
|
-
abytes2(
|
|
10359
|
+
abytes2(data2);
|
|
9791
10360
|
const { view, buffer, blockLen } = this;
|
|
9792
|
-
const len =
|
|
10361
|
+
const len = data2.length;
|
|
9793
10362
|
let processed = false;
|
|
9794
10363
|
for (let pos = 0; pos < len; ) {
|
|
9795
10364
|
const take = Math.min(blockLen - this.pos, len - pos);
|
|
9796
10365
|
if (take === blockLen) {
|
|
9797
|
-
const dataView = createView2(
|
|
10366
|
+
const dataView = createView2(data2);
|
|
9798
10367
|
for (; blockLen <= len - pos; pos += blockLen)
|
|
9799
10368
|
this.process(dataView, pos);
|
|
9800
10369
|
processed = true;
|
|
9801
10370
|
continue;
|
|
9802
10371
|
}
|
|
9803
|
-
buffer.set(pos === 0 && take === len ?
|
|
10372
|
+
buffer.set(pos === 0 && take === len ? data2 : data2.subarray(pos, pos + take), this.pos);
|
|
9804
10373
|
this.pos += take;
|
|
9805
10374
|
pos += take;
|
|
9806
10375
|
if (this.pos === blockLen) {
|
|
@@ -9809,7 +10378,7 @@ var HashMD2 = class {
|
|
|
9809
10378
|
processed = true;
|
|
9810
10379
|
}
|
|
9811
10380
|
}
|
|
9812
|
-
this.length +=
|
|
10381
|
+
this.length += data2.length;
|
|
9813
10382
|
if (processed)
|
|
9814
10383
|
this.roundClean();
|
|
9815
10384
|
return this;
|
|
@@ -10309,7 +10878,7 @@ function afunction(value, title) {
|
|
|
10309
10878
|
var bytesToHex3 = bytesToHex2;
|
|
10310
10879
|
var hexToBytes3 = (hex) => hexToBytes2(hex);
|
|
10311
10880
|
var isBytes3 = isBytes2;
|
|
10312
|
-
var
|
|
10881
|
+
var randomBytes5 = (bytesLength) => randomBytes4(bytesLength);
|
|
10313
10882
|
var _0n7 = /* @__PURE__ */ BigInt(0);
|
|
10314
10883
|
var _1n7 = /* @__PURE__ */ BigInt(1);
|
|
10315
10884
|
var atitle2 = (title) => title ? `"${title}" ` : "";
|
|
@@ -10868,18 +11437,18 @@ function validateTableBytes(numPoints, fpBytes) {
|
|
|
10868
11437
|
if (bytes > TABLE_BYTES_MAX)
|
|
10869
11438
|
throw new Error("invalid window size: table would need ~" + Math.ceil(bytes / 2 ** 20) + " MiB, max " + TABLE_BYTES_MAX / 2 ** 20 + " MiB");
|
|
10870
11439
|
}
|
|
10871
|
-
function probeRandomBytes(
|
|
10872
|
-
if (
|
|
11440
|
+
function probeRandomBytes(randomBytes8, length) {
|
|
11441
|
+
if (randomBytes8 === void 0)
|
|
10873
11442
|
return void 0;
|
|
10874
|
-
afunction(
|
|
11443
|
+
afunction(randomBytes8, "randomBytes");
|
|
10875
11444
|
try {
|
|
10876
|
-
const probe =
|
|
11445
|
+
const probe = randomBytes8(length);
|
|
10877
11446
|
if (!isBytes3(probe) || probe.length !== length)
|
|
10878
11447
|
return void 0;
|
|
10879
11448
|
} catch {
|
|
10880
11449
|
return void 0;
|
|
10881
11450
|
}
|
|
10882
|
-
return
|
|
11451
|
+
return randomBytes8;
|
|
10883
11452
|
}
|
|
10884
11453
|
function validateMSMPoints2(points, c) {
|
|
10885
11454
|
aarray(points, "points");
|
|
@@ -10972,9 +11541,9 @@ var ScalarMultiplier = class {
|
|
|
10972
11541
|
baseCanBeBlinded;
|
|
10973
11542
|
bits;
|
|
10974
11543
|
// Parametrized with a given Point class (not individual point)
|
|
10975
|
-
constructor(Point,
|
|
11544
|
+
constructor(Point, randomBytes8) {
|
|
10976
11545
|
validatePointCons(Point);
|
|
10977
|
-
this.randomBytes = probeRandomBytes(
|
|
11546
|
+
this.randomBytes = probeRandomBytes(randomBytes8, BLIND_BYTES);
|
|
10978
11547
|
this.Point = Point;
|
|
10979
11548
|
this.BASE = Point.BASE;
|
|
10980
11549
|
this.ZERO = Point.ZERO;
|
|
@@ -11245,7 +11814,7 @@ function edwards2(params, extraOpts = {}) {
|
|
|
11245
11814
|
if (FpLegendre2(Fp2, CURVE.d) !== -1)
|
|
11246
11815
|
throw new Error("edwards: CURVE.d must be a non-square in Fp for complete addition formulas");
|
|
11247
11816
|
validateObject2(opts, {}, { uvRatio: "function", randomBytes: "function" });
|
|
11248
|
-
const
|
|
11817
|
+
const randomBytes8 = opts.randomBytes === void 0 ? randomBytes5 : opts.randomBytes;
|
|
11249
11818
|
const MASK = _2n6 << BigInt(Fp2.BYTES * 8) - _1n10;
|
|
11250
11819
|
function isOdd(n) {
|
|
11251
11820
|
if (!Fp2.isOdd)
|
|
@@ -11506,7 +12075,7 @@ function edwards2(params, extraOpts = {}) {
|
|
|
11506
12075
|
}
|
|
11507
12076
|
}
|
|
11508
12077
|
const normalize = (points) => normalizeZ2(Point, points);
|
|
11509
|
-
const wnaf = new ScalarMultiplier(Point,
|
|
12078
|
+
const wnaf = new ScalarMultiplier(Point, randomBytes8);
|
|
11510
12079
|
if (wnaf.bits >= 6)
|
|
11511
12080
|
Point.BASE.precompute(6);
|
|
11512
12081
|
Object.freeze(Point.prototype);
|
|
@@ -11549,7 +12118,7 @@ function montgomery2(curveDef) {
|
|
|
11549
12118
|
const is25519 = type === "x25519";
|
|
11550
12119
|
if (!is25519 && type !== "x448")
|
|
11551
12120
|
throw new Error("invalid type");
|
|
11552
|
-
const randomBytes_ = rand === void 0 ?
|
|
12121
|
+
const randomBytes_ = rand === void 0 ? randomBytes5 : rand;
|
|
11553
12122
|
const montgomeryBits = is25519 ? 255 : 448;
|
|
11554
12123
|
const swap = cswap(P);
|
|
11555
12124
|
const fieldLen = is25519 ? 32 : 56;
|
|
@@ -11838,11 +12407,11 @@ var NqHandshake = class {
|
|
|
11838
12407
|
isFinished() {
|
|
11839
12408
|
return this.status === 2;
|
|
11840
12409
|
}
|
|
11841
|
-
mixHash(
|
|
11842
|
-
this.symmetric.mixHash(
|
|
12410
|
+
mixHash(data2) {
|
|
12411
|
+
this.symmetric.mixHash(data2);
|
|
11843
12412
|
}
|
|
11844
|
-
mixKeyAndHash(
|
|
11845
|
-
this.symmetric.mixKeyAndHash(
|
|
12413
|
+
mixKeyAndHash(data2) {
|
|
12414
|
+
this.symmetric.mixKeyAndHash(data2);
|
|
11846
12415
|
}
|
|
11847
12416
|
pushPsk(psk) {
|
|
11848
12417
|
if (psk.length !== PSK_LEN) {
|
|
@@ -12516,13 +13085,13 @@ var _BLAKE2 = class {
|
|
|
12516
13085
|
this.buffer = new Uint8Array(blockLen);
|
|
12517
13086
|
this.buffer32 = u32(this.buffer);
|
|
12518
13087
|
}
|
|
12519
|
-
update(
|
|
13088
|
+
update(data2) {
|
|
12520
13089
|
aexists2(this);
|
|
12521
|
-
abytes2(
|
|
13090
|
+
abytes2(data2);
|
|
12522
13091
|
const { blockLen, buffer, buffer32 } = this;
|
|
12523
|
-
const len =
|
|
12524
|
-
const offset =
|
|
12525
|
-
const buf =
|
|
13092
|
+
const len = data2.length;
|
|
13093
|
+
const offset = data2.byteOffset;
|
|
13094
|
+
const buf = data2.buffer;
|
|
12526
13095
|
for (let pos = 0; pos < len; ) {
|
|
12527
13096
|
if (this.pos === blockLen) {
|
|
12528
13097
|
swap32IfBE(buffer32);
|
|
@@ -12542,7 +13111,7 @@ var _BLAKE2 = class {
|
|
|
12542
13111
|
swap32IfBE(data32);
|
|
12543
13112
|
continue;
|
|
12544
13113
|
}
|
|
12545
|
-
buffer.set(pos === 0 && take === len ?
|
|
13114
|
+
buffer.set(pos === 0 && take === len ? data2 : data2.subarray(pos, pos + take), this.pos);
|
|
12546
13115
|
this.pos += take;
|
|
12547
13116
|
this.length += take;
|
|
12548
13117
|
pos += take;
|
|
@@ -12926,8 +13495,8 @@ var hmac = /* @__PURE__ */ (() => {
|
|
|
12926
13495
|
|
|
12927
13496
|
// ../../node_modules/.pnpm/@lukeburns+clatterjs@1.0.0/node_modules/@lukeburns/clatterjs/dist/crypto/hash.js
|
|
12928
13497
|
function makeHash(name2, hash, hashLen, blockLen) {
|
|
12929
|
-
const doHash = (
|
|
12930
|
-
const hmac1 = (key,
|
|
13498
|
+
const doHash = (data2) => hash.create().update(data2).digest();
|
|
13499
|
+
const hmac1 = (key, data2) => hmac(hash, key, data2);
|
|
12931
13500
|
const hmacMany = (key, parts) => hmac1(key, parts.length === 1 ? parts[0] : concatBytes2(...parts));
|
|
12932
13501
|
return {
|
|
12933
13502
|
name: name2,
|
|
@@ -13080,20 +13649,20 @@ var wrapCipher = /* @__NO_SIDE_EFFECTS__ */ (params, constructor) => {
|
|
|
13080
13649
|
};
|
|
13081
13650
|
let called = false;
|
|
13082
13651
|
const wrCipher = {
|
|
13083
|
-
encrypt(
|
|
13652
|
+
encrypt(data2, output) {
|
|
13084
13653
|
if (called)
|
|
13085
13654
|
throw new Error("cannot encrypt() twice with same key + nonce");
|
|
13086
13655
|
called = true;
|
|
13087
|
-
abytes4(
|
|
13656
|
+
abytes4(data2, void 0, "data");
|
|
13088
13657
|
checkOutput(cipher.encrypt.length, output);
|
|
13089
|
-
return cipher.encrypt(
|
|
13658
|
+
return cipher.encrypt(data2, output);
|
|
13090
13659
|
},
|
|
13091
|
-
decrypt(
|
|
13092
|
-
abytes4(
|
|
13093
|
-
if (tagl &&
|
|
13660
|
+
decrypt(data2, output) {
|
|
13661
|
+
abytes4(data2, void 0, "data");
|
|
13662
|
+
if (tagl && data2.length < tagl)
|
|
13094
13663
|
throw new Error('"ciphertext" expected length >= tagLength=' + tagl);
|
|
13095
13664
|
checkOutput(cipher.decrypt.length, output);
|
|
13096
|
-
return cipher.decrypt(
|
|
13665
|
+
return cipher.decrypt(data2, output);
|
|
13097
13666
|
}
|
|
13098
13667
|
};
|
|
13099
13668
|
return wrCipher;
|
|
@@ -13137,12 +13706,12 @@ var BLOCK_LEN = 64;
|
|
|
13137
13706
|
var BLOCK_LEN32 = 16;
|
|
13138
13707
|
var MAX_COUNTER = /* @__PURE__ */ (() => 2 ** 32 - 1)();
|
|
13139
13708
|
var U32_EMPTY = /* @__PURE__ */ Uint32Array.of();
|
|
13140
|
-
function runCipher(core, sigma, key, nonce,
|
|
13141
|
-
const len =
|
|
13709
|
+
function runCipher(core, sigma, key, nonce, data2, output, counter, rounds) {
|
|
13710
|
+
const len = data2.length;
|
|
13142
13711
|
const block = new Uint8Array(BLOCK_LEN);
|
|
13143
13712
|
const b32 = u322(block);
|
|
13144
|
-
const isAligned = isLE2 && isAligned32(
|
|
13145
|
-
const d32 = isAligned ? u322(
|
|
13713
|
+
const isAligned = isLE2 && isAligned32(data2) && isAligned32(output);
|
|
13714
|
+
const d32 = isAligned ? u322(data2) : U32_EMPTY;
|
|
13146
13715
|
const o32 = isAligned ? u322(output) : U32_EMPTY;
|
|
13147
13716
|
if (!isLE2) {
|
|
13148
13717
|
for (let pos = 0; pos < len; counter++) {
|
|
@@ -13153,7 +13722,7 @@ function runCipher(core, sigma, key, nonce, data, output, counter, rounds) {
|
|
|
13153
13722
|
const take = Math.min(BLOCK_LEN, len - pos);
|
|
13154
13723
|
for (let j = 0, posj; j < take; j++) {
|
|
13155
13724
|
posj = pos + j;
|
|
13156
|
-
output[posj] =
|
|
13725
|
+
output[posj] = data2[posj] ^ block[j];
|
|
13157
13726
|
}
|
|
13158
13727
|
pos += take;
|
|
13159
13728
|
}
|
|
@@ -13177,7 +13746,7 @@ function runCipher(core, sigma, key, nonce, data, output, counter, rounds) {
|
|
|
13177
13746
|
}
|
|
13178
13747
|
for (let j = 0, posj; j < take; j++) {
|
|
13179
13748
|
posj = pos + j;
|
|
13180
|
-
output[posj] =
|
|
13749
|
+
output[posj] = data2[posj] ^ block[j];
|
|
13181
13750
|
}
|
|
13182
13751
|
pos += take;
|
|
13183
13752
|
}
|
|
@@ -13190,11 +13759,11 @@ function createCipher(core, opts) {
|
|
|
13190
13759
|
anumber4(rounds);
|
|
13191
13760
|
abool2(counterRight);
|
|
13192
13761
|
abool2(allowShortKeys);
|
|
13193
|
-
return (key, nonce,
|
|
13762
|
+
return (key, nonce, data2, output, counter = 0) => {
|
|
13194
13763
|
abytes4(key, void 0, "key");
|
|
13195
13764
|
abytes4(nonce, void 0, "nonce");
|
|
13196
|
-
abytes4(
|
|
13197
|
-
const len =
|
|
13765
|
+
abytes4(data2, void 0, "data");
|
|
13766
|
+
const len = data2.length;
|
|
13198
13767
|
output = getOutput(len, output, false);
|
|
13199
13768
|
anumber4(counter);
|
|
13200
13769
|
if (counter < 0 || counter >= MAX_COUNTER)
|
|
@@ -13245,7 +13814,7 @@ function createCipher(core, opts) {
|
|
|
13245
13814
|
}
|
|
13246
13815
|
const n32 = swap32IfBE2(u322(nonce));
|
|
13247
13816
|
try {
|
|
13248
|
-
runCipher(core, sigma, k32, n32,
|
|
13817
|
+
runCipher(core, sigma, k32, n32, data2, output, counter, rounds);
|
|
13249
13818
|
return output;
|
|
13250
13819
|
} finally {
|
|
13251
13820
|
clean3(...toClean);
|
|
@@ -13292,7 +13861,7 @@ var Poly1305 = class {
|
|
|
13292
13861
|
for (let i = 0; i < 8; i++)
|
|
13293
13862
|
this.pad[i] = u8to16(key, 16 + 2 * i);
|
|
13294
13863
|
}
|
|
13295
|
-
process(
|
|
13864
|
+
process(data2, offset, isLast = false) {
|
|
13296
13865
|
const hibit = isLast ? 0 : 1 << 11;
|
|
13297
13866
|
const { h, r } = this;
|
|
13298
13867
|
const r0 = r[0];
|
|
@@ -13305,14 +13874,14 @@ var Poly1305 = class {
|
|
|
13305
13874
|
const r7 = r[7];
|
|
13306
13875
|
const r8 = r[8];
|
|
13307
13876
|
const r9 = r[9];
|
|
13308
|
-
const t0 = u8to16(
|
|
13309
|
-
const t1 = u8to16(
|
|
13310
|
-
const t2 = u8to16(
|
|
13311
|
-
const t3 = u8to16(
|
|
13312
|
-
const t4 = u8to16(
|
|
13313
|
-
const t5 = u8to16(
|
|
13314
|
-
const t6 = u8to16(
|
|
13315
|
-
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);
|
|
13316
13885
|
let h0 = h[0] + (t0 & 8191);
|
|
13317
13886
|
let h1 = h[1] + ((t0 >>> 13 | t1 << 3) & 8191);
|
|
13318
13887
|
let h2 = h[2] + ((t1 >>> 10 | t2 << 6) & 8191);
|
|
@@ -13448,20 +14017,20 @@ var Poly1305 = class {
|
|
|
13448
14017
|
}
|
|
13449
14018
|
clean3(g);
|
|
13450
14019
|
}
|
|
13451
|
-
update(
|
|
14020
|
+
update(data2) {
|
|
13452
14021
|
aexists3(this);
|
|
13453
|
-
abytes4(
|
|
13454
|
-
|
|
14022
|
+
abytes4(data2);
|
|
14023
|
+
data2 = copyBytes4(data2);
|
|
13455
14024
|
const { buffer, blockLen } = this;
|
|
13456
|
-
const len =
|
|
14025
|
+
const len = data2.length;
|
|
13457
14026
|
for (let pos = 0; pos < len; ) {
|
|
13458
14027
|
const take = Math.min(blockLen - this.pos, len - pos);
|
|
13459
14028
|
if (take === blockLen) {
|
|
13460
14029
|
for (; blockLen <= len - pos; pos += blockLen)
|
|
13461
|
-
this.process(
|
|
14030
|
+
this.process(data2, pos);
|
|
13462
14031
|
continue;
|
|
13463
14032
|
}
|
|
13464
|
-
buffer.set(
|
|
14033
|
+
buffer.set(data2.subarray(pos, pos + take), this.pos);
|
|
13465
14034
|
this.pos += take;
|
|
13466
14035
|
pos += take;
|
|
13467
14036
|
if (this.pos === blockLen) {
|
|
@@ -13635,9 +14204,9 @@ var _poly1305_aead = (xorStream) => (key, nonce, AAD) => {
|
|
|
13635
14204
|
},
|
|
13636
14205
|
decrypt(ciphertext, output) {
|
|
13637
14206
|
output = getOutput(ciphertext.length - tagLength, output, false);
|
|
13638
|
-
const
|
|
14207
|
+
const data2 = ciphertext.subarray(0, -tagLength);
|
|
13639
14208
|
const passedTag = ciphertext.subarray(-tagLength);
|
|
13640
|
-
const tag = computeTag(xorStream, key, nonce,
|
|
14209
|
+
const tag = computeTag(xorStream, key, nonce, data2, AAD);
|
|
13641
14210
|
if (!equalBytes2(passedTag, tag)) {
|
|
13642
14211
|
clean3(tag);
|
|
13643
14212
|
throw new Error("invalid tag");
|
|
@@ -13756,7 +14325,7 @@ var NoiseIkSession = class {
|
|
|
13756
14325
|
...initiator ? { rs: Uint8Array.from(this.expectedRemoteStatic) } : {},
|
|
13757
14326
|
cipher: chachaPoly,
|
|
13758
14327
|
hash: sha256H,
|
|
13759
|
-
rng: options.random ??
|
|
14328
|
+
rng: options.random ?? randomBytes2
|
|
13760
14329
|
});
|
|
13761
14330
|
if (this.handshake.getName() !== NOISE_IK_PROTOCOL) {
|
|
13762
14331
|
throw new NoiseSessionError("NOISE_SUITE_MISMATCH", "The Noise provider selected an unexpected cipher suite.");
|
|
@@ -13906,6 +14475,7 @@ var ClientSecureTransport = class {
|
|
|
13906
14475
|
incoming = new SecureMessageCodec();
|
|
13907
14476
|
outgoing = new SecureMessageCodec();
|
|
13908
14477
|
closed = false;
|
|
14478
|
+
sends = new SerialSend();
|
|
13909
14479
|
async connect() {
|
|
13910
14480
|
this.closed = false;
|
|
13911
14481
|
this.incoming.reset();
|
|
@@ -13933,12 +14503,14 @@ var ClientSecureTransport = class {
|
|
|
13933
14503
|
throw error;
|
|
13934
14504
|
}
|
|
13935
14505
|
}
|
|
13936
|
-
async send(
|
|
13937
|
-
|
|
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();
|
|
13938
14509
|
try {
|
|
13939
|
-
|
|
13940
|
-
|
|
13941
|
-
|
|
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
|
+
});
|
|
13942
14514
|
} catch (error) {
|
|
13943
14515
|
await this.close().catch(() => void 0);
|
|
13944
14516
|
throw error;
|
|
@@ -13946,11 +14518,11 @@ var ClientSecureTransport = class {
|
|
|
13946
14518
|
}
|
|
13947
14519
|
onMessage(handler) {
|
|
13948
14520
|
this.unsubscribeInner?.();
|
|
13949
|
-
this.unsubscribeInner = this.inner.onMessage((
|
|
14521
|
+
this.unsubscribeInner = this.inner.onMessage((data2) => {
|
|
13950
14522
|
const noise = this.noise;
|
|
13951
14523
|
if (noise === void 0 || !noise.complete || this.closed) return;
|
|
13952
14524
|
try {
|
|
13953
|
-
const message = this.incoming.decode(noise.decrypt(
|
|
14525
|
+
const message = this.incoming.decode(noise.decrypt(data2));
|
|
13954
14526
|
if (message !== void 0) handler(message);
|
|
13955
14527
|
} catch {
|
|
13956
14528
|
void this.close();
|
|
@@ -13989,11 +14561,11 @@ async function waitForResponder(inner, noise) {
|
|
|
13989
14561
|
await new Promise((resolve3, reject) => {
|
|
13990
14562
|
let settled = false;
|
|
13991
14563
|
const timer = setTimeout(() => finish(new Error("Noise IK handshake timed out.")), 1e4);
|
|
13992
|
-
const unsubscribe = inner.onHandshake((step,
|
|
14564
|
+
const unsubscribe = inner.onHandshake((step, data2) => {
|
|
13993
14565
|
if (settled) return;
|
|
13994
14566
|
try {
|
|
13995
14567
|
if (step !== 2) throw new Error("Noise IK responder sent an out-of-order handshake message.");
|
|
13996
|
-
noise.readHandshake(
|
|
14568
|
+
noise.readHandshake(data2);
|
|
13997
14569
|
if (!noise.complete) throw new Error("Noise IK handshake did not complete.");
|
|
13998
14570
|
finish();
|
|
13999
14571
|
} catch (error) {
|
|
@@ -14016,16 +14588,19 @@ async function waitForResponder(inner, noise) {
|
|
|
14016
14588
|
|
|
14017
14589
|
// src/control-route.ts
|
|
14018
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}`;
|
|
14019
14593
|
var ENDPOINT_SEGMENT_PATTERN = /^[A-Za-z0-9_$.-]+$/;
|
|
14020
14594
|
var INVALID_REQUEST_RPC_ID = "invalid-request";
|
|
14021
|
-
function registerControlRoute(connection, handler, webServer) {
|
|
14595
|
+
function registerControlRoute(connection, handler, webServer, statusStream) {
|
|
14022
14596
|
if (webServer !== void 0 && connection.requestRejection !== void 0) {
|
|
14023
14597
|
const dispose = webServer.register({
|
|
14024
14598
|
kind: "prefix",
|
|
14025
14599
|
path: CONTROL_RPC_PREFIX,
|
|
14026
|
-
handler: (req, res) => handleControlRequest(connection, handler, req, res)
|
|
14600
|
+
handler: (req, res) => handleControlRequest(connection, handler, req, res, statusStream)
|
|
14027
14601
|
});
|
|
14028
14602
|
return async () => {
|
|
14603
|
+
statusStream?.close();
|
|
14029
14604
|
await dispose();
|
|
14030
14605
|
};
|
|
14031
14606
|
}
|
|
@@ -14033,7 +14608,7 @@ function registerControlRoute(connection, handler, webServer) {
|
|
|
14033
14608
|
authority: "loopback"
|
|
14034
14609
|
});
|
|
14035
14610
|
}
|
|
14036
|
-
async function handleControlRequest(connection, handler, req, res) {
|
|
14611
|
+
async function handleControlRequest(connection, handler, req, res, statusStream) {
|
|
14037
14612
|
const rejection = connection.requestRejection?.(req);
|
|
14038
14613
|
if (rejection !== void 0) {
|
|
14039
14614
|
res.writeHead(rejection);
|
|
@@ -14041,6 +14616,10 @@ async function handleControlRequest(connection, handler, req, res) {
|
|
|
14041
14616
|
return;
|
|
14042
14617
|
}
|
|
14043
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
|
+
}
|
|
14044
14623
|
if (req.method !== "POST" || endpoint === void 0) {
|
|
14045
14624
|
writeText(res, 404, "not found");
|
|
14046
14625
|
return;
|
|
@@ -14132,12 +14711,12 @@ function writeText(res, status2, body) {
|
|
|
14132
14711
|
}
|
|
14133
14712
|
|
|
14134
14713
|
// src/ids.ts
|
|
14135
|
-
import { randomBytes as
|
|
14714
|
+
import { randomBytes as randomBytes7 } from "node:crypto";
|
|
14136
14715
|
function uuidV7(now = Date.now()) {
|
|
14137
14716
|
if (!Number.isSafeInteger(now) || now < 0 || now > 281474976710655) {
|
|
14138
14717
|
throw new RangeError("UUIDv7 timestamp must be a non-negative 48-bit integer");
|
|
14139
14718
|
}
|
|
14140
|
-
const bytes =
|
|
14719
|
+
const bytes = randomBytes7(16);
|
|
14141
14720
|
let timestamp = BigInt(now);
|
|
14142
14721
|
for (let index = 5; index >= 0; index -= 1) {
|
|
14143
14722
|
bytes[index] = Number(timestamp & 0xffn);
|
|
@@ -14451,13 +15030,13 @@ var AsyncFrameQueue = class {
|
|
|
14451
15030
|
};
|
|
14452
15031
|
function routeStreamEvent(event, streamId, queue) {
|
|
14453
15032
|
if (event.event === "harness.api.frame") {
|
|
14454
|
-
const
|
|
14455
|
-
if (
|
|
14456
|
-
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);
|
|
14457
15036
|
}
|
|
14458
15037
|
if (event.event === "harness.api.stream.closed") {
|
|
14459
|
-
const
|
|
14460
|
-
if (
|
|
15038
|
+
const data2 = event.data;
|
|
15039
|
+
if (data2.streamId === streamId) queue.close();
|
|
14461
15040
|
}
|
|
14462
15041
|
}
|
|
14463
15042
|
function bytesToBase642(bytes) {
|
|
@@ -14566,8 +15145,8 @@ function normalizeEvent(value) {
|
|
|
14566
15145
|
const { sourceEventSeqs: _sourceEventSeqs, ...rest } = next;
|
|
14567
15146
|
next = rest;
|
|
14568
15147
|
}
|
|
14569
|
-
const
|
|
14570
|
-
if (
|
|
15148
|
+
const data2 = normalizeEventData(type, next.data);
|
|
15149
|
+
if (data2 !== next.data) next = { ...next, data: data2 };
|
|
14571
15150
|
const surfaceOp = normalizeSurfaceOp(next.surfaceOp);
|
|
14572
15151
|
if (surfaceOp !== next.surfaceOp) next = { ...next, surfaceOp };
|
|
14573
15152
|
return next;
|
|
@@ -14765,14 +15344,26 @@ var RemoteTypertGateway2 = class {
|
|
|
14765
15344
|
};
|
|
14766
15345
|
var AsyncValueQueue = class {
|
|
14767
15346
|
values = [];
|
|
15347
|
+
bytes = 0;
|
|
14768
15348
|
waiters = [];
|
|
14769
15349
|
closed = false;
|
|
14770
15350
|
error;
|
|
14771
15351
|
push(value) {
|
|
14772
15352
|
if (this.closed) return;
|
|
14773
15353
|
const waiter = this.waiters.shift();
|
|
14774
|
-
if (waiter
|
|
14775
|
-
|
|
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 });
|
|
14776
15367
|
}
|
|
14777
15368
|
close() {
|
|
14778
15369
|
if (this.closed) return;
|
|
@@ -14787,7 +15378,9 @@ var AsyncValueQueue = class {
|
|
|
14787
15378
|
async *[Symbol.asyncIterator]() {
|
|
14788
15379
|
while (true) {
|
|
14789
15380
|
if (this.values.length > 0) {
|
|
14790
|
-
|
|
15381
|
+
const entry = this.values.shift();
|
|
15382
|
+
this.bytes -= entry.bytes;
|
|
15383
|
+
yield entry.value;
|
|
14791
15384
|
continue;
|
|
14792
15385
|
}
|
|
14793
15386
|
if (this.closed) {
|
|
@@ -14805,15 +15398,15 @@ var AsyncValueQueue = class {
|
|
|
14805
15398
|
};
|
|
14806
15399
|
function routeStreamEvent2(event, streamId, queue) {
|
|
14807
15400
|
if (event.event === "harness.remote.frame") {
|
|
14808
|
-
const
|
|
14809
|
-
if (
|
|
15401
|
+
const data3 = event.data;
|
|
15402
|
+
if (data3.streamId === streamId && data3.hasValue === true) queue.push(data3.value);
|
|
14810
15403
|
return;
|
|
14811
15404
|
}
|
|
14812
15405
|
if (event.event !== "harness.remote.stream.closed") return;
|
|
14813
|
-
const
|
|
14814
|
-
if (
|
|
14815
|
-
if (
|
|
14816
|
-
const failure2 =
|
|
15406
|
+
const data2 = event.data;
|
|
15407
|
+
if (data2.streamId !== streamId) return;
|
|
15408
|
+
if (data2.reason === "failed") {
|
|
15409
|
+
const failure2 = data2.failure;
|
|
14817
15410
|
queue.fail(remoteFailure({
|
|
14818
15411
|
code: typeof failure2?.code === "string" ? failure2.code : "internal",
|
|
14819
15412
|
message: typeof failure2?.message === "string" ? failure2.message : "The remote Harness stream failed.",
|
|
@@ -15580,7 +16173,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
|
|
|
15580
16173
|
follow.liveToolResultSeq.delete(itemId);
|
|
15581
16174
|
follow.liveToolResultSeq.delete(`${itemId}:call`);
|
|
15582
16175
|
}
|
|
15583
|
-
pushEvent(follow, type,
|
|
16176
|
+
pushEvent(follow, type, data2, view, placement) {
|
|
15584
16177
|
const seq = follow.nextSeq++;
|
|
15585
16178
|
const event = {
|
|
15586
16179
|
type,
|
|
@@ -15588,7 +16181,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
|
|
|
15588
16181
|
time: Date.now(),
|
|
15589
16182
|
data: adaptEventData(
|
|
15590
16183
|
type,
|
|
15591
|
-
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,
|
|
15592
16185
|
this.sessionGeneration
|
|
15593
16186
|
),
|
|
15594
16187
|
...isSurfaceEvent(type) ? placement === void 0 ? { surfaceOp: "append" } : {
|
|
@@ -15616,9 +16209,9 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
|
|
|
15616
16209
|
for (const block of collectImageBlocks(value)) {
|
|
15617
16210
|
const attachment = record2(block.attachment);
|
|
15618
16211
|
const attachmentId = string(attachment.attachmentId);
|
|
15619
|
-
const
|
|
15620
|
-
if (attachmentId === void 0 ||
|
|
15621
|
-
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 });
|
|
15622
16215
|
}
|
|
15623
16216
|
}
|
|
15624
16217
|
ensureStreamBlock(follow, key, itemId, kind) {
|
|
@@ -16278,7 +16871,7 @@ async function loadCatalog(client, signal, pendingThreads) {
|
|
|
16278
16871
|
if (cursor2 === void 0) break;
|
|
16279
16872
|
}
|
|
16280
16873
|
if (pendingThreads !== void 0) {
|
|
16281
|
-
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));
|
|
16282
16875
|
for (const [threadId, thread] of pendingThreads) {
|
|
16283
16876
|
if (listedIds.has(threadId)) pendingThreads.delete(threadId);
|
|
16284
16877
|
else threads.unshift(thread);
|
|
@@ -16353,8 +16946,8 @@ async function loadModelDirectory(client, signal) {
|
|
|
16353
16946
|
for (const value of array(result.data)) {
|
|
16354
16947
|
const source = record2(value);
|
|
16355
16948
|
if (source.hidden === true) continue;
|
|
16356
|
-
const
|
|
16357
|
-
if (
|
|
16949
|
+
const id4 = string(source.model) ?? string(source.id);
|
|
16950
|
+
if (id4 === void 0 || models.has(id4)) continue;
|
|
16358
16951
|
const efforts = array(source.supportedReasoningEfforts).map((value2) => {
|
|
16359
16952
|
const effort = record2(value2);
|
|
16360
16953
|
const effortId = string(effort.reasoningEffort);
|
|
@@ -16369,8 +16962,8 @@ async function loadModelDirectory(client, signal) {
|
|
|
16369
16962
|
const defaultEffort2 = string(source.defaultReasoningEffort);
|
|
16370
16963
|
const description = string(source.description);
|
|
16371
16964
|
const model = {
|
|
16372
|
-
id:
|
|
16373
|
-
name: string(source.displayName) ??
|
|
16965
|
+
id: id4,
|
|
16966
|
+
name: string(source.displayName) ?? id4,
|
|
16374
16967
|
...description === void 0 ? {} : { description },
|
|
16375
16968
|
...efforts.length === 0 ? {} : {
|
|
16376
16969
|
reasoning: {
|
|
@@ -16379,8 +16972,8 @@ async function loadModelDirectory(client, signal) {
|
|
|
16379
16972
|
}
|
|
16380
16973
|
}
|
|
16381
16974
|
};
|
|
16382
|
-
models.set(
|
|
16383
|
-
if (source.isDefault === true) defaultModelId =
|
|
16975
|
+
models.set(id4, model);
|
|
16976
|
+
if (source.isDefault === true) defaultModelId = id4;
|
|
16384
16977
|
}
|
|
16385
16978
|
cursor2 = typeof result.nextCursor === "string" && result.nextCursor.length > 0 ? result.nextCursor : void 0;
|
|
16386
16979
|
if (cursor2 === void 0) break;
|
|
@@ -16403,7 +16996,7 @@ function projectCodexNativeHistory(thread, sessionId, sessionGeneration = "legac
|
|
|
16403
16996
|
let seq = 0;
|
|
16404
16997
|
let turnNumber = 0;
|
|
16405
16998
|
const active = activeTurnId(thread.turns);
|
|
16406
|
-
const append = (type,
|
|
16999
|
+
const append = (type, data2, time = Date.now(), view, sourceEventSeqs) => {
|
|
16407
17000
|
const eventSeq = seq++;
|
|
16408
17001
|
entries.push({
|
|
16409
17002
|
type: "event",
|
|
@@ -16411,7 +17004,7 @@ function projectCodexNativeHistory(thread, sessionId, sessionGeneration = "legac
|
|
|
16411
17004
|
type,
|
|
16412
17005
|
seq: eventSeq,
|
|
16413
17006
|
time,
|
|
16414
|
-
data: adaptEventData(type,
|
|
17007
|
+
data: adaptEventData(type, data2, sessionGeneration),
|
|
16415
17008
|
...sourceEventSeqs === void 0 ? {} : { sourceEventSeqs },
|
|
16416
17009
|
...isSurfaceEvent(type) ? { surfaceOp: "append" } : {}
|
|
16417
17010
|
},
|
|
@@ -16501,9 +17094,9 @@ function paginateCodexNativeHistory(history, request) {
|
|
|
16501
17094
|
...history.activeTurnId === void 0 ? {} : { activeTurnId: history.activeTurnId }
|
|
16502
17095
|
};
|
|
16503
17096
|
}
|
|
16504
|
-
function adaptEventData(type,
|
|
16505
|
-
if (sessionGeneration !== "v3" || type !== "assistant/message") return
|
|
16506
|
-
const value = record2(
|
|
17097
|
+
function adaptEventData(type, data2, sessionGeneration) {
|
|
17098
|
+
if (sessionGeneration !== "v3" || type !== "assistant/message") return data2;
|
|
17099
|
+
const value = record2(data2);
|
|
16507
17100
|
return Array.isArray(value.stream) ? value : { ...value, stream: [] };
|
|
16508
17101
|
}
|
|
16509
17102
|
function adaptCodexHistoryPage(value, sessionGeneration) {
|
|
@@ -16537,14 +17130,14 @@ function adaptCodexHistoryPage(value, sessionGeneration) {
|
|
|
16537
17130
|
}
|
|
16538
17131
|
function itemEvents(item, turn, step, requestId, selection = modelSelection(), sessionId = CODEX_SESSION_PREFIX, supplementalImages = []) {
|
|
16539
17132
|
const type = string(item.type);
|
|
16540
|
-
const
|
|
17133
|
+
const id4 = string(item.id) ?? `${turn}:${step}:${hashString(JSON.stringify(item))}`;
|
|
16541
17134
|
const text = itemText2(item);
|
|
16542
17135
|
if (type === "userMessage") return [{
|
|
16543
17136
|
type: "user/message",
|
|
16544
17137
|
data: {
|
|
16545
|
-
id:
|
|
17138
|
+
id: id4,
|
|
16546
17139
|
role: "user",
|
|
16547
|
-
content: messageContent(item, `${sessionId}:${
|
|
17140
|
+
content: messageContent(item, `${sessionId}:${id4}`, "text", text),
|
|
16548
17141
|
source: requestId === void 0 || requestId === "" ? { kind: "user" } : { kind: "user", rpcId: requestId }
|
|
16549
17142
|
}
|
|
16550
17143
|
}];
|
|
@@ -16554,11 +17147,11 @@ function itemEvents(item, turn, step, requestId, selection = modelSelection(), s
|
|
|
16554
17147
|
turn,
|
|
16555
17148
|
step,
|
|
16556
17149
|
message: {
|
|
16557
|
-
id:
|
|
17150
|
+
id: id4,
|
|
16558
17151
|
role: "assistant",
|
|
16559
17152
|
content: messageContent(
|
|
16560
17153
|
item,
|
|
16561
|
-
`${sessionId}:${
|
|
17154
|
+
`${sessionId}:${id4}`,
|
|
16562
17155
|
type === "reasoning" ? "reasoning" : "text",
|
|
16563
17156
|
text,
|
|
16564
17157
|
type === "agentMessage" ? supplementalImages : []
|
|
@@ -16573,10 +17166,10 @@ function itemEvents(item, turn, step, requestId, selection = modelSelection(), s
|
|
|
16573
17166
|
return [
|
|
16574
17167
|
{
|
|
16575
17168
|
type: "tool/call",
|
|
16576
|
-
data: { turn, step, callId:
|
|
17169
|
+
data: { turn, step, callId: id4, name: name2, arguments: JSON.stringify(args) },
|
|
16577
17170
|
view: { for: "call", view: toolCallView(item, type, args) }
|
|
16578
17171
|
},
|
|
16579
|
-
toolResultEvent(item, turn, step,
|
|
17172
|
+
toolResultEvent(item, turn, step, id4, toolResultText(item, type, text))
|
|
16580
17173
|
];
|
|
16581
17174
|
}
|
|
16582
17175
|
if (type === "error") return [{
|
|
@@ -16585,7 +17178,7 @@ function itemEvents(item, turn, step, requestId, selection = modelSelection(), s
|
|
|
16585
17178
|
turn,
|
|
16586
17179
|
step,
|
|
16587
17180
|
message: {
|
|
16588
|
-
id:
|
|
17181
|
+
id: id4,
|
|
16589
17182
|
role: "assistant",
|
|
16590
17183
|
content: [{ type: "text", text: text ?? "CodeX reported an error." }],
|
|
16591
17184
|
source: { kind: "model", provider: selection.provider, model: selection.model }
|
|
@@ -16672,19 +17265,19 @@ function parseImageBlock(block) {
|
|
|
16672
17265
|
const url = string(block.url) ?? string(block.image_url) ?? string(record2(block.image_url).url);
|
|
16673
17266
|
const parsed = url === void 0 ? void 0 : parseDataImageUrl2(url);
|
|
16674
17267
|
if (parsed !== void 0) return parsed;
|
|
16675
|
-
const
|
|
16676
|
-
if (
|
|
16677
|
-
if (
|
|
16678
|
-
const mediaType = string(block.mediaType) ?? string(block.mimeType) ?? sniffImageMediaType2(
|
|
16679
|
-
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;
|
|
16680
17273
|
}
|
|
16681
17274
|
function parseDataImageUrl2(value) {
|
|
16682
17275
|
const match = DATA_IMAGE_URL2.exec(value);
|
|
16683
17276
|
if (match === null) return void 0;
|
|
16684
17277
|
const mediaType = match[1];
|
|
16685
|
-
const
|
|
16686
|
-
if (!CODEX_IMAGE_MEDIA_TYPES2.has(mediaType) || !isCanonicalBase642(
|
|
16687
|
-
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}` };
|
|
16688
17281
|
}
|
|
16689
17282
|
function collectImageBlocks(value, output = []) {
|
|
16690
17283
|
if (Array.isArray(value)) {
|
|
@@ -16705,8 +17298,8 @@ function base64ByteLength(value) {
|
|
|
16705
17298
|
const padding = value.endsWith("==") ? 2 : value.endsWith("=") ? 1 : 0;
|
|
16706
17299
|
return Math.max(1, Math.floor(value.length * 3 / 4) - padding);
|
|
16707
17300
|
}
|
|
16708
|
-
function imageDimensions(mediaType,
|
|
16709
|
-
const bytes = base64PrefixBytes2(
|
|
17301
|
+
function imageDimensions(mediaType, data2) {
|
|
17302
|
+
const bytes = base64PrefixBytes2(data2, 256 * 1024);
|
|
16710
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;
|
|
16711
17304
|
return parsed ?? { width: 1, height: 1 };
|
|
16712
17305
|
}
|
|
@@ -16759,8 +17352,8 @@ function webpDimensions(bytes) {
|
|
|
16759
17352
|
}
|
|
16760
17353
|
return void 0;
|
|
16761
17354
|
}
|
|
16762
|
-
function sniffImageMediaType2(
|
|
16763
|
-
const bytes = base64PrefixBytes2(
|
|
17355
|
+
function sniffImageMediaType2(data2) {
|
|
17356
|
+
const bytes = base64PrefixBytes2(data2, 32);
|
|
16764
17357
|
if (pngDimensions(bytes) !== void 0) return "image/png";
|
|
16765
17358
|
if (bytes.length >= 3 && bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) return "image/jpeg";
|
|
16766
17359
|
if (gifDimensions(bytes) !== void 0) return "image/gif";
|
|
@@ -16803,7 +17396,7 @@ function stringFromBytes(bytes, start, end) {
|
|
|
16803
17396
|
for (let index = start; index < end; index += 1) output += String.fromCharCode(bytes[index]);
|
|
16804
17397
|
return output;
|
|
16805
17398
|
}
|
|
16806
|
-
function toolResultEvent(item, turn, step,
|
|
17399
|
+
function toolResultEvent(item, turn, step, id4, resultText) {
|
|
16807
17400
|
const type = string(item.type) ?? "unknown";
|
|
16808
17401
|
return {
|
|
16809
17402
|
type: "tool/result",
|
|
@@ -16811,23 +17404,23 @@ function toolResultEvent(item, turn, step, id2, resultText) {
|
|
|
16811
17404
|
turn,
|
|
16812
17405
|
step,
|
|
16813
17406
|
message: {
|
|
16814
|
-
id: `${
|
|
17407
|
+
id: `${id4}:result`,
|
|
16815
17408
|
role: "user",
|
|
16816
17409
|
content: [{
|
|
16817
17410
|
type: "tool-result",
|
|
16818
|
-
toolCallId:
|
|
17411
|
+
toolCallId: id4,
|
|
16819
17412
|
content: [{ type: "text", text: resultText }],
|
|
16820
17413
|
...item.status === "failed" || item.success === false ? { isError: true } : {}
|
|
16821
17414
|
}],
|
|
16822
|
-
source: { kind: "tool", callId:
|
|
17415
|
+
source: { kind: "tool", callId: id4 }
|
|
16823
17416
|
}
|
|
16824
17417
|
},
|
|
16825
17418
|
view: { for: "result", view: toolResultView(item, type, resultText) }
|
|
16826
17419
|
};
|
|
16827
17420
|
}
|
|
16828
17421
|
function toolResultContentReplacement(event) {
|
|
16829
|
-
const
|
|
16830
|
-
const message = record2(
|
|
17422
|
+
const data2 = record2(event.data);
|
|
17423
|
+
const message = record2(data2.message);
|
|
16831
17424
|
const content = array(message.content);
|
|
16832
17425
|
const result = record2(content[0]);
|
|
16833
17426
|
if (result.isError === void 0) return event;
|
|
@@ -16835,7 +17428,7 @@ function toolResultContentReplacement(event) {
|
|
|
16835
17428
|
return {
|
|
16836
17429
|
...event,
|
|
16837
17430
|
data: {
|
|
16838
|
-
...
|
|
17431
|
+
...data2,
|
|
16839
17432
|
message: { ...message, content: [stableResult] }
|
|
16840
17433
|
}
|
|
16841
17434
|
};
|
|
@@ -16969,12 +17562,12 @@ function projectWorkspace(project) {
|
|
|
16969
17562
|
}
|
|
16970
17563
|
function projectCodexProject(value, fallbackPosition) {
|
|
16971
17564
|
const source = record2(value);
|
|
16972
|
-
const
|
|
16973
|
-
if (
|
|
17565
|
+
const id4 = string(source.id);
|
|
17566
|
+
if (id4 === void 0) return void 0;
|
|
16974
17567
|
const roots = array(source.roots).map((root) => string(record2(root).path)).filter((path) => path !== void 0 && path.length > 0).map((path) => ({ path }));
|
|
16975
17568
|
if (roots.length === 0) return void 0;
|
|
16976
17569
|
return {
|
|
16977
|
-
id:
|
|
17570
|
+
id: id4,
|
|
16978
17571
|
name: string(source.name) ?? basename(roots[0].path),
|
|
16979
17572
|
roots,
|
|
16980
17573
|
position: finiteNumber(source.position) ?? fallbackPosition,
|
|
@@ -17400,6 +17993,105 @@ var AsyncValueQueue2 = class {
|
|
|
17400
17993
|
// src/server-api.ts
|
|
17401
17994
|
import { platform } from "node:os";
|
|
17402
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
|
+
|
|
17403
18095
|
// src/config.ts
|
|
17404
18096
|
import { hostname } from "node:os";
|
|
17405
18097
|
import s from "@deepseek-ai/schemastery";
|
|
@@ -17409,6 +18101,8 @@ var Config = s.object({
|
|
|
17409
18101
|
role: s.union(["host", "client", "both"]),
|
|
17410
18102
|
serverUrl: s.string(),
|
|
17411
18103
|
deviceName: s.string(),
|
|
18104
|
+
terminal: s.object({ enabled: s.boolean() }),
|
|
18105
|
+
loopback: s.object({ ports: s.array(s.number()) }),
|
|
17412
18106
|
forceRelay: s.boolean(),
|
|
17413
18107
|
logLevel: s.union(["debug", "info", "warn", "error"]),
|
|
17414
18108
|
reconnect: s.union([
|
|
@@ -17422,7 +18116,8 @@ var Config = s.object({
|
|
|
17422
18116
|
codex: s.object({
|
|
17423
18117
|
enabled: s.boolean(),
|
|
17424
18118
|
binary: s.string()
|
|
17425
|
-
})
|
|
18119
|
+
}),
|
|
18120
|
+
acp: s.object({ enabled: s.boolean(), backends: s.array(s.object({ id: s.string(), enabled: s.boolean(), command: s.string(), args: s.array(s.string()), cwd: s.string() })) })
|
|
17426
18121
|
});
|
|
17427
18122
|
var reconnectSchema = external_exports.union([
|
|
17428
18123
|
external_exports.boolean(),
|
|
@@ -17437,13 +18132,16 @@ var configSchema = external_exports.object({
|
|
|
17437
18132
|
role: external_exports.enum(["host", "client", "both"]).optional(),
|
|
17438
18133
|
serverUrl: external_exports.string().url().optional(),
|
|
17439
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(),
|
|
17440
18137
|
forceRelay: external_exports.boolean().optional(),
|
|
17441
18138
|
logLevel: external_exports.enum(["debug", "info", "warn", "error"]).optional(),
|
|
17442
18139
|
reconnect: reconnectSchema.optional(),
|
|
17443
18140
|
codex: external_exports.object({
|
|
17444
18141
|
enabled: external_exports.boolean().optional(),
|
|
17445
18142
|
binary: external_exports.string().trim().min(1).max(4096).optional()
|
|
17446
|
-
}).strict().optional()
|
|
18143
|
+
}).strict().optional(),
|
|
18144
|
+
acp: external_exports.object({ enabled: external_exports.boolean().optional(), backends: external_exports.array(external_exports.object({ id: external_exports.string().trim().regex(/^[a-z0-9][a-z0-9._-]{0,31}$/i), enabled: external_exports.boolean().optional(), command: external_exports.string().trim().min(1).max(4096).optional(), args: external_exports.array(external_exports.string().max(4096)).max(32).optional(), cwd: external_exports.string().max(4096).optional() }).strict()).max(12).optional(), backend: external_exports.string().trim().regex(/^[a-z0-9][a-z0-9._-]{0,31}$/i).optional(), command: external_exports.string().trim().min(1).max(4096).optional(), args: external_exports.array(external_exports.string().max(4096)).max(32).optional(), cwd: external_exports.string().max(4096).optional() }).strict().optional()
|
|
17447
18145
|
}).strict();
|
|
17448
18146
|
function resolveConfig(input2 = {}, env = process.env) {
|
|
17449
18147
|
const parsed = configSchema.parse(input2);
|
|
@@ -17460,6 +18158,8 @@ function resolveConfig(input2 = {}, env = process.env) {
|
|
|
17460
18158
|
role: parsed.role ?? "host",
|
|
17461
18159
|
...serverUrl === void 0 ? {} : { serverUrl },
|
|
17462
18160
|
deviceName: parsed.deviceName ?? hostname(),
|
|
18161
|
+
terminal: { enabled: parsed.terminal?.enabled ?? false },
|
|
18162
|
+
loopback: { ports: [...new Set(parsed.loopback?.ports ?? [])] },
|
|
17463
18163
|
forceRelay: parsed.forceRelay ?? false,
|
|
17464
18164
|
logLevel: parsed.logLevel ?? "info",
|
|
17465
18165
|
reconnect: {
|
|
@@ -17471,7 +18171,12 @@ function resolveConfig(input2 = {}, env = process.env) {
|
|
|
17471
18171
|
codex: {
|
|
17472
18172
|
enabled: parsed.codex?.enabled ?? true,
|
|
17473
18173
|
binary: parsed.codex?.binary ?? "codex"
|
|
17474
|
-
}
|
|
18174
|
+
},
|
|
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 } : {} };
|
|
18179
|
+
}) }
|
|
17475
18180
|
};
|
|
17476
18181
|
}
|
|
17477
18182
|
function normalizeServerUrl(value) {
|
|
@@ -17493,7 +18198,7 @@ function normalizeServerUrl(value) {
|
|
|
17493
18198
|
}
|
|
17494
18199
|
|
|
17495
18200
|
// src/version.ts
|
|
17496
|
-
var PLUGIN_VERSION = "0.4.
|
|
18201
|
+
var PLUGIN_VERSION = "0.4.15";
|
|
17497
18202
|
|
|
17498
18203
|
// src/server-api.ts
|
|
17499
18204
|
var TERMINAL_CONTROL_CHARACTERS = /[\u0000-\u001f\u007f-\u009f]/u;
|
|
@@ -17652,22 +18357,48 @@ var HostServerApi = class {
|
|
|
17652
18357
|
this.credentials = await this.credentialsPromise;
|
|
17653
18358
|
return this.credentials;
|
|
17654
18359
|
}
|
|
17655
|
-
async refreshCredentials() {
|
|
18360
|
+
async refreshCredentials(rejectedAccessToken = this.credentials?.accessToken) {
|
|
17656
18361
|
const identity = this.requireIdentity();
|
|
17657
|
-
|
|
17658
|
-
|
|
17659
|
-
|
|
17660
|
-
|
|
17661
|
-
|
|
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);
|
|
17662
18369
|
});
|
|
17663
|
-
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({
|
|
17664
18396
|
serverUrl: this.baseUrl,
|
|
17665
18397
|
deviceId: identity.deviceId,
|
|
17666
18398
|
authorizationMethod: stored.authorizationMethod,
|
|
17667
18399
|
...stored.account === void 0 ? {} : { account: stored.account },
|
|
17668
18400
|
...validateTokens(tokens)
|
|
17669
18401
|
});
|
|
17670
|
-
return this.credentials;
|
|
17671
18402
|
}
|
|
17672
18403
|
async listDevices() {
|
|
17673
18404
|
const result = await this.request("/api/v1/devices");
|
|
@@ -17693,21 +18424,13 @@ var HostServerApi = class {
|
|
|
17693
18424
|
return { online: result.online, ...typeof result.lastSeenAt === "number" ? { lastSeenAt: result.lastSeenAt } : {} };
|
|
17694
18425
|
}
|
|
17695
18426
|
async loadOrIssue(identity) {
|
|
17696
|
-
|
|
17697
|
-
|
|
17698
|
-
|
|
17699
|
-
|
|
17700
|
-
|
|
17701
|
-
|
|
17702
|
-
|
|
17703
|
-
body: JSON.stringify({ deviceId: identity.deviceId, refreshToken: stored.refreshToken })
|
|
17704
|
-
});
|
|
17705
|
-
return this.store.save({
|
|
17706
|
-
serverUrl: this.baseUrl,
|
|
17707
|
-
deviceId: identity.deviceId,
|
|
17708
|
-
authorizationMethod: stored.authorizationMethod,
|
|
17709
|
-
...stored.account === void 0 ? {} : { account: stored.account },
|
|
17710
|
-
...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);
|
|
17711
18434
|
});
|
|
17712
18435
|
}
|
|
17713
18436
|
async register(identity, authorization) {
|
|
@@ -17800,11 +18523,12 @@ var ClientServerApi = class extends HostServerApi {
|
|
|
17800
18523
|
}
|
|
17801
18524
|
};
|
|
17802
18525
|
var ServerApiError = class extends Error {
|
|
17803
|
-
constructor(code, message, retryable, status2) {
|
|
18526
|
+
constructor(code, message, retryable, status2, phase) {
|
|
17804
18527
|
super(message);
|
|
17805
18528
|
this.code = code;
|
|
17806
18529
|
this.retryable = retryable;
|
|
17807
18530
|
this.status = status2;
|
|
18531
|
+
this.phase = phase;
|
|
17808
18532
|
}
|
|
17809
18533
|
};
|
|
17810
18534
|
function validateTokens(value) {
|
|
@@ -17975,12 +18699,12 @@ var TypertGatewaySwitch = class {
|
|
|
17975
18699
|
}
|
|
17976
18700
|
this.installed = true;
|
|
17977
18701
|
}
|
|
17978
|
-
selectRemote(remote, support = ALL_REMOTE_COMMANDS,
|
|
18702
|
+
selectRemote(remote, support = ALL_REMOTE_COMMANDS, target2) {
|
|
17979
18703
|
if (!this.installed) throw new Error("The Typert gateway switch is not installed.");
|
|
17980
18704
|
this.remoteInvoke = typeof remote === "function" ? remote : (request) => remote.invoke(request);
|
|
17981
18705
|
this.remoteTarget = typeof remote === "function" ? void 0 : remote;
|
|
17982
18706
|
this.remoteSupport = { ...support };
|
|
17983
|
-
this.target =
|
|
18707
|
+
this.target = target2 === void 0 ? void 0 : { ...target2 };
|
|
17984
18708
|
}
|
|
17985
18709
|
selectLocal() {
|
|
17986
18710
|
this.remoteInvoke = void 0;
|
|
@@ -18047,7 +18771,7 @@ import { networkInterfaces } from "node:os";
|
|
|
18047
18771
|
// src/native-rtc-helper.ts
|
|
18048
18772
|
import { spawn, spawnSync } from "node:child_process";
|
|
18049
18773
|
import { existsSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
18050
|
-
import { delimiter, dirname as
|
|
18774
|
+
import { delimiter, dirname as dirname3, join as join3 } from "node:path";
|
|
18051
18775
|
import { fileURLToPath } from "node:url";
|
|
18052
18776
|
var cachedExternalFactory;
|
|
18053
18777
|
var cachedExternalFactoryResolved = false;
|
|
@@ -18098,21 +18822,21 @@ function nodeBinaryCandidates() {
|
|
|
18098
18822
|
add3(process.env.DSH_REMOTE_NODE);
|
|
18099
18823
|
add3(process.env.NODE);
|
|
18100
18824
|
add3(process.execPath);
|
|
18101
|
-
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"));
|
|
18102
18826
|
add3("/opt/homebrew/bin/node");
|
|
18103
18827
|
add3("/usr/local/bin/node");
|
|
18104
18828
|
add3("/usr/bin/node");
|
|
18105
|
-
add3(
|
|
18106
|
-
add3(
|
|
18107
|
-
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"));
|
|
18108
18832
|
for (const nvmNode of nvmNodeCandidates()) add3(nvmNode);
|
|
18109
18833
|
return candidates;
|
|
18110
18834
|
}
|
|
18111
18835
|
function nvmNodeCandidates() {
|
|
18112
|
-
const root =
|
|
18836
|
+
const root = join3(process.env.HOME ?? "", ".nvm", "versions", "node");
|
|
18113
18837
|
if (root === "" || !existsSync(root)) return [];
|
|
18114
18838
|
try {
|
|
18115
|
-
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 }));
|
|
18116
18840
|
} catch {
|
|
18117
18841
|
return [];
|
|
18118
18842
|
}
|
|
@@ -18134,7 +18858,7 @@ function isUsableExternalNode(candidate, requireFrom) {
|
|
|
18134
18858
|
"console.log(`node:${process.versions.node}`);"
|
|
18135
18859
|
].join("")
|
|
18136
18860
|
], {
|
|
18137
|
-
cwd:
|
|
18861
|
+
cwd: dirname3(requireFrom),
|
|
18138
18862
|
env,
|
|
18139
18863
|
encoding: "utf8",
|
|
18140
18864
|
timeout: 3e3
|
|
@@ -18156,7 +18880,7 @@ var ExternalNativePeerConnection = class {
|
|
|
18156
18880
|
this.nodeBinary = nodeBinary;
|
|
18157
18881
|
this.requireFrom = requireFrom;
|
|
18158
18882
|
this.child = spawn(this.nodeBinary, ["--input-type=module", "--eval", HELPER_SOURCE], {
|
|
18159
|
-
cwd:
|
|
18883
|
+
cwd: dirname3(this.requireFrom),
|
|
18160
18884
|
env: { ...process.env, DSH_REMOTE_RTC_HELPER_REQUIRE_FROM: this.requireFrom },
|
|
18161
18885
|
stdio: ["pipe", "pipe", "pipe"]
|
|
18162
18886
|
});
|
|
@@ -18225,8 +18949,8 @@ var ExternalNativePeerConnection = class {
|
|
|
18225
18949
|
}, 1e3);
|
|
18226
18950
|
timer.unref?.();
|
|
18227
18951
|
}
|
|
18228
|
-
sendChannelData(channelId,
|
|
18229
|
-
const payload = typeof
|
|
18952
|
+
sendChannelData(channelId, data2) {
|
|
18953
|
+
const payload = typeof data2 === "string" ? { channelId, text: data2 } : { channelId, base64: Buffer.from(data2).toString("base64") };
|
|
18230
18954
|
this.notify("channelSend", payload);
|
|
18231
18955
|
}
|
|
18232
18956
|
closeChannel(channelId) {
|
|
@@ -18234,11 +18958,11 @@ var ExternalNativePeerConnection = class {
|
|
|
18234
18958
|
}
|
|
18235
18959
|
request(method, payload) {
|
|
18236
18960
|
if (this.closed) return Promise.reject(new Error("native rtc helper is closed"));
|
|
18237
|
-
const
|
|
18961
|
+
const id4 = this.nextRequestId++;
|
|
18238
18962
|
const promise = new Promise((resolve3, reject) => {
|
|
18239
|
-
this.pending.set(
|
|
18963
|
+
this.pending.set(id4, { resolve: resolve3, reject });
|
|
18240
18964
|
});
|
|
18241
|
-
this.write({ id:
|
|
18965
|
+
this.write({ id: id4, method, payload });
|
|
18242
18966
|
return promise;
|
|
18243
18967
|
}
|
|
18244
18968
|
notify(method, payload) {
|
|
@@ -18342,8 +19066,8 @@ var ExternalNativeDataChannel = class {
|
|
|
18342
19066
|
onclose = null;
|
|
18343
19067
|
onerror = null;
|
|
18344
19068
|
onbufferedamountlow = null;
|
|
18345
|
-
send(
|
|
18346
|
-
this.owner.sendChannelData(this.channelId,
|
|
19069
|
+
send(data2) {
|
|
19070
|
+
this.owner.sendChannelData(this.channelId, data2);
|
|
18347
19071
|
}
|
|
18348
19072
|
close() {
|
|
18349
19073
|
this.owner.closeChannel(this.channelId);
|
|
@@ -18356,8 +19080,8 @@ var ExternalNativeDataChannel = class {
|
|
|
18356
19080
|
if (previous !== "closed" && readyState === "closed") this.onclose?.();
|
|
18357
19081
|
if (bufferedAmount === 0) this.onbufferedamountlow?.();
|
|
18358
19082
|
}
|
|
18359
|
-
emitMessage(
|
|
18360
|
-
this.onmessage?.({ data });
|
|
19083
|
+
emitMessage(data2) {
|
|
19084
|
+
this.onmessage?.({ data: data2 });
|
|
18361
19085
|
}
|
|
18362
19086
|
emitError() {
|
|
18363
19087
|
this.onerror?.();
|
|
@@ -18834,10 +19558,10 @@ function adaptDataChannel2(raw) {
|
|
|
18834
19558
|
return raw.onerror;
|
|
18835
19559
|
},
|
|
18836
19560
|
onbufferedamountlow: null,
|
|
18837
|
-
send(
|
|
18838
|
-
const bytes = typeof
|
|
19561
|
+
send(data2) {
|
|
19562
|
+
const bytes = typeof data2 === "string" ? Buffer.byteLength(data2) : data2.byteLength;
|
|
18839
19563
|
try {
|
|
18840
|
-
raw.send(typeof
|
|
19564
|
+
raw.send(typeof data2 === "string" ? data2 : Buffer.from(data2));
|
|
18841
19565
|
} catch (error) {
|
|
18842
19566
|
console.error("[werift-send-error] bytes=" + bytes, error instanceof Error ? error.message : error);
|
|
18843
19567
|
throw error;
|
|
@@ -18900,17 +19624,17 @@ function adaptNativeDataChannel(raw) {
|
|
|
18900
19624
|
get onbufferedamountlow() {
|
|
18901
19625
|
return raw.onbufferedamountlow;
|
|
18902
19626
|
},
|
|
18903
|
-
send(
|
|
18904
|
-
raw.send(typeof
|
|
19627
|
+
send(data2) {
|
|
19628
|
+
raw.send(typeof data2 === "string" ? data2 : Buffer.from(data2));
|
|
18905
19629
|
},
|
|
18906
19630
|
close() {
|
|
18907
19631
|
raw.close();
|
|
18908
19632
|
}
|
|
18909
19633
|
};
|
|
18910
19634
|
}
|
|
18911
|
-
function toArrayBuffer2(
|
|
18912
|
-
if (typeof
|
|
18913
|
-
return
|
|
19635
|
+
function toArrayBuffer2(data2) {
|
|
19636
|
+
if (typeof data2 === "string") return data2;
|
|
19637
|
+
return data2.buffer.slice(data2.byteOffset, data2.byteOffset + data2.byteLength);
|
|
18914
19638
|
}
|
|
18915
19639
|
function normalizeNativeCandidate(candidate) {
|
|
18916
19640
|
const json = typeof candidate.toJSON === "function" ? candidate.toJSON() : {
|
|
@@ -18924,11 +19648,11 @@ function normalizeNativeCandidate(candidate) {
|
|
|
18924
19648
|
sdpMLineIndex: normalizeSdpMLineIndex(json.sdpMLineIndex)
|
|
18925
19649
|
};
|
|
18926
19650
|
}
|
|
18927
|
-
function normalizeNativeMessageData(
|
|
18928
|
-
if (typeof
|
|
18929
|
-
if (
|
|
18930
|
-
if (ArrayBuffer.isView(
|
|
18931
|
-
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);
|
|
18932
19656
|
}
|
|
18933
19657
|
return new Uint8Array().buffer;
|
|
18934
19658
|
}
|
|
@@ -18958,8 +19682,8 @@ function detectHostIpv4Candidates(interfaces, preferredHostIpv4Candidates = [])
|
|
|
18958
19682
|
return [...new Set(candidates.map((candidate) => candidate.ip))];
|
|
18959
19683
|
}
|
|
18960
19684
|
async function detectRouteHostIpv4Candidates(targets, timeoutMs = 500) {
|
|
18961
|
-
const detected = await Promise.all(targets.map(async (
|
|
18962
|
-
const routeTarget = parseRouteTarget(
|
|
19685
|
+
const detected = await Promise.all(targets.map(async (target2) => {
|
|
19686
|
+
const routeTarget = parseRouteTarget(target2);
|
|
18963
19687
|
return routeTarget === void 0 ? void 0 : await detectRouteHostIpv4(routeTarget, timeoutMs).catch(() => void 0);
|
|
18964
19688
|
}));
|
|
18965
19689
|
return [...new Set(detected.filter((ip) => ip !== void 0 && isUsableIpv4(ip)))];
|
|
@@ -19027,7 +19751,7 @@ function parseRouteTarget(value) {
|
|
|
19027
19751
|
return void 0;
|
|
19028
19752
|
}
|
|
19029
19753
|
}
|
|
19030
|
-
async function detectRouteHostIpv4(
|
|
19754
|
+
async function detectRouteHostIpv4(target2, timeoutMs) {
|
|
19031
19755
|
const socket = createSocket("udp4");
|
|
19032
19756
|
try {
|
|
19033
19757
|
return await new Promise((resolve3, reject) => {
|
|
@@ -19044,7 +19768,7 @@ async function detectRouteHostIpv4(target, timeoutMs) {
|
|
|
19044
19768
|
reject(error);
|
|
19045
19769
|
};
|
|
19046
19770
|
socket.once("error", onError);
|
|
19047
|
-
socket.connect(
|
|
19771
|
+
socket.connect(target2.port, target2.host, () => {
|
|
19048
19772
|
cleanup();
|
|
19049
19773
|
const address = socket.address();
|
|
19050
19774
|
resolve3(typeof address === "string" ? void 0 : address.address);
|
|
@@ -19119,6 +19843,7 @@ var ClientModeRuntime = class {
|
|
|
19119
19843
|
this.proxySwitch = apiProxy === void 0 ? void 0 : new ApiProxySwitch(apiProxy);
|
|
19120
19844
|
this.gatewaySwitch = new TypertGatewaySwitch(typertGateway);
|
|
19121
19845
|
}
|
|
19846
|
+
preview;
|
|
19122
19847
|
identity;
|
|
19123
19848
|
connected;
|
|
19124
19849
|
pendingWorkspaceSelection;
|
|
@@ -19170,6 +19895,11 @@ var ClientModeRuntime = class {
|
|
|
19170
19895
|
...this.host === void 0 ? {} : { host: this.host.hostStatus() }
|
|
19171
19896
|
};
|
|
19172
19897
|
}
|
|
19898
|
+
async closePreview() {
|
|
19899
|
+
const preview = this.preview;
|
|
19900
|
+
this.preview = void 0;
|
|
19901
|
+
await preview?.close();
|
|
19902
|
+
}
|
|
19173
19903
|
async detailedStatus() {
|
|
19174
19904
|
const connected = this.connected;
|
|
19175
19905
|
if (connected === void 0 || this.identity === void 0) return this.status();
|
|
@@ -19235,6 +19965,7 @@ var ClientModeRuntime = class {
|
|
|
19235
19965
|
this.pendingWorkspaceSelection = void 0;
|
|
19236
19966
|
await this.closeCodexVirtual();
|
|
19237
19967
|
this.proxySwitch?.selectLocal();
|
|
19968
|
+
await this.closePreview();
|
|
19238
19969
|
this.gatewaySwitch.selectLocal();
|
|
19239
19970
|
await this.closeCodexStreams(previous?.client);
|
|
19240
19971
|
await previous?.client.close().catch(() => void 0);
|
|
@@ -19256,6 +19987,7 @@ var ClientModeRuntime = class {
|
|
|
19256
19987
|
if (mode === "local") {
|
|
19257
19988
|
await this.closeCodexVirtual();
|
|
19258
19989
|
this.proxySwitch?.selectLocal();
|
|
19990
|
+
await this.closePreview();
|
|
19259
19991
|
this.gatewaySwitch.selectLocal();
|
|
19260
19992
|
const previous2 = this.connected;
|
|
19261
19993
|
this.connected = void 0;
|
|
@@ -19278,6 +20010,7 @@ var ClientModeRuntime = class {
|
|
|
19278
20010
|
throw error;
|
|
19279
20011
|
}
|
|
19280
20012
|
const previous = this.connected;
|
|
20013
|
+
await this.closePreview();
|
|
19281
20014
|
this.connected = next;
|
|
19282
20015
|
this.clearConnectionProgress(next.progressRunId);
|
|
19283
20016
|
this.pendingWorkspaceSelection = void 0;
|
|
@@ -19413,9 +20146,11 @@ var ClientModeRuntime = class {
|
|
|
19413
20146
|
return this.status();
|
|
19414
20147
|
}
|
|
19415
20148
|
async close() {
|
|
20149
|
+
await this.closePreview();
|
|
19416
20150
|
if (this.closed) return;
|
|
19417
20151
|
this.closed = true;
|
|
19418
20152
|
this.proxySwitch?.selectLocal();
|
|
20153
|
+
await this.closePreview();
|
|
19419
20154
|
this.gatewaySwitch.selectLocal();
|
|
19420
20155
|
this.pendingWorkspaceSelection = void 0;
|
|
19421
20156
|
await this.closeCodexVirtual();
|
|
@@ -19498,24 +20233,24 @@ var ClientModeRuntime = class {
|
|
|
19498
20233
|
this.codexStreams.set(value.streamId, stream);
|
|
19499
20234
|
return { opened: true, streamId: value.streamId, threadId: value.threadId };
|
|
19500
20235
|
}
|
|
19501
|
-
publishLocalCodexFrame = async (event,
|
|
19502
|
-
const streamId =
|
|
20236
|
+
publishLocalCodexFrame = async (event, data2) => {
|
|
20237
|
+
const streamId = data2.streamId;
|
|
19503
20238
|
const stream = this.codexStreams.get(streamId);
|
|
19504
20239
|
if (stream === void 0 || stream.target.kind !== "local") return;
|
|
19505
20240
|
if (event === "codex.app.frame") {
|
|
19506
|
-
this.appendCodexFrame(stream,
|
|
20241
|
+
this.appendCodexFrame(stream, data2);
|
|
19507
20242
|
return;
|
|
19508
20243
|
}
|
|
19509
|
-
const closed =
|
|
20244
|
+
const closed = data2;
|
|
19510
20245
|
stream.closed = typeof closed.reason === "string" ? closed.reason : "closed";
|
|
19511
20246
|
stream.wake();
|
|
19512
20247
|
};
|
|
19513
|
-
appendCodexFrame(stream,
|
|
19514
|
-
if (!isRecord7(
|
|
20248
|
+
appendCodexFrame(stream, data2) {
|
|
20249
|
+
if (!isRecord7(data2) || !isRecord7(data2.frame) || typeof data2.frame.method !== "string") return;
|
|
19515
20250
|
if (stream.frames.length >= 256) {
|
|
19516
20251
|
stream.closed = "overflow";
|
|
19517
20252
|
} else {
|
|
19518
|
-
stream.frames.push({ method:
|
|
20253
|
+
stream.frames.push({ method: data2.frame.method, params: data2.frame.params });
|
|
19519
20254
|
}
|
|
19520
20255
|
stream.wake();
|
|
19521
20256
|
}
|
|
@@ -19569,16 +20304,16 @@ var ClientModeRuntime = class {
|
|
|
19569
20304
|
}));
|
|
19570
20305
|
}
|
|
19571
20306
|
selectRemoteTarget(remote, transport = this.selectHarnessRemoteTransport(remote)) {
|
|
19572
|
-
const
|
|
20307
|
+
const target2 = { deviceId: remote.target.deviceId, name: remote.target.name };
|
|
19573
20308
|
if (transport === "remoteGateway") {
|
|
19574
|
-
this.gatewaySwitch.selectRemote(this.remoteTypertGateway(remote), void 0,
|
|
20309
|
+
this.gatewaySwitch.selectRemote(this.remoteTypertGateway(remote), void 0, target2);
|
|
19575
20310
|
return;
|
|
19576
20311
|
}
|
|
19577
|
-
this.proxySwitch?.selectRemote(new RemoteHarnessApiProxy(remote.client).api,
|
|
20312
|
+
this.proxySwitch?.selectRemote(new RemoteHarnessApiProxy(remote.client).api, target2);
|
|
19578
20313
|
this.gatewaySwitch.selectRemote((request) => invokeRemoteCommand(remote.client, request), {
|
|
19579
20314
|
execute: true,
|
|
19580
20315
|
list: remote.features.commandList
|
|
19581
|
-
},
|
|
20316
|
+
}, target2);
|
|
19582
20317
|
}
|
|
19583
20318
|
remoteTypertGateway(remote) {
|
|
19584
20319
|
const localSessionGeneration = harnessSessionGeneration(this.host?.localHarnessVersion?.());
|
|
@@ -19588,13 +20323,13 @@ var ClientModeRuntime = class {
|
|
|
19588
20323
|
);
|
|
19589
20324
|
}
|
|
19590
20325
|
selectCodexTarget(virtual, remote) {
|
|
19591
|
-
const
|
|
20326
|
+
const target2 = { deviceId: remote.target.deviceId, name: remote.target.name };
|
|
19592
20327
|
if (this.gatewaySwitch.supportsCarrier()) {
|
|
19593
|
-
this.gatewaySwitch.selectRemote(virtual, void 0,
|
|
20328
|
+
this.gatewaySwitch.selectRemote(virtual, void 0, target2);
|
|
19594
20329
|
return;
|
|
19595
20330
|
}
|
|
19596
|
-
this.proxySwitch.selectRemote(virtual.api,
|
|
19597
|
-
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);
|
|
19598
20333
|
}
|
|
19599
20334
|
async closeCodexVirtual() {
|
|
19600
20335
|
const virtual = this.codexVirtual;
|
|
@@ -19640,14 +20375,14 @@ var ClientModeRuntime = class {
|
|
|
19640
20375
|
throw new ClientModeError("MEMBERSHIP_REQUIRED", "The selected Host is not authorized for this account.");
|
|
19641
20376
|
}
|
|
19642
20377
|
this.updateConnectionProgress(progressRunId, "authorizing-peer");
|
|
19643
|
-
const
|
|
20378
|
+
const target2 = await this.authorizeHostPeer(serverDevice);
|
|
19644
20379
|
const presence = await this.server.presenceFor(targetDeviceId);
|
|
19645
20380
|
if (!presence.online) throw new ClientModeError("HOST_OFFLINE", "The selected Host is offline.", true);
|
|
19646
20381
|
const credentials = await this.server.authenticate(identity);
|
|
19647
20382
|
const rtcFactory = this.config.forceRelay ? void 0 : await this.rtcFactoryProvider({ routeTargets: [this.server.baseUrl] }).catch(() => void 0);
|
|
19648
20383
|
if (!this.config.forceRelay && rtcFactory === void 0) {
|
|
19649
20384
|
this.logger.warn("remote Harness WebRTC backend unavailable; using relay", {
|
|
19650
|
-
targetDeviceId: shortId(
|
|
20385
|
+
targetDeviceId: shortId(target2.deviceId)
|
|
19651
20386
|
});
|
|
19652
20387
|
}
|
|
19653
20388
|
let webRtcFallback = false;
|
|
@@ -19666,13 +20401,13 @@ var ClientModeRuntime = class {
|
|
|
19666
20401
|
onWebRtcFallback: (error, diagnostics) => {
|
|
19667
20402
|
webRtcFallback = true;
|
|
19668
20403
|
this.logger.warn(attempt === "direct" ? "remote Harness direct WebRTC failed; trying TURN" : "remote Harness TURN WebRTC failed; using relay", {
|
|
19669
|
-
targetDeviceId: shortId(
|
|
20404
|
+
targetDeviceId: shortId(target2.deviceId),
|
|
19670
20405
|
attempt,
|
|
19671
20406
|
reason: diagnosticReason(error)
|
|
19672
20407
|
});
|
|
19673
20408
|
if (diagnostics !== void 0) {
|
|
19674
20409
|
this.logger.debug("remote Harness WebRTC fallback diagnostics", {
|
|
19675
|
-
targetDeviceId: shortId(
|
|
20410
|
+
targetDeviceId: shortId(target2.deviceId),
|
|
19676
20411
|
attempt,
|
|
19677
20412
|
...webrtcDiagnosticsLogFields(diagnostics)
|
|
19678
20413
|
});
|
|
@@ -19687,7 +20422,7 @@ var ClientModeRuntime = class {
|
|
|
19687
20422
|
const stopProgressTimer = this.beginAttemptProgress(progressRunId, attempt);
|
|
19688
20423
|
try {
|
|
19689
20424
|
transport = createTransport(attempt);
|
|
19690
|
-
client = new RemoteClientCore(new ClientSecureTransport(transport, identity,
|
|
20425
|
+
client = new RemoteClientCore(new ClientSecureTransport(transport, identity, target2), 6e4);
|
|
19691
20426
|
await client.connect();
|
|
19692
20427
|
signal?.throwIfAborted();
|
|
19693
20428
|
} finally {
|
|
@@ -19699,7 +20434,7 @@ var ClientModeRuntime = class {
|
|
|
19699
20434
|
transport = void 0;
|
|
19700
20435
|
if (attempt === "turn") {
|
|
19701
20436
|
this.logger.info("remote Harness relay fallback re-established", {
|
|
19702
|
-
targetDeviceId: shortId(
|
|
20437
|
+
targetDeviceId: shortId(target2.deviceId)
|
|
19703
20438
|
});
|
|
19704
20439
|
}
|
|
19705
20440
|
}
|
|
@@ -19716,6 +20451,7 @@ var ClientModeRuntime = class {
|
|
|
19716
20451
|
);
|
|
19717
20452
|
connectedClient.onClose(() => {
|
|
19718
20453
|
if (this.connected?.client !== connectedClient) return;
|
|
20454
|
+
void this.closePreview();
|
|
19719
20455
|
this.connected = void 0;
|
|
19720
20456
|
this.connectionProgress = void 0;
|
|
19721
20457
|
this.pendingWorkspaceSelection = void 0;
|
|
@@ -19724,12 +20460,12 @@ var ClientModeRuntime = class {
|
|
|
19724
20460
|
this.gatewaySwitch.selectLocal();
|
|
19725
20461
|
void connectedClient.close().catch(() => void 0);
|
|
19726
20462
|
this.logger.warn("remote Harness transport closed; falling back to local mode", {
|
|
19727
|
-
targetDeviceId: shortId(
|
|
20463
|
+
targetDeviceId: shortId(target2.deviceId)
|
|
19728
20464
|
});
|
|
19729
20465
|
});
|
|
19730
20466
|
const connectionDetails = await connectedTransport.connectionDetails().catch(() => void 0);
|
|
19731
20467
|
this.logger.info("remote Harness transport ready", {
|
|
19732
|
-
targetDeviceId: shortId(
|
|
20468
|
+
targetDeviceId: shortId(target2.deviceId),
|
|
19733
20469
|
transport: connectedClient.getStats().mode,
|
|
19734
20470
|
...connectionDetails === void 0 ? {} : {
|
|
19735
20471
|
preferredTransports: connectionDetails.preferredTransports,
|
|
@@ -19739,14 +20475,14 @@ var ClientModeRuntime = class {
|
|
|
19739
20475
|
});
|
|
19740
20476
|
if (connectionDetails?.webRtc?.diagnostics !== void 0) {
|
|
19741
20477
|
this.logger.debug("remote Harness transport diagnostics", {
|
|
19742
|
-
targetDeviceId: shortId(
|
|
20478
|
+
targetDeviceId: shortId(target2.deviceId),
|
|
19743
20479
|
...webrtcDiagnosticsLogFields(connectionDetails.webRtc.diagnostics)
|
|
19744
20480
|
});
|
|
19745
20481
|
}
|
|
19746
20482
|
const features = await probeRemoteHostFeatures(connectedClient, serverDevice.clientVersion);
|
|
19747
20483
|
return {
|
|
19748
20484
|
client: connectedClient,
|
|
19749
|
-
target,
|
|
20485
|
+
target: target2,
|
|
19750
20486
|
transport: connectedTransport,
|
|
19751
20487
|
features,
|
|
19752
20488
|
progressRunId,
|
|
@@ -19763,6 +20499,7 @@ var ClientModeRuntime = class {
|
|
|
19763
20499
|
if (this.connected?.target.deviceId === targetDeviceId) return this.connected;
|
|
19764
20500
|
const next = await this.connect(targetDeviceId, signal);
|
|
19765
20501
|
const previous = this.connected;
|
|
20502
|
+
await this.closePreview();
|
|
19766
20503
|
this.connected = next;
|
|
19767
20504
|
this.clearConnectionProgress(next.progressRunId);
|
|
19768
20505
|
await previous?.client.close().catch(() => void 0);
|
|
@@ -19819,6 +20556,12 @@ var ClientModeRuntime = class {
|
|
|
19819
20556
|
}
|
|
19820
20557
|
return ok2(await this.pollClientOAuthQrLogin(value.qrId));
|
|
19821
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
|
+
}
|
|
19822
20565
|
if (endpoint === "directory.list") {
|
|
19823
20566
|
const value = record3(payload);
|
|
19824
20567
|
if (typeof value.targetDeviceId !== "string") throw new ClientModeError("INVALID_MESSAGE", "A Host is required.");
|
|
@@ -20083,6 +20826,7 @@ function remoteHostFeatures(clientVersion) {
|
|
|
20083
20826
|
return {
|
|
20084
20827
|
commandList: isVersionAtLeast(clientVersion, REMOTE_COMMAND_LIST_MIN_VERSION),
|
|
20085
20828
|
fileViewer: isVersionAtLeast(clientVersion, REMOTE_FILE_VIEWER_MIN_VERSION),
|
|
20829
|
+
terminal: false,
|
|
20086
20830
|
apiProxy: true,
|
|
20087
20831
|
remoteGateway: false,
|
|
20088
20832
|
codex: false
|
|
@@ -20104,6 +20848,7 @@ async function probeRemoteHostFeatures(client, clientVersion) {
|
|
|
20104
20848
|
const apiProxy = capabilities.has("harness.api.v1");
|
|
20105
20849
|
const remoteV1 = capabilities.has("harness.remote.v1");
|
|
20106
20850
|
const remoteV3 = capabilities.has("harness.remote.v3");
|
|
20851
|
+
const terminal = capabilities.has("harness.terminal.v1");
|
|
20107
20852
|
const codex = capabilities.has("codex.appserver.v1");
|
|
20108
20853
|
if (remoteV1 && remoteV3) {
|
|
20109
20854
|
throw new ClientModeError("INVALID_MESSAGE", "The remote Host advertised conflicting Harness Session formats.");
|
|
@@ -20116,6 +20861,7 @@ async function probeRemoteHostFeatures(client, clientVersion) {
|
|
|
20116
20861
|
return {
|
|
20117
20862
|
commandList: remoteGateway || apiProxy && fallback.commandList,
|
|
20118
20863
|
fileViewer: capabilities.has("fileviewer.read.v1"),
|
|
20864
|
+
terminal,
|
|
20119
20865
|
apiProxy,
|
|
20120
20866
|
remoteGateway,
|
|
20121
20867
|
...sessionFormat === void 0 ? {} : { sessionFormat },
|
|
@@ -20182,12 +20928,13 @@ function iceServersForAttempt(attempt, iceServers) {
|
|
|
20182
20928
|
|
|
20183
20929
|
// src/control-runtime.ts
|
|
20184
20930
|
import { hostname as hostname2 } from "node:os";
|
|
20931
|
+
import { execFileSync } from "node:child_process";
|
|
20185
20932
|
|
|
20186
20933
|
// src/identity-store.ts
|
|
20187
20934
|
import { createHash } from "node:crypto";
|
|
20188
|
-
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";
|
|
20189
20936
|
import { homedir } from "node:os";
|
|
20190
|
-
import { dirname as
|
|
20937
|
+
import { dirname as dirname4, join as join4 } from "node:path";
|
|
20191
20938
|
var identitySchema = external_exports.object({
|
|
20192
20939
|
schemaVersion: external_exports.literal(1),
|
|
20193
20940
|
deviceId: external_exports.string().uuid(),
|
|
@@ -20216,15 +20963,15 @@ var IdentityStore = class {
|
|
|
20216
20963
|
peers = /* @__PURE__ */ new Map();
|
|
20217
20964
|
constructor(options = {}) {
|
|
20218
20965
|
const env = options.env ?? process.env;
|
|
20219
|
-
const dshHome = env.DSH_HOME ||
|
|
20220
|
-
this.directory = options.directory ??
|
|
20966
|
+
const dshHome = env.DSH_HOME || join4(options.homeDirectory ?? homedir(), ".dsh");
|
|
20967
|
+
this.directory = options.directory ?? join4(dshHome, "remote");
|
|
20221
20968
|
}
|
|
20222
20969
|
async loadOrCreate(deviceName) {
|
|
20223
|
-
await
|
|
20224
|
-
await
|
|
20225
|
-
const devicePath =
|
|
20226
|
-
const keyPath =
|
|
20227
|
-
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)]);
|
|
20228
20975
|
if (hasDevice !== hasKey) {
|
|
20229
20976
|
throw new IdentityInvalidError("device identity is incomplete; repair it explicitly before reconnecting");
|
|
20230
20977
|
}
|
|
@@ -20235,10 +20982,10 @@ var IdentityStore = class {
|
|
|
20235
20982
|
await atomicTextWrite(keyPath, `${keys.privateKey}
|
|
20236
20983
|
`, 384);
|
|
20237
20984
|
}
|
|
20238
|
-
await
|
|
20985
|
+
await assertPrivateMode2(keyPath);
|
|
20239
20986
|
try {
|
|
20240
|
-
let record6 = identitySchema.parse(JSON.parse(await
|
|
20241
|
-
const privateKey = (await
|
|
20987
|
+
let record6 = identitySchema.parse(JSON.parse(await readFile3(devicePath, "utf8")));
|
|
20988
|
+
const privateKey = (await readFile3(keyPath, "utf8")).trim();
|
|
20242
20989
|
const regenerated = generateKeyPair(fromBase64Url2(privateKey));
|
|
20243
20990
|
if (regenerated.publicKey !== record6.publicKey) {
|
|
20244
20991
|
throw new IdentityInvalidError("device public and private keys do not match");
|
|
@@ -20260,7 +21007,7 @@ var IdentityStore = class {
|
|
|
20260
21007
|
return this.identity;
|
|
20261
21008
|
}
|
|
20262
21009
|
async reset(deviceName) {
|
|
20263
|
-
await
|
|
21010
|
+
await rm2(this.directory, { recursive: true, force: true });
|
|
20264
21011
|
this.identity = void 0;
|
|
20265
21012
|
this.peers.clear();
|
|
20266
21013
|
return this.loadOrCreate(deviceName);
|
|
@@ -20292,11 +21039,11 @@ var IdentityStore = class {
|
|
|
20292
21039
|
return removed;
|
|
20293
21040
|
}
|
|
20294
21041
|
async loadPeers() {
|
|
20295
|
-
const path =
|
|
20296
|
-
if (!await
|
|
21042
|
+
const path = join4(this.directory, "trusted-peers.json");
|
|
21043
|
+
if (!await exists2(path)) {
|
|
20297
21044
|
await atomicJsonWrite(path, { schemaVersion: 1, peers: [] }, 384);
|
|
20298
21045
|
}
|
|
20299
|
-
const parsed = trustedPeersSchema.parse(JSON.parse(await
|
|
21046
|
+
const parsed = trustedPeersSchema.parse(JSON.parse(await readFile3(path, "utf8")));
|
|
20300
21047
|
const peers = /* @__PURE__ */ new Map();
|
|
20301
21048
|
for (const peer of parsed.peers) {
|
|
20302
21049
|
if (peer.fingerprint !== fingerprint(peer.publicKey)) {
|
|
@@ -20308,7 +21055,7 @@ var IdentityStore = class {
|
|
|
20308
21055
|
this.peers = peers;
|
|
20309
21056
|
}
|
|
20310
21057
|
async savePeers() {
|
|
20311
|
-
await atomicJsonWrite(
|
|
21058
|
+
await atomicJsonWrite(join4(this.directory, "trusted-peers.json"), {
|
|
20312
21059
|
schemaVersion: 1,
|
|
20313
21060
|
peers: [...this.peers.values()]
|
|
20314
21061
|
}, 384);
|
|
@@ -20317,15 +21064,15 @@ var IdentityStore = class {
|
|
|
20317
21064
|
function serverStorageDirectory(root, serverUrl, role) {
|
|
20318
21065
|
const origin = new URL(serverUrl).origin;
|
|
20319
21066
|
const scope = createHash("sha256").update(origin).digest("hex").slice(0, 24);
|
|
20320
|
-
return
|
|
21067
|
+
return join4(root, "servers", scope, role);
|
|
20321
21068
|
}
|
|
20322
21069
|
function fingerprint(publicKey) {
|
|
20323
21070
|
const compact = createHash("sha256").update(fromBase64Url2(publicKey)).digest("hex").slice(0, 12).toUpperCase();
|
|
20324
21071
|
return compact.match(/.{1,4}/g).join(" ");
|
|
20325
21072
|
}
|
|
20326
|
-
async function
|
|
21073
|
+
async function assertPrivateMode2(path) {
|
|
20327
21074
|
if (process.platform === "win32") return;
|
|
20328
|
-
const mode = (await
|
|
21075
|
+
const mode = (await stat2(path)).mode & 511;
|
|
20329
21076
|
if ((mode & 63) !== 0) {
|
|
20330
21077
|
throw new IdentityInvalidError(`private key permissions must be 0600, got ${mode.toString(8).padStart(3, "0")}`);
|
|
20331
21078
|
}
|
|
@@ -20335,16 +21082,16 @@ async function atomicJsonWrite(path, value, mode) {
|
|
|
20335
21082
|
`, mode);
|
|
20336
21083
|
}
|
|
20337
21084
|
async function atomicTextWrite(path, value, mode) {
|
|
20338
|
-
await
|
|
21085
|
+
await mkdir2(dirname4(path), { recursive: true, mode: 448 });
|
|
20339
21086
|
const temporary = `${path}.${process.pid}.${uuidV7()}.tmp`;
|
|
20340
|
-
await
|
|
20341
|
-
await
|
|
20342
|
-
await
|
|
20343
|
-
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);
|
|
20344
21091
|
}
|
|
20345
|
-
async function
|
|
21092
|
+
async function exists2(path) {
|
|
20346
21093
|
try {
|
|
20347
|
-
await
|
|
21094
|
+
await stat2(path);
|
|
20348
21095
|
return true;
|
|
20349
21096
|
} catch (error) {
|
|
20350
21097
|
if (isNodeError(error) && error.code === "ENOENT") return false;
|
|
@@ -20358,74 +21105,148 @@ function safeErrorMessage(error) {
|
|
|
20358
21105
|
return error instanceof Error ? error.message : "invalid identity data";
|
|
20359
21106
|
}
|
|
20360
21107
|
|
|
20361
|
-
// src/
|
|
20362
|
-
|
|
20363
|
-
|
|
20364
|
-
var
|
|
20365
|
-
|
|
20366
|
-
|
|
20367
|
-
|
|
20368
|
-
|
|
20369
|
-
|
|
20370
|
-
|
|
20371
|
-
|
|
20372
|
-
|
|
20373
|
-
|
|
20374
|
-
|
|
20375
|
-
|
|
20376
|
-
|
|
20377
|
-
|
|
20378
|
-
|
|
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);
|
|
20379
21165
|
}
|
|
20380
|
-
|
|
20381
|
-
|
|
20382
|
-
|
|
20383
|
-
|
|
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;
|
|
20384
21198
|
try {
|
|
20385
|
-
|
|
20386
|
-
|
|
20387
|
-
|
|
21199
|
+
const snapshot = await this.readSnapshot();
|
|
21200
|
+
if (snapshot !== void 0 && !this.closed) this.write(snapshot);
|
|
21201
|
+
} finally {
|
|
21202
|
+
this.sampling = false;
|
|
20388
21203
|
}
|
|
20389
|
-
return parsed.serverUrl === serverUrl && parsed.deviceId === deviceId ? parsed : void 0;
|
|
20390
21204
|
}
|
|
20391
|
-
async
|
|
20392
|
-
|
|
20393
|
-
|
|
20394
|
-
|
|
20395
|
-
|
|
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
|
+
}
|
|
20396
21212
|
}
|
|
20397
|
-
|
|
20398
|
-
|
|
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;
|
|
20399
21238
|
}
|
|
20400
|
-
|
|
20401
|
-
|
|
20402
|
-
|
|
20403
|
-
|
|
20404
|
-
|
|
20405
|
-
|
|
20406
|
-
|
|
20407
|
-
|
|
20408
|
-
|
|
20409
|
-
await rename2(temporary, path);
|
|
20410
|
-
await chmod2(path, 384);
|
|
20411
|
-
}
|
|
20412
|
-
async function assertPrivateMode2(path) {
|
|
20413
|
-
if (process.platform === "win32") return;
|
|
20414
|
-
const mode = (await stat2(path)).mode & 511;
|
|
20415
|
-
if ((mode & 63) !== 0) throw new ServerCredentialsInvalidError("server credentials permissions must be 0600");
|
|
20416
|
-
}
|
|
20417
|
-
async function exists2(path) {
|
|
20418
|
-
try {
|
|
20419
|
-
await stat2(path);
|
|
20420
|
-
return true;
|
|
20421
|
-
} catch (error) {
|
|
20422
|
-
if (error instanceof Error && "code" in error && error.code === "ENOENT") return false;
|
|
20423
|
-
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
|
+
}
|
|
20424
21248
|
}
|
|
20425
|
-
}
|
|
20426
|
-
function safeMessage(error) {
|
|
20427
|
-
return error instanceof Error ? error.message : "invalid credential data";
|
|
20428
|
-
}
|
|
21249
|
+
};
|
|
20429
21250
|
|
|
20430
21251
|
// src/control-runtime.ts
|
|
20431
21252
|
var PluginControlRuntime = class {
|
|
@@ -20437,15 +21258,35 @@ var PluginControlRuntime = class {
|
|
|
20437
21258
|
this.host = host;
|
|
20438
21259
|
}
|
|
20439
21260
|
register(connection, webServer) {
|
|
20440
|
-
|
|
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;
|
|
20441
21278
|
}
|
|
20442
21279
|
async handle(endpoint, payload, signal) {
|
|
20443
21280
|
try {
|
|
21281
|
+
if (endpoint === "settings.development.set") return ok3(await this.setDevelopment(payload));
|
|
20444
21282
|
if (endpoint === "settings.get") return ok3(await this.settingsView());
|
|
20445
21283
|
if (endpoint === "settings.configure") return ok3(await this.configure(payload));
|
|
20446
21284
|
if (endpoint === "settings.server.set") return ok3(await this.setServer(payload));
|
|
20447
21285
|
if (endpoint === "settings.role.set") return ok3(await this.setRole(payload));
|
|
20448
21286
|
if (endpoint === "settings.codex.set") return ok3(await this.setCodex(payload));
|
|
21287
|
+
if (endpoint === "settings.acp.set") return ok3(await this.setAcp(payload));
|
|
21288
|
+
if (endpoint === "settings.acp.add") return ok3(await this.addAcp(payload));
|
|
21289
|
+
if (endpoint === "settings.acp.remove") return ok3(await this.removeAcp(payload));
|
|
20449
21290
|
if (endpoint === "settings.logout") return ok3(await this.logout());
|
|
20450
21291
|
if (endpoint === "host.reconnect") {
|
|
20451
21292
|
if (this.host === void 0) throw new ClientModeError("METHOD_NOT_ALLOWED", "This plugin is not running as a Host.");
|
|
@@ -20541,6 +21382,23 @@ var PluginControlRuntime = class {
|
|
|
20541
21382
|
await this.settings.replace({ ...current, role });
|
|
20542
21383
|
return this.settingsView();
|
|
20543
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
|
+
}
|
|
20544
21402
|
async setCodex(payload) {
|
|
20545
21403
|
if (this.settings === void 0) {
|
|
20546
21404
|
throw new ClientModeError("SETTINGS_UNAVAILABLE", "DSH user settings are unavailable in this profile.");
|
|
@@ -20557,6 +21415,37 @@ var PluginControlRuntime = class {
|
|
|
20557
21415
|
await this.settings.replace(editableConfig(next));
|
|
20558
21416
|
return this.settingsView();
|
|
20559
21417
|
}
|
|
21418
|
+
async setAcp(payload) {
|
|
21419
|
+
if (this.settings === void 0) throw new ClientModeError("SETTINGS_UNAVAILABLE", "DSH user settings are unavailable in this profile.");
|
|
21420
|
+
const value = record4(payload);
|
|
21421
|
+
if (typeof value.backend !== "string" || typeof value.enabled !== "boolean") throw new ClientModeError("INVALID_MESSAGE", "ACP backend and enabled are required.");
|
|
21422
|
+
const current = resolveConfig(this.settings.get());
|
|
21423
|
+
const backends = current.acp?.backends.map((item) => item.id === value.backend ? { ...item, enabled: value.enabled } : item) ?? [];
|
|
21424
|
+
await this.settings.replace({ ...editableConfig(current), acp: { enabled: current.acp?.enabled ?? true, backends } });
|
|
21425
|
+
return this.settingsView();
|
|
21426
|
+
}
|
|
21427
|
+
async addAcp(payload) {
|
|
21428
|
+
if (this.settings === void 0) throw new ClientModeError("SETTINGS_UNAVAILABLE", "DSH user settings are unavailable in this profile.");
|
|
21429
|
+
const value = record4(payload);
|
|
21430
|
+
if (typeof value.id !== "string" || typeof value.command !== "string" || !Array.isArray(value.args) || !value.args.every((item) => typeof item === "string")) {
|
|
21431
|
+
throw new ClientModeError("INVALID_MESSAGE", "ACP name, command, and arguments are required.");
|
|
21432
|
+
}
|
|
21433
|
+
const current = resolveConfig(this.settings.get());
|
|
21434
|
+
if (current.acp?.backends.some((item) => item.id === value.id)) throw new ClientModeError("INVALID_MESSAGE", "ACP backend already exists.");
|
|
21435
|
+
const backends = [...current.acp?.backends ?? [], { id: value.id, command: value.command, args: value.args, enabled: false }];
|
|
21436
|
+
const next = resolveConfig({ ...editableConfig(current), acp: { enabled: current.acp?.enabled ?? true, backends } });
|
|
21437
|
+
await this.settings.replace(editableConfig(next));
|
|
21438
|
+
return this.settingsView();
|
|
21439
|
+
}
|
|
21440
|
+
async removeAcp(payload) {
|
|
21441
|
+
if (this.settings === void 0) throw new ClientModeError("SETTINGS_UNAVAILABLE", "DSH user settings are unavailable in this profile.");
|
|
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.");
|
|
21444
|
+
const current = resolveConfig(this.settings.get());
|
|
21445
|
+
const backends = (current.acp?.backends ?? []).filter((item) => item.id !== id4);
|
|
21446
|
+
await this.settings.replace(editableConfig({ ...current, acp: { enabled: current.acp?.enabled ?? true, backends } }));
|
|
21447
|
+
return this.settingsView();
|
|
21448
|
+
}
|
|
20560
21449
|
async authorizeOwnedRole(serverUrl, sourceRole, targetRole) {
|
|
20561
21450
|
const sourceDirectory = serverStorageDirectory(this.identityDirectory, serverUrl, sourceRole);
|
|
20562
21451
|
const sourceIdentity = await new IdentityStore({ directory: sourceDirectory }).loadOrCreate(hostname2());
|
|
@@ -20597,6 +21486,7 @@ var PluginControlRuntime = class {
|
|
|
20597
21486
|
writable: this.settings !== void 0,
|
|
20598
21487
|
applies: "restart",
|
|
20599
21488
|
associations,
|
|
21489
|
+
acpAvailability: Object.fromEntries((config.acp?.backends ?? []).map((item) => [item.id, commandAvailable(item.command ?? "")])),
|
|
20600
21490
|
...association === void 0 ? {} : { association }
|
|
20601
21491
|
};
|
|
20602
21492
|
}
|
|
@@ -20633,11 +21523,21 @@ var PluginControlRuntime = class {
|
|
|
20633
21523
|
};
|
|
20634
21524
|
}
|
|
20635
21525
|
};
|
|
21526
|
+
function commandAvailable(command) {
|
|
21527
|
+
try {
|
|
21528
|
+
execFileSync(process.platform === "win32" ? "where" : "which", [command], { stdio: "ignore" });
|
|
21529
|
+
return true;
|
|
21530
|
+
} catch {
|
|
21531
|
+
return false;
|
|
21532
|
+
}
|
|
21533
|
+
}
|
|
20636
21534
|
function editableConfig(config) {
|
|
20637
21535
|
return {
|
|
20638
21536
|
enabled: config.enabled,
|
|
20639
21537
|
role: config.role,
|
|
20640
21538
|
...config.serverUrl === void 0 ? {} : { serverUrl: config.serverUrl },
|
|
21539
|
+
terminal: config.terminal,
|
|
21540
|
+
loopback: config.loopback,
|
|
20641
21541
|
forceRelay: config.forceRelay,
|
|
20642
21542
|
logLevel: config.logLevel,
|
|
20643
21543
|
reconnect: config.reconnect.enabled ? {
|
|
@@ -20648,7 +21548,8 @@ function editableConfig(config) {
|
|
|
20648
21548
|
codex: {
|
|
20649
21549
|
enabled: config.codex.enabled,
|
|
20650
21550
|
binary: config.codex.binary
|
|
20651
|
-
}
|
|
21551
|
+
},
|
|
21552
|
+
...config.acp === void 0 ? {} : { acp: { enabled: config.acp.enabled, backends: config.acp.backends } }
|
|
20652
21553
|
};
|
|
20653
21554
|
}
|
|
20654
21555
|
function isRecord8(value) {
|
|
@@ -20707,8 +21608,70 @@ function redact(value, key = "") {
|
|
|
20707
21608
|
return Object.fromEntries(Object.entries(value).map(([childKey, child]) => [childKey, redact(child, childKey)]));
|
|
20708
21609
|
}
|
|
20709
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
|
+
|
|
20710
21673
|
// src/service.ts
|
|
20711
|
-
import { randomUUID as
|
|
21674
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
20712
21675
|
|
|
20713
21676
|
// src/connection-controller.ts
|
|
20714
21677
|
var ConnectionController = class {
|
|
@@ -20779,7 +21742,21 @@ var ConnectionController = class {
|
|
|
20779
21742
|
return this.active.size;
|
|
20780
21743
|
}
|
|
20781
21744
|
peerDeviceIds() {
|
|
20782
|
-
return
|
|
21745
|
+
return this.connectedPeers().map((peer) => peer.deviceId);
|
|
21746
|
+
}
|
|
21747
|
+
connectedPeers() {
|
|
21748
|
+
const peers = [];
|
|
21749
|
+
const seen = /* @__PURE__ */ new Set();
|
|
21750
|
+
for (const connection of this.active.values()) {
|
|
21751
|
+
const deviceId = connection.channel.peerDeviceId;
|
|
21752
|
+
if (seen.has(deviceId)) continue;
|
|
21753
|
+
seen.add(deviceId);
|
|
21754
|
+
peers.push({
|
|
21755
|
+
deviceId,
|
|
21756
|
+
...connection.channel.mode === void 0 ? {} : { mode: connection.channel.mode }
|
|
21757
|
+
});
|
|
21758
|
+
}
|
|
21759
|
+
return peers;
|
|
20783
21760
|
}
|
|
20784
21761
|
peerDeviceId() {
|
|
20785
21762
|
const peers = this.peerDeviceIds();
|
|
@@ -20878,21 +21855,6 @@ function shortId2(value) {
|
|
|
20878
21855
|
return value.length <= 12 ? value : `${value.slice(0, 8)}\u2026${value.slice(-4)}`;
|
|
20879
21856
|
}
|
|
20880
21857
|
|
|
20881
|
-
// src/safe-error.ts
|
|
20882
|
-
var RpcError = class extends Error {
|
|
20883
|
-
constructor(code, message, details, retryable = false) {
|
|
20884
|
-
super(message);
|
|
20885
|
-
this.code = code;
|
|
20886
|
-
this.details = details;
|
|
20887
|
-
this.retryable = retryable;
|
|
20888
|
-
}
|
|
20889
|
-
};
|
|
20890
|
-
function safeErrorCode(error) {
|
|
20891
|
-
if (error instanceof RpcError) return error.code;
|
|
20892
|
-
if (error instanceof external_exports.ZodError) return "INVALID_MESSAGE";
|
|
20893
|
-
return "INTERNAL_ERROR";
|
|
20894
|
-
}
|
|
20895
|
-
|
|
20896
21858
|
// src/rpc-router.ts
|
|
20897
21859
|
var wireRequestSchema = external_exports.object({ method: external_exports.string().min(1), params: external_exports.unknown() }).strict();
|
|
20898
21860
|
var emptyParamsSchema = external_exports.object({}).strict();
|
|
@@ -20915,6 +21877,7 @@ var apiMethods = /* @__PURE__ */ new Set([
|
|
|
20915
21877
|
"harness.remote.transfer.close",
|
|
20916
21878
|
"harness.remote.stream.open",
|
|
20917
21879
|
"harness.remote.stream.close",
|
|
21880
|
+
"loopback.call",
|
|
20918
21881
|
"fileviewer.call",
|
|
20919
21882
|
"codex.app.call",
|
|
20920
21883
|
"codex.app.respond",
|
|
@@ -20924,7 +21887,14 @@ var apiMethods = /* @__PURE__ */ new Set([
|
|
|
20924
21887
|
"codex.app.transfer.chunk",
|
|
20925
21888
|
"codex.app.transfer.commit",
|
|
20926
21889
|
"codex.app.transfer.read",
|
|
20927
|
-
"codex.app.transfer.close"
|
|
21890
|
+
"codex.app.transfer.close",
|
|
21891
|
+
"acp.initialize",
|
|
21892
|
+
"acp.session.new",
|
|
21893
|
+
"acp.session.load",
|
|
21894
|
+
"acp.session.prompt",
|
|
21895
|
+
"acp.session.respond_permission",
|
|
21896
|
+
"acp.session.cancel",
|
|
21897
|
+
"acp.session.set_mode"
|
|
20928
21898
|
]);
|
|
20929
21899
|
var HOST_CAPABILITIES = [
|
|
20930
21900
|
"harness.api.v1",
|
|
@@ -20933,10 +21903,11 @@ var HOST_CAPABILITIES = [
|
|
|
20933
21903
|
"harness.remote.transfer.v1",
|
|
20934
21904
|
"fileviewer.read.v1",
|
|
20935
21905
|
"codex.appserver.v1",
|
|
20936
|
-
"codex.appserver.transfer.v1"
|
|
21906
|
+
"codex.appserver.transfer.v1",
|
|
21907
|
+
"agent.acp.v1"
|
|
20937
21908
|
];
|
|
20938
21909
|
var RpcRouter = class {
|
|
20939
|
-
constructor(harnessApi, maxPending = 128, logger, fileViewer, harnessRemote, capabilities = () => HOST_CAPABILITIES, codex) {
|
|
21910
|
+
constructor(harnessApi, maxPending = 128, logger, fileViewer, harnessRemote, capabilities = () => HOST_CAPABILITIES, codex, acp, loopback) {
|
|
20940
21911
|
this.harnessApi = harnessApi;
|
|
20941
21912
|
this.maxPending = maxPending;
|
|
20942
21913
|
this.logger = logger;
|
|
@@ -20944,9 +21915,12 @@ var RpcRouter = class {
|
|
|
20944
21915
|
this.harnessRemote = harnessRemote;
|
|
20945
21916
|
this.capabilities = capabilities;
|
|
20946
21917
|
this.codex = codex;
|
|
21918
|
+
this.acp = acp;
|
|
21919
|
+
this.loopback = loopback;
|
|
20947
21920
|
}
|
|
20948
21921
|
active = 0;
|
|
20949
21922
|
async closePeerStreams() {
|
|
21923
|
+
this.loopback?.closeAll();
|
|
20950
21924
|
await Promise.all([
|
|
20951
21925
|
this.harnessApi?.closeAll(),
|
|
20952
21926
|
this.harnessRemote?.closeAll(),
|
|
@@ -21028,6 +22002,10 @@ var RpcRouter = class {
|
|
|
21028
22002
|
return this.requireRemoteGateway().openStream(params);
|
|
21029
22003
|
case "harness.remote.stream.close":
|
|
21030
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
|
+
}
|
|
21031
22009
|
case "fileviewer.call": {
|
|
21032
22010
|
if (this.fileViewer === void 0) {
|
|
21033
22011
|
throw new RpcError("FILE_VIEWER_UNAVAILABLE", "The Remote Host does not have DSH File Viewer available.");
|
|
@@ -21052,6 +22030,20 @@ var RpcRouter = class {
|
|
|
21052
22030
|
return this.requireCodex().readTransfer(params);
|
|
21053
22031
|
case "codex.app.transfer.close":
|
|
21054
22032
|
return this.requireCodex().closeTransfer(params);
|
|
22033
|
+
case "acp.initialize":
|
|
22034
|
+
return this.requireAcp().initialize(params);
|
|
22035
|
+
case "acp.session.new":
|
|
22036
|
+
return this.requireAcp().sessionNew(params);
|
|
22037
|
+
case "acp.session.load":
|
|
22038
|
+
return this.requireAcp().sessionLoad(params);
|
|
22039
|
+
case "acp.session.prompt":
|
|
22040
|
+
return this.requireAcp().prompt(params, async () => void 0);
|
|
22041
|
+
case "acp.session.respond_permission":
|
|
22042
|
+
return this.requireAcp().respondPermission(params);
|
|
22043
|
+
case "acp.session.cancel":
|
|
22044
|
+
return this.requireAcp().cancel(params);
|
|
22045
|
+
case "acp.session.set_mode":
|
|
22046
|
+
return this.requireAcp().setMode(params);
|
|
21055
22047
|
default:
|
|
21056
22048
|
throw new RpcError("METHOD_NOT_FOUND", "The requested method does not exist.");
|
|
21057
22049
|
}
|
|
@@ -21068,6 +22060,10 @@ var RpcRouter = class {
|
|
|
21068
22060
|
}
|
|
21069
22061
|
return this.harnessRemote;
|
|
21070
22062
|
}
|
|
22063
|
+
requireAcp() {
|
|
22064
|
+
if (!this.acp) throw new RpcError("CAPABILITY_NOT_SUPPORTED", "ACP is not configured on this Host.");
|
|
22065
|
+
return this.acp;
|
|
22066
|
+
}
|
|
21071
22067
|
requireCodex() {
|
|
21072
22068
|
if (this.codex === void 0) {
|
|
21073
22069
|
throw new RpcError("FEATURE_NOT_SUPPORTED", "Codex Remote is disabled or unavailable on this Host.");
|
|
@@ -21230,6 +22226,7 @@ var HostServerConnection = class {
|
|
|
21230
22226
|
lastActiveAt;
|
|
21231
22227
|
reconnectRequested = false;
|
|
21232
22228
|
resumeQueued = false;
|
|
22229
|
+
authRecoveryAttempted = false;
|
|
21233
22230
|
rtcFactory;
|
|
21234
22231
|
negotiatedCapabilities = ["transport.relay"];
|
|
21235
22232
|
controlFrameLimits = {};
|
|
@@ -21293,14 +22290,23 @@ var HostServerConnection = class {
|
|
|
21293
22290
|
}
|
|
21294
22291
|
async run() {
|
|
21295
22292
|
let delayMs = this.config.reconnect.initialDelayMs;
|
|
22293
|
+
this.authRecoveryAttempted = false;
|
|
21296
22294
|
while (!this.stopped) {
|
|
21297
22295
|
try {
|
|
21298
22296
|
await this.connectOnce();
|
|
21299
22297
|
delayMs = this.config.reconnect.initialDelayMs;
|
|
21300
22298
|
} catch (error) {
|
|
21301
22299
|
const code = errorCode2(error);
|
|
22300
|
+
if (code === "CREDENTIALS_REFRESHED") continue;
|
|
21302
22301
|
this.terminalError = code;
|
|
21303
|
-
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
|
+
}
|
|
21304
22310
|
if (TERMINAL_AUTH_ERRORS.has(code) || !this.config.reconnect.enabled) return;
|
|
21305
22311
|
}
|
|
21306
22312
|
if (this.stopped) return;
|
|
@@ -21356,6 +22362,7 @@ var HostServerConnection = class {
|
|
|
21356
22362
|
this.lastActiveAt = Date.now();
|
|
21357
22363
|
if (frame.type === "hello.ack") {
|
|
21358
22364
|
const payload = requireHelloAck(frame.payload);
|
|
22365
|
+
this.authRecoveryAttempted = false;
|
|
21359
22366
|
this.controlFrameLimits = {
|
|
21360
22367
|
maxControlFrameBytes: payload.maxControlFrameBytes,
|
|
21361
22368
|
maxRelayFrameBytes: payload.maxRelayFrameBytes
|
|
@@ -21392,13 +22399,28 @@ var HostServerConnection = class {
|
|
|
21392
22399
|
socket.onclose = (event) => {
|
|
21393
22400
|
const close = async () => {
|
|
21394
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
|
+
}
|
|
21395
22410
|
if (event.code === 4002) {
|
|
22411
|
+
if (this.authRecoveryAttempted) {
|
|
22412
|
+
finish(new ControlConnectionError("AUTH_INVALID", "Server rejected refreshed credentials."));
|
|
22413
|
+
return;
|
|
22414
|
+
}
|
|
21396
22415
|
try {
|
|
21397
|
-
await this.api.refreshCredentials();
|
|
22416
|
+
await this.api.refreshCredentials(credentials.accessToken);
|
|
21398
22417
|
} catch (error) {
|
|
21399
22418
|
finish(asError2(error));
|
|
21400
22419
|
return;
|
|
21401
22420
|
}
|
|
22421
|
+
this.authRecoveryAttempted = true;
|
|
22422
|
+
finish(new ControlConnectionError("CREDENTIALS_REFRESHED", "Retry hello with refreshed credentials."));
|
|
22423
|
+
return;
|
|
21402
22424
|
}
|
|
21403
22425
|
if (event.code === 4004) {
|
|
21404
22426
|
finish(new ControlConnectionError("DEVICE_REVOKED", "The Server revoked this Host device."));
|
|
@@ -21646,7 +22668,7 @@ var HostServerConnection = class {
|
|
|
21646
22668
|
}
|
|
21647
22669
|
});
|
|
21648
22670
|
tunnel.rtc = rtc;
|
|
21649
|
-
rtc.onMessage((
|
|
22671
|
+
rtc.onMessage((data2) => tunnel.channel?.receive(void 0, data2));
|
|
21650
22672
|
rtc.onClose(() => {
|
|
21651
22673
|
void this.handleRtcFailed(tunnel, rtc, new Error("WebRTC data channel closed."));
|
|
21652
22674
|
});
|
|
@@ -21803,6 +22825,10 @@ var HostServerConnection = class {
|
|
|
21803
22825
|
async sendRelay(tunnel, ciphertext) {
|
|
21804
22826
|
const counter = Number(tunnel.noise.sendingCounter() - 1n);
|
|
21805
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");
|
|
21806
22832
|
this.sendControl("relay", {
|
|
21807
22833
|
connectionId: tunnel.connectionId,
|
|
21808
22834
|
targetDeviceId: tunnel.peer.deviceId,
|
|
@@ -21868,6 +22894,8 @@ var HostServerConnection = class {
|
|
|
21868
22894
|
}
|
|
21869
22895
|
};
|
|
21870
22896
|
var TERMINAL_AUTH_ERRORS = /* @__PURE__ */ new Set([
|
|
22897
|
+
"CONNECTION_REPLACED",
|
|
22898
|
+
"SERVER_CREDENTIALS_BUSY",
|
|
21871
22899
|
"ACCOUNT_AUTH_REQUIRED",
|
|
21872
22900
|
"AUTH_INVALID",
|
|
21873
22901
|
"DEVICE_OWNERSHIP_REQUIRED",
|
|
@@ -21888,6 +22916,7 @@ var ServerNoiseChannel = class {
|
|
|
21888
22916
|
this.peerDeviceId = tunnel.peer.deviceId;
|
|
21889
22917
|
this.peerIdentityKey = tunnel.peer.publicKey;
|
|
21890
22918
|
}
|
|
22919
|
+
sends = new SerialSend();
|
|
21891
22920
|
security;
|
|
21892
22921
|
peerDeviceId;
|
|
21893
22922
|
peerIdentityKey;
|
|
@@ -21898,11 +22927,12 @@ var ServerNoiseChannel = class {
|
|
|
21898
22927
|
closed = false;
|
|
21899
22928
|
async send(message) {
|
|
21900
22929
|
if (this.closed) throw new Error("secure channel is closed");
|
|
21901
|
-
const
|
|
22930
|
+
const encoded = encodeMessage(message);
|
|
21902
22931
|
try {
|
|
21903
|
-
|
|
21904
|
-
|
|
21905
|
-
|
|
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
|
+
});
|
|
21906
22936
|
} catch (error) {
|
|
21907
22937
|
await this.close().catch(() => void 0);
|
|
21908
22938
|
throw error;
|
|
@@ -21947,10 +22977,10 @@ function websocketUrl2(baseUrl) {
|
|
|
21947
22977
|
url.pathname = `${url.pathname.replace(/\/$/, "")}/ws/v1/connect`;
|
|
21948
22978
|
return url.toString();
|
|
21949
22979
|
}
|
|
21950
|
-
function decodeControl(
|
|
21951
|
-
if (typeof
|
|
22980
|
+
function decodeControl(data2, limits) {
|
|
22981
|
+
if (typeof data2 !== "string") throw new ControlConnectionError("INVALID_MESSAGE", "Server control frames must be text JSON.");
|
|
21952
22982
|
try {
|
|
21953
|
-
return decodeControlFrame(
|
|
22983
|
+
return decodeControlFrame(data2, limits);
|
|
21954
22984
|
} catch {
|
|
21955
22985
|
throw new ControlConnectionError("INVALID_MESSAGE", "Server sent an invalid control frame.");
|
|
21956
22986
|
}
|
|
@@ -22056,10 +23086,11 @@ function errorCode2(error) {
|
|
|
22056
23086
|
return error instanceof ServerApiError || error instanceof ControlConnectionError ? error.code : "CONNECTION_FAILED";
|
|
22057
23087
|
}
|
|
22058
23088
|
function isRetryable(error) {
|
|
22059
|
-
return error instanceof ServerApiError
|
|
23089
|
+
return !TERMINAL_AUTH_ERRORS.has(errorCode2(error)) && (!(error instanceof ServerApiError) || error.retryable);
|
|
22060
23090
|
}
|
|
22061
23091
|
function closeCode(code) {
|
|
22062
23092
|
if (code === 4002) return "AUTH_INVALID";
|
|
23093
|
+
if (code === 4003) return "CONNECTION_REPLACED";
|
|
22063
23094
|
if (code === 4004) return "DEVICE_REVOKED";
|
|
22064
23095
|
if (code === 4007) return "RATE_LIMITED";
|
|
22065
23096
|
if (code === 4011) return "UNSUPPORTED_VERSION";
|
|
@@ -22074,13 +23105,13 @@ var MAX_ENTRIES = 500;
|
|
|
22074
23105
|
async function listRemoteDirectory(path, signal) {
|
|
22075
23106
|
signal?.throwIfAborted();
|
|
22076
23107
|
const home = resolve(homedir2());
|
|
22077
|
-
const
|
|
22078
|
-
if (!isAbsolute2(
|
|
22079
|
-
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 });
|
|
22080
23111
|
const directories = [];
|
|
22081
23112
|
for (const row of rows) {
|
|
22082
23113
|
signal?.throwIfAborted();
|
|
22083
|
-
const child = resolve(
|
|
23114
|
+
const child = resolve(target2, row.name);
|
|
22084
23115
|
let directory = row.isDirectory();
|
|
22085
23116
|
if (!directory && row.isSymbolicLink()) directory = await stat3(child).then((value) => value.isDirectory()).catch(() => false);
|
|
22086
23117
|
if (!directory) continue;
|
|
@@ -22088,9 +23119,9 @@ async function listRemoteDirectory(path, signal) {
|
|
|
22088
23119
|
}
|
|
22089
23120
|
directories.sort((left, right) => left.name.localeCompare(right.name, void 0, { sensitivity: "base" }));
|
|
22090
23121
|
return {
|
|
22091
|
-
path:
|
|
23122
|
+
path: target2,
|
|
22092
23123
|
home,
|
|
22093
|
-
crumbs: crumbs(
|
|
23124
|
+
crumbs: crumbs(target2),
|
|
22094
23125
|
entries: directories.slice(0, MAX_ENTRIES),
|
|
22095
23126
|
truncated: directories.length > MAX_ENTRIES
|
|
22096
23127
|
};
|
|
@@ -22154,11 +23185,22 @@ var transferCloseSchema = external_exports.object({ transferId: transferIdSchema
|
|
|
22154
23185
|
var commandExecuteSchema = external_exports.object({
|
|
22155
23186
|
agentId: external_exports.string().min(1).max(128),
|
|
22156
23187
|
line: external_exports.string().min(1).max(2048),
|
|
22157
|
-
//
|
|
22158
|
-
//
|
|
22159
|
-
//
|
|
22160
|
-
|
|
22161
|
-
|
|
23188
|
+
// dsh-commands <= 0.1.2 calls this field `images`; dsh-commands 0.1.5
|
|
23189
|
+
// renamed it to `submittedAttachments` and expanded its element type to
|
|
23190
|
+
// include staged file receipts. Remote command execution keeps attachments
|
|
23191
|
+
// out of scope, so either compatibility field is restricted to an empty
|
|
23192
|
+
// list. The dispatcher below chooses the matching Host descriptor.
|
|
23193
|
+
images: external_exports.array(external_exports.never()).length(0).optional(),
|
|
23194
|
+
submittedAttachments: external_exports.array(external_exports.never()).length(0).optional()
|
|
23195
|
+
}).strict().superRefine((value, context) => {
|
|
23196
|
+
if (value.images !== void 0 && value.submittedAttachments !== void 0) {
|
|
23197
|
+
context.addIssue({
|
|
23198
|
+
code: external_exports.ZodIssueCode.custom,
|
|
23199
|
+
message: "Use either images or submittedAttachments, not both.",
|
|
23200
|
+
path: ["submittedAttachments"]
|
|
23201
|
+
});
|
|
23202
|
+
}
|
|
23203
|
+
});
|
|
22162
23204
|
var commandListSchema = external_exports.object({
|
|
22163
23205
|
agentId: external_exports.string().min(1).max(128)
|
|
22164
23206
|
}).strict();
|
|
@@ -22279,7 +23321,7 @@ var HarnessApiBridge = class {
|
|
|
22279
23321
|
this.maxStreams = maxStreams;
|
|
22280
23322
|
this.logger = logger;
|
|
22281
23323
|
this.harnessVersion = harnessVersion;
|
|
22282
|
-
this.methods = createMethodMap(api, typertGateway);
|
|
23324
|
+
this.methods = createMethodMap(api, typertGateway, harnessVersion);
|
|
22283
23325
|
this.mux = api.events.mux.bind(api.events);
|
|
22284
23326
|
this.host = api.events.host.bind(api.events);
|
|
22285
23327
|
this.answer = api.respond.bind(api);
|
|
@@ -22600,7 +23642,7 @@ function normalizeHostDescribe(response, harnessVersion, canListDirectory) {
|
|
|
22600
23642
|
}
|
|
22601
23643
|
};
|
|
22602
23644
|
}
|
|
22603
|
-
function createMethodMap(api, typertGateway) {
|
|
23645
|
+
function createMethodMap(api, typertGateway, harnessVersion) {
|
|
22604
23646
|
const domains = api;
|
|
22605
23647
|
const methods = /* @__PURE__ */ new Map();
|
|
22606
23648
|
for (const method of HARNESS_API_ALLOWLIST) {
|
|
@@ -22610,7 +23652,17 @@ function createMethodMap(api, typertGateway) {
|
|
|
22610
23652
|
const implementation2 = async (request, signal) => {
|
|
22611
23653
|
if (commandMethod === "execute") {
|
|
22612
23654
|
const payload = commandExecuteSchema.parse(request.payload);
|
|
22613
|
-
const
|
|
23655
|
+
const legacyArgs = {
|
|
23656
|
+
agentId: payload.agentId,
|
|
23657
|
+
line: payload.line,
|
|
23658
|
+
images: payload.images ?? []
|
|
23659
|
+
};
|
|
23660
|
+
const currentArgs = {
|
|
23661
|
+
agentId: payload.agentId,
|
|
23662
|
+
line: payload.line,
|
|
23663
|
+
submittedAttachments: payload.submittedAttachments ?? []
|
|
23664
|
+
};
|
|
23665
|
+
const args2 = harnessSessionGeneration(harnessVersion) === "v3" ? currentArgs : legacyArgs;
|
|
22614
23666
|
const value2 = await typertGateway.invoke({
|
|
22615
23667
|
namespace,
|
|
22616
23668
|
method: "execute",
|
|
@@ -22776,6 +23828,9 @@ function disableRemoteSettingsDocument(response) {
|
|
|
22776
23828
|
}
|
|
22777
23829
|
var EMPTY_SETTINGS_NAMESPACES = /* @__PURE__ */ new Set();
|
|
22778
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
|
+
}
|
|
22779
23834
|
const allowed = await registeredSettingsNamespaces(api);
|
|
22780
23835
|
if (!allowed.has(ns)) throw deniedSettingsNamespace(ns);
|
|
22781
23836
|
}
|
|
@@ -22912,6 +23967,24 @@ var streamOpenSchema2 = external_exports.object({
|
|
|
22912
23967
|
payload: external_exports.unknown()
|
|
22913
23968
|
}).strict();
|
|
22914
23969
|
var streamCloseSchema2 = external_exports.object({ streamId: external_exports.string().min(1).max(128) }).strict();
|
|
23970
|
+
var commandExecuteArgsSchema = external_exports.object({
|
|
23971
|
+
agentId: external_exports.string().min(1).max(128),
|
|
23972
|
+
line: external_exports.string().min(1).max(2048),
|
|
23973
|
+
// dsh-commands <= 0.1.2 calls this field `images`; dsh-commands 0.1.5
|
|
23974
|
+
// renamed it to `submittedAttachments`. Only empty arrays are exposed by
|
|
23975
|
+
// the Remote bridge; command attachments are deliberately out of scope.
|
|
23976
|
+
images: external_exports.array(external_exports.never()).length(0).optional(),
|
|
23977
|
+
submittedAttachments: external_exports.array(external_exports.never()).length(0).optional()
|
|
23978
|
+
}).strict().superRefine((value, context) => {
|
|
23979
|
+
if (value.images !== void 0 && value.submittedAttachments !== void 0) {
|
|
23980
|
+
context.addIssue({
|
|
23981
|
+
code: external_exports.ZodIssueCode.custom,
|
|
23982
|
+
message: "Use either images or submittedAttachments, not both.",
|
|
23983
|
+
path: ["submittedAttachments"]
|
|
23984
|
+
});
|
|
23985
|
+
}
|
|
23986
|
+
});
|
|
23987
|
+
var commandExecutePayloadSchema = external_exports.object({ args: commandExecuteArgsSchema }).strict();
|
|
22915
23988
|
var transferOpenSchema2 = external_exports.object({
|
|
22916
23989
|
transferId: external_exports.string().uuid(),
|
|
22917
23990
|
totalBytes: external_exports.number().int().positive().max(MAX_HARNESS_API_TRANSFER_BYTES),
|
|
@@ -22983,6 +24056,15 @@ var HARNESS_REMOTE_ALLOWLIST = [
|
|
|
22983
24056
|
"subagents/interruptByParent",
|
|
22984
24057
|
"subagents/list",
|
|
22985
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",
|
|
22986
24068
|
"workspace/archiveSession",
|
|
22987
24069
|
"workspace/create",
|
|
22988
24070
|
"workspace/delete",
|
|
@@ -22993,10 +24075,12 @@ var HARNESS_REMOTE_ALLOWLIST = [
|
|
|
22993
24075
|
];
|
|
22994
24076
|
var allowedEndpoints = new Set(HARNESS_REMOTE_ALLOWLIST);
|
|
22995
24077
|
var HarnessRemoteBridge = class {
|
|
22996
|
-
constructor(gateway, publish, logger) {
|
|
24078
|
+
constructor(gateway, publish, logger, harnessVersion, terminal = new TerminalPolicy(() => false, "", /* @__PURE__ */ new Map())) {
|
|
22997
24079
|
this.gateway = gateway;
|
|
22998
24080
|
this.publish = publish;
|
|
22999
24081
|
this.logger = logger;
|
|
24082
|
+
this.harnessVersion = harnessVersion;
|
|
24083
|
+
this.terminal = terminal;
|
|
23000
24084
|
}
|
|
23001
24085
|
streams = /* @__PURE__ */ new Map();
|
|
23002
24086
|
incomingTransfers = /* @__PURE__ */ new Map();
|
|
@@ -23004,20 +24088,29 @@ var HarnessRemoteBridge = class {
|
|
|
23004
24088
|
async call(input2) {
|
|
23005
24089
|
const params = callSchema3.parse(input2);
|
|
23006
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;
|
|
23007
24099
|
if (params.endpoint === "session/canOpenWorkspacePath") {
|
|
23008
24100
|
return { ok: true, value: true };
|
|
23009
24101
|
}
|
|
23010
24102
|
const startedAt = performance.now();
|
|
23011
24103
|
const signal = AbortSignal.timeout(6e4);
|
|
23012
24104
|
try {
|
|
23013
|
-
const nativeResult = await this.gateway.dispatch(params.endpoint, params.payload, signal);
|
|
24105
|
+
const nativeResult = params.endpoint === "commands/execute" ? await dispatchCommandForHost(this.gateway, params.payload, signal, this.harnessVersion) : await this.gateway.dispatch(params.endpoint, params.payload, signal);
|
|
23014
24106
|
const result = params.endpoint === "directoryPicker/list" && needsDirectoryFallback(nativeResult) ? await this.directoryList(params.payload, signal) : nativeResult;
|
|
23015
24107
|
this.logger?.debug("harness remote call ok", {
|
|
23016
24108
|
endpoint: params.endpoint,
|
|
23017
24109
|
durationMs: Math.round(performance.now() - startedAt)
|
|
23018
24110
|
});
|
|
23019
|
-
return result;
|
|
24111
|
+
return reservation === void 0 ? result : this.terminal.result(params.endpoint, params.payload, result, reservation);
|
|
23020
24112
|
} catch (error) {
|
|
24113
|
+
if (reservation !== void 0) this.terminal.result(params.endpoint, params.payload, { ok: false, error: { code: "FAILED", message: "", details: {} } }, reservation);
|
|
23021
24114
|
if (params.endpoint === "directoryPicker/list") {
|
|
23022
24115
|
const result = await this.directoryList(params.payload, signal);
|
|
23023
24116
|
this.logger?.debug("harness remote call ok", {
|
|
@@ -23144,13 +24237,24 @@ var HarnessRemoteBridge = class {
|
|
|
23144
24237
|
async openStream(input2) {
|
|
23145
24238
|
const params = streamOpenSchema2.parse(input2);
|
|
23146
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);
|
|
23147
24243
|
if (this.streams.has(params.streamId)) throw new RpcError("REQUEST_CONFLICT", "The Harness Remote stream is already open.");
|
|
23148
24244
|
if (this.streams.size >= MAX_ACTIVE_STREAMS) {
|
|
23149
24245
|
throw new RpcError("RATE_LIMITED", "Too many Harness Remote streams are open.", void 0, true);
|
|
23150
24246
|
}
|
|
23151
24247
|
const controller = new AbortController();
|
|
23152
|
-
const source = await this.gateway.open(params.endpoint, params.payload, controller.signal);
|
|
23153
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
|
+
}
|
|
23154
24258
|
void this.pump(params.streamId, source, controller.signal);
|
|
23155
24259
|
return { opened: true, streamId: params.streamId };
|
|
23156
24260
|
}
|
|
@@ -23171,7 +24275,7 @@ var HarnessRemoteBridge = class {
|
|
|
23171
24275
|
for (const [, stream] of streams) stream.controller.abort(reason);
|
|
23172
24276
|
}
|
|
23173
24277
|
assertAllowed(endpoint) {
|
|
23174
|
-
if (!allowedEndpoints.has(endpoint)) {
|
|
24278
|
+
if (!allowedEndpoints.has(endpoint) && !TERMINAL_CALLS.has(endpoint) && !TERMINAL_STREAMS.has(endpoint)) {
|
|
23175
24279
|
throw new RpcError("METHOD_NOT_ALLOWED", "The requested Harness Remote endpoint is not allowed.");
|
|
23176
24280
|
}
|
|
23177
24281
|
if (endpoint === "$events" && !this.gateway.supportsCarrier) {
|
|
@@ -23205,10 +24309,33 @@ var HarnessRemoteBridge = class {
|
|
|
23205
24309
|
}
|
|
23206
24310
|
pruneTransfers() {
|
|
23207
24311
|
const cutoff = Date.now() - TRANSFER_IDLE_MS;
|
|
23208
|
-
for (const [
|
|
23209
|
-
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);
|
|
23210
24314
|
}
|
|
23211
24315
|
};
|
|
24316
|
+
async function dispatchCommandForHost(gateway, payload, signal, harnessVersion) {
|
|
24317
|
+
const parsed = commandExecutePayloadSchema.parse(payload);
|
|
24318
|
+
const legacyPayload = {
|
|
24319
|
+
args: {
|
|
24320
|
+
agentId: parsed.args.agentId,
|
|
24321
|
+
line: parsed.args.line,
|
|
24322
|
+
images: parsed.args.images ?? []
|
|
24323
|
+
}
|
|
24324
|
+
};
|
|
24325
|
+
const currentPayload = {
|
|
24326
|
+
args: {
|
|
24327
|
+
agentId: parsed.args.agentId,
|
|
24328
|
+
line: parsed.args.line,
|
|
24329
|
+
submittedAttachments: parsed.args.submittedAttachments ?? []
|
|
24330
|
+
}
|
|
24331
|
+
};
|
|
24332
|
+
const args = harnessSessionGeneration(harnessVersion) === "v3" ? currentPayload : legacyPayload;
|
|
24333
|
+
return gateway.dispatch(
|
|
24334
|
+
"commands/execute",
|
|
24335
|
+
args,
|
|
24336
|
+
signal
|
|
24337
|
+
);
|
|
24338
|
+
}
|
|
23212
24339
|
function decodeCanonicalBase642(value) {
|
|
23213
24340
|
const bytes = Buffer.from(value, "base64");
|
|
23214
24341
|
if (bytes.toString("base64") !== value) throw new RpcError("INVALID_MESSAGE", "The Harness Remote transfer chunk is invalid.");
|
|
@@ -23242,7 +24369,7 @@ function isRecord9(value) {
|
|
|
23242
24369
|
}
|
|
23243
24370
|
|
|
23244
24371
|
// src/codex/domain.ts
|
|
23245
|
-
import { randomUUID } from "node:crypto";
|
|
24372
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
23246
24373
|
import { readdir as readdir2, realpath, stat as stat4 } from "node:fs/promises";
|
|
23247
24374
|
import { homedir as homedir3 } from "node:os";
|
|
23248
24375
|
import { basename as basename3, isAbsolute as isAbsolute3, join as join5, relative, resolve as resolve2 } from "node:path";
|
|
@@ -23296,11 +24423,11 @@ var CodexAppServerClient = class {
|
|
|
23296
24423
|
if (!this.ready) throw new CodexAppServerError("CODEX_UNAVAILABLE", "Codex App Server is not ready.");
|
|
23297
24424
|
return this.request(method, params, timeoutMs);
|
|
23298
24425
|
}
|
|
23299
|
-
async respond(
|
|
23300
|
-
this.write({ id:
|
|
24426
|
+
async respond(id4, result) {
|
|
24427
|
+
this.write({ id: id4, result });
|
|
23301
24428
|
}
|
|
23302
|
-
async respondError(
|
|
23303
|
-
this.write({ id:
|
|
24429
|
+
async respondError(id4, code, message) {
|
|
24430
|
+
this.write({ id: id4, error: { code, message } });
|
|
23304
24431
|
}
|
|
23305
24432
|
onInbound(handler) {
|
|
23306
24433
|
this.inboundHandlers.add(handler);
|
|
@@ -23381,19 +24508,19 @@ var CodexAppServerClient = class {
|
|
|
23381
24508
|
}
|
|
23382
24509
|
}
|
|
23383
24510
|
request(method, params, timeoutMs) {
|
|
23384
|
-
const
|
|
24511
|
+
const id4 = this.nextId++;
|
|
23385
24512
|
const result = new Promise((resolve3, reject) => {
|
|
23386
24513
|
const timer = setTimeout(() => {
|
|
23387
|
-
this.pending.delete(
|
|
24514
|
+
this.pending.delete(id4);
|
|
23388
24515
|
reject(new CodexAppServerError("CODEX_REQUEST_TIMEOUT", "Codex App Server request timed out."));
|
|
23389
24516
|
}, timeoutMs);
|
|
23390
24517
|
timer.unref?.();
|
|
23391
|
-
this.pending.set(
|
|
24518
|
+
this.pending.set(id4, { resolve: resolve3, reject, timer });
|
|
23392
24519
|
});
|
|
23393
24520
|
try {
|
|
23394
|
-
this.write({ id:
|
|
24521
|
+
this.write({ id: id4, method, params });
|
|
23395
24522
|
} catch (error) {
|
|
23396
|
-
const pending = this.takePending(
|
|
24523
|
+
const pending = this.takePending(id4);
|
|
23397
24524
|
pending?.reject(error instanceof Error ? error : new Error("Codex App Server write failed."));
|
|
23398
24525
|
}
|
|
23399
24526
|
return result;
|
|
@@ -23464,15 +24591,15 @@ var CodexAppServerClient = class {
|
|
|
23464
24591
|
this.failureNotified = true;
|
|
23465
24592
|
for (const handler of this.unavailableHandlers) handler(code);
|
|
23466
24593
|
}
|
|
23467
|
-
takePending(
|
|
23468
|
-
const pending = this.pending.get(
|
|
24594
|
+
takePending(id4) {
|
|
24595
|
+
const pending = this.pending.get(id4);
|
|
23469
24596
|
if (pending === void 0) return void 0;
|
|
23470
|
-
this.pending.delete(
|
|
24597
|
+
this.pending.delete(id4);
|
|
23471
24598
|
clearTimeout(pending.timer);
|
|
23472
24599
|
return pending;
|
|
23473
24600
|
}
|
|
23474
24601
|
failPending(error) {
|
|
23475
|
-
for (const
|
|
24602
|
+
for (const id4 of [...this.pending.keys()]) this.takePending(id4)?.reject(error);
|
|
23476
24603
|
}
|
|
23477
24604
|
};
|
|
23478
24605
|
function safeUpstreamError(value) {
|
|
@@ -23486,7 +24613,7 @@ function isRecord10(value) {
|
|
|
23486
24613
|
}
|
|
23487
24614
|
|
|
23488
24615
|
// src/codex/method-policy.ts
|
|
23489
|
-
var
|
|
24616
|
+
var id3 = external_exports.string().min(1).max(256);
|
|
23490
24617
|
var cursor = external_exports.string().min(1).max(4096).nullable().optional();
|
|
23491
24618
|
var textInput = external_exports.object({
|
|
23492
24619
|
type: external_exports.literal("text"),
|
|
@@ -23535,9 +24662,9 @@ var schemas = {
|
|
|
23535
24662
|
useStateDbOnly: external_exports.boolean().optional(),
|
|
23536
24663
|
searchTerm: external_exports.string().max(1024).optional()
|
|
23537
24664
|
}).strict(),
|
|
23538
|
-
"thread/read": external_exports.object({ threadId:
|
|
24665
|
+
"thread/read": external_exports.object({ threadId: id3, includeTurns: external_exports.boolean().optional() }).strict(),
|
|
23539
24666
|
"dsh/sessionHistory": external_exports.object({
|
|
23540
|
-
threadId:
|
|
24667
|
+
threadId: id3,
|
|
23541
24668
|
beforeSeq: external_exports.number().int().nonnegative().optional(),
|
|
23542
24669
|
throughSeq: external_exports.number().int().min(-1).optional(),
|
|
23543
24670
|
maxMessages: external_exports.number().int().min(1).max(200).optional()
|
|
@@ -23552,21 +24679,21 @@ var schemas = {
|
|
|
23552
24679
|
permissionPreset: permissionPreset.optional()
|
|
23553
24680
|
}).strict(),
|
|
23554
24681
|
"thread/resume": external_exports.object({
|
|
23555
|
-
threadId:
|
|
24682
|
+
threadId: id3,
|
|
23556
24683
|
model: external_exports.string().min(1).max(128).optional(),
|
|
23557
24684
|
permissionPreset: permissionPreset.optional()
|
|
23558
24685
|
}).strict(),
|
|
23559
24686
|
"thread/fork": external_exports.object({
|
|
23560
|
-
threadId:
|
|
23561
|
-
lastTurnId:
|
|
24687
|
+
threadId: id3,
|
|
24688
|
+
lastTurnId: id3.optional(),
|
|
23562
24689
|
permissionPreset: permissionPreset.optional()
|
|
23563
24690
|
}).strict(),
|
|
23564
|
-
"thread/name/set": external_exports.object({ threadId:
|
|
23565
|
-
"thread/archive": external_exports.object({ threadId:
|
|
23566
|
-
"thread/unarchive": external_exports.object({ threadId:
|
|
23567
|
-
"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(),
|
|
23568
24695
|
"turn/start": external_exports.object({
|
|
23569
|
-
threadId:
|
|
24696
|
+
threadId: id3,
|
|
23570
24697
|
input,
|
|
23571
24698
|
model: external_exports.string().min(1).max(128).optional(),
|
|
23572
24699
|
effort: external_exports.enum(["none", "minimal", "low", "medium", "high", "xhigh", "max", "ultra"]).optional(),
|
|
@@ -23574,8 +24701,8 @@ var schemas = {
|
|
|
23574
24701
|
personality: external_exports.string().min(1).max(64).optional(),
|
|
23575
24702
|
permissionPreset: permissionPreset.optional()
|
|
23576
24703
|
}).strict(),
|
|
23577
|
-
"turn/steer": external_exports.object({ threadId:
|
|
23578
|
-
"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()
|
|
23579
24706
|
};
|
|
23580
24707
|
var CODEX_APP_ALLOWLIST = Object.freeze(Object.keys(schemas));
|
|
23581
24708
|
function parseCodexCall(method, params) {
|
|
@@ -23793,14 +24920,14 @@ var CodexPeerBridge = class {
|
|
|
23793
24920
|
if (this.closed) return;
|
|
23794
24921
|
const streamIds = [...this.streams.entries()].filter(([, targetThreadId]) => targetThreadId === threadId).map(([streamId]) => streamId);
|
|
23795
24922
|
for (const streamId of streamIds) {
|
|
23796
|
-
const
|
|
23797
|
-
if (new TextEncoder().encode(JSON.stringify(
|
|
24923
|
+
const data2 = { streamId, frame };
|
|
24924
|
+
if (new TextEncoder().encode(JSON.stringify(data2)).byteLength > MAX_SECURE_MESSAGE_BYTES) {
|
|
23798
24925
|
this.streams.delete(streamId);
|
|
23799
24926
|
await this.publish("codex.app.stream.closed", { streamId, reason: "failed" });
|
|
23800
24927
|
this.logger?.warn("Codex stream closed after oversized frame", { streamId });
|
|
23801
24928
|
continue;
|
|
23802
24929
|
}
|
|
23803
|
-
await this.publish("codex.app.frame",
|
|
24930
|
+
await this.publish("codex.app.frame", data2);
|
|
23804
24931
|
}
|
|
23805
24932
|
}
|
|
23806
24933
|
async failStreams(reason = "failed") {
|
|
@@ -23829,11 +24956,11 @@ var CodexPeerBridge = class {
|
|
|
23829
24956
|
}
|
|
23830
24957
|
pruneTransfers() {
|
|
23831
24958
|
const staleBefore = Date.now() - TRANSFER_IDLE_MS;
|
|
23832
|
-
for (const [
|
|
23833
|
-
if (transfer.touchedAt < staleBefore) this.incomingTransfers.delete(
|
|
24959
|
+
for (const [id4, transfer] of this.incomingTransfers) {
|
|
24960
|
+
if (transfer.touchedAt < staleBefore) this.incomingTransfers.delete(id4);
|
|
23834
24961
|
}
|
|
23835
|
-
for (const [
|
|
23836
|
-
if (transfer.touchedAt < staleBefore) this.outgoingTransfers.delete(
|
|
24962
|
+
for (const [id4, transfer] of this.outgoingTransfers) {
|
|
24963
|
+
if (transfer.touchedAt < staleBefore) this.outgoingTransfers.delete(id4);
|
|
23837
24964
|
}
|
|
23838
24965
|
}
|
|
23839
24966
|
requireOpen() {
|
|
@@ -24300,7 +25427,7 @@ var CodexRemoteDomain = class {
|
|
|
24300
25427
|
await appServer.respond(message.id, { decision: "decline" });
|
|
24301
25428
|
return;
|
|
24302
25429
|
}
|
|
24303
|
-
const requestHandle =
|
|
25430
|
+
const requestHandle = randomUUID2();
|
|
24304
25431
|
this.approvals.set(requestHandle, {
|
|
24305
25432
|
upstreamId: message.id,
|
|
24306
25433
|
connectionId: owner.connectionId,
|
|
@@ -24608,14 +25735,14 @@ var CodexRemoteDomain = class {
|
|
|
24608
25735
|
}
|
|
24609
25736
|
}
|
|
24610
25737
|
async listCodexDirectory(path) {
|
|
24611
|
-
const
|
|
24612
|
-
const rows = await readdir2(
|
|
25738
|
+
const target2 = await this.resolveCodexDirectory(path);
|
|
25739
|
+
const rows = await readdir2(target2.path, { withFileTypes: true }).catch(() => void 0);
|
|
24613
25740
|
if (rows === void 0) {
|
|
24614
25741
|
throw new RpcError("CODEX_PATH_NOT_ALLOWED", "The CodeX working directory is not available as a Workspace.");
|
|
24615
25742
|
}
|
|
24616
25743
|
const entries = [];
|
|
24617
25744
|
for (const row of rows) {
|
|
24618
|
-
const child = resolve2(
|
|
25745
|
+
const child = resolve2(target2.path, row.name);
|
|
24619
25746
|
let directory = row.isDirectory();
|
|
24620
25747
|
if (!directory && row.isSymbolicLink()) {
|
|
24621
25748
|
directory = await stat4(child).then((value) => value.isDirectory()).catch(() => false);
|
|
@@ -24624,7 +25751,7 @@ var CodexRemoteDomain = class {
|
|
|
24624
25751
|
try {
|
|
24625
25752
|
const canonicalChild = await realpath(child);
|
|
24626
25753
|
if (!(await stat4(canonicalChild)).isDirectory()) continue;
|
|
24627
|
-
if (!containsCodexPath(
|
|
25754
|
+
if (!containsCodexPath(target2.canonicalRoot, canonicalChild)) continue;
|
|
24628
25755
|
} catch {
|
|
24629
25756
|
continue;
|
|
24630
25757
|
}
|
|
@@ -24636,9 +25763,9 @@ var CodexRemoteDomain = class {
|
|
|
24636
25763
|
}
|
|
24637
25764
|
entries.sort((left, right) => left.name.localeCompare(right.name, void 0, { sensitivity: "base" }));
|
|
24638
25765
|
return {
|
|
24639
|
-
path:
|
|
24640
|
-
home:
|
|
24641
|
-
crumbs: codexDirectoryCrumbs(
|
|
25766
|
+
path: target2.path,
|
|
25767
|
+
home: target2.root,
|
|
25768
|
+
crumbs: codexDirectoryCrumbs(target2.root, target2.path),
|
|
24642
25769
|
entries: entries.slice(0, CODEX_DIRECTORY_ENTRY_LIMIT),
|
|
24643
25770
|
truncated: entries.length > CODEX_DIRECTORY_ENTRY_LIMIT
|
|
24644
25771
|
};
|
|
@@ -24718,7 +25845,7 @@ function sanitizeThreadList(result) {
|
|
|
24718
25845
|
if (!isRecord12(result) || !Array.isArray(result.data)) {
|
|
24719
25846
|
throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned an invalid thread list.");
|
|
24720
25847
|
}
|
|
24721
|
-
const
|
|
25848
|
+
const data2 = result.data.flatMap((value) => {
|
|
24722
25849
|
if (!isRecord12(value) || typeof value.id !== "string") return [];
|
|
24723
25850
|
return [{
|
|
24724
25851
|
id: value.id,
|
|
@@ -24735,7 +25862,7 @@ function sanitizeThreadList(result) {
|
|
|
24735
25862
|
}];
|
|
24736
25863
|
});
|
|
24737
25864
|
return {
|
|
24738
|
-
data,
|
|
25865
|
+
data: data2,
|
|
24739
25866
|
...typeof result.nextCursor === "string" && result.nextCursor.length > 0 ? { nextCursor: result.nextCursor } : { nextCursor: null },
|
|
24740
25867
|
...typeof result.backwardsCursor === "string" && result.backwardsCursor.length > 0 ? { backwardsCursor: result.backwardsCursor } : {}
|
|
24741
25868
|
};
|
|
@@ -24769,9 +25896,9 @@ function sanitizeProjectList(result) {
|
|
|
24769
25896
|
if (!isRecord12(result) || !Array.isArray(result.data)) {
|
|
24770
25897
|
throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned an invalid project list.");
|
|
24771
25898
|
}
|
|
24772
|
-
const
|
|
25899
|
+
const data2 = result.data.flatMap((value) => sanitizeProject(value) ?? []);
|
|
24773
25900
|
return {
|
|
24774
|
-
data,
|
|
25901
|
+
data: data2,
|
|
24775
25902
|
...typeof result.nextCursor === "string" && result.nextCursor.length > 0 ? { nextCursor: result.nextCursor } : { nextCursor: null }
|
|
24776
25903
|
};
|
|
24777
25904
|
}
|
|
@@ -24912,7 +26039,119 @@ function isActiveWriterMessage(message) {
|
|
|
24912
26039
|
return message.toLowerCase().includes("active writer");
|
|
24913
26040
|
}
|
|
24914
26041
|
|
|
26042
|
+
// src/acp.ts
|
|
26043
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
26044
|
+
var AcpGateway = class {
|
|
26045
|
+
constructor(adapters) {
|
|
26046
|
+
this.adapters = adapters;
|
|
26047
|
+
}
|
|
26048
|
+
get(p) {
|
|
26049
|
+
const list = this.adapters instanceof Object && "backend" in this.adapters ? [this.adapters] : [...this.adapters];
|
|
26050
|
+
const a = list.find((x) => !p.backend || x.backend === p.backend);
|
|
26051
|
+
if (!a) throw new Error("CAPABILITY_NOT_SUPPORTED");
|
|
26052
|
+
return a;
|
|
26053
|
+
}
|
|
26054
|
+
initialize(p) {
|
|
26055
|
+
return this.get(p).initialize(p);
|
|
26056
|
+
}
|
|
26057
|
+
sessionNew(p) {
|
|
26058
|
+
return this.get(p).sessionNew(p);
|
|
26059
|
+
}
|
|
26060
|
+
sessionLoad(p) {
|
|
26061
|
+
const a = this.get(p);
|
|
26062
|
+
if (!a.sessionLoad) throw new Error("CAPABILITY_NOT_SUPPORTED");
|
|
26063
|
+
return a.sessionLoad(p);
|
|
26064
|
+
}
|
|
26065
|
+
prompt(p, emit) {
|
|
26066
|
+
return this.get(p).prompt(p, emit);
|
|
26067
|
+
}
|
|
26068
|
+
respondPermission(p) {
|
|
26069
|
+
const a = this.get(p);
|
|
26070
|
+
if (!a.respondPermission) throw new Error("CAPABILITY_NOT_SUPPORTED");
|
|
26071
|
+
return a.respondPermission(p);
|
|
26072
|
+
}
|
|
26073
|
+
cancel(p) {
|
|
26074
|
+
const a = this.get(p);
|
|
26075
|
+
if (!a.cancel) throw new Error("CAPABILITY_NOT_SUPPORTED");
|
|
26076
|
+
return a.cancel(p);
|
|
26077
|
+
}
|
|
26078
|
+
setMode(p) {
|
|
26079
|
+
const a = this.get(p);
|
|
26080
|
+
if (!a.setMode) throw new Error("CAPABILITY_NOT_SUPPORTED");
|
|
26081
|
+
return a.setMode(p);
|
|
26082
|
+
}
|
|
26083
|
+
};
|
|
26084
|
+
var StdioAcpAdapter = class {
|
|
26085
|
+
constructor(config) {
|
|
26086
|
+
this.config = config;
|
|
26087
|
+
this.backend = config.id;
|
|
26088
|
+
}
|
|
26089
|
+
backend;
|
|
26090
|
+
child;
|
|
26091
|
+
nextId = 1;
|
|
26092
|
+
ensure() {
|
|
26093
|
+
if (!this.child) this.child = spawn3(this.config.command, this.config.args ?? [], { cwd: this.config.cwd, stdio: "pipe" });
|
|
26094
|
+
return this.child;
|
|
26095
|
+
}
|
|
26096
|
+
async initialize(params) {
|
|
26097
|
+
await this.call("initialize", params);
|
|
26098
|
+
return { protocolVersion: 1, capability: "agent.acp.v1", backend: this.backend, capabilities: ["session.new", "session.load", "session.prompt", "session.cancel"] };
|
|
26099
|
+
}
|
|
26100
|
+
async sessionNew(params) {
|
|
26101
|
+
const r = await this.call("session/new", params);
|
|
26102
|
+
if (!r.sessionId) throw new Error("INVALID_MESSAGE");
|
|
26103
|
+
return { sessionId: r.sessionId };
|
|
26104
|
+
}
|
|
26105
|
+
async sessionLoad(params) {
|
|
26106
|
+
const r = await this.call("session/load", params);
|
|
26107
|
+
if (!r.sessionId) throw new Error("INVALID_MESSAGE");
|
|
26108
|
+
return { sessionId: r.sessionId };
|
|
26109
|
+
}
|
|
26110
|
+
async prompt(params, emit) {
|
|
26111
|
+
await this.call("session/prompt", params, async (n) => emit({ sessionId: params.sessionId, update: n, seq: this.nextId++ }));
|
|
26112
|
+
}
|
|
26113
|
+
async cancel(p) {
|
|
26114
|
+
await this.call("session/cancel", p);
|
|
26115
|
+
}
|
|
26116
|
+
async setMode(p) {
|
|
26117
|
+
await this.call("session/set_mode", p);
|
|
26118
|
+
}
|
|
26119
|
+
async respondPermission(p) {
|
|
26120
|
+
await this.call("session/request_permission", p);
|
|
26121
|
+
}
|
|
26122
|
+
async close() {
|
|
26123
|
+
this.child?.kill();
|
|
26124
|
+
this.child = void 0;
|
|
26125
|
+
}
|
|
26126
|
+
call(method, params, onNotification) {
|
|
26127
|
+
const c = this.ensure();
|
|
26128
|
+
const id4 = this.nextId++;
|
|
26129
|
+
c.stdin.write(JSON.stringify({ jsonrpc: "2.0", id: id4, method, params }) + "\n");
|
|
26130
|
+
return new Promise((resolve3, reject) => {
|
|
26131
|
+
let buf = "";
|
|
26132
|
+
const onData = async (d) => {
|
|
26133
|
+
buf += d;
|
|
26134
|
+
const lines = buf.split("\n");
|
|
26135
|
+
buf = lines.pop() ?? "";
|
|
26136
|
+
for (const l of lines) {
|
|
26137
|
+
try {
|
|
26138
|
+
const m = JSON.parse(l);
|
|
26139
|
+
if (m.id === id4) {
|
|
26140
|
+
c.stdout.off("data", onData);
|
|
26141
|
+
m.error ? reject(new Error(m.error.message ?? "ACP error")) : resolve3(m.result);
|
|
26142
|
+
} else if (m.method && onNotification) await onNotification(m.params);
|
|
26143
|
+
} catch {
|
|
26144
|
+
}
|
|
26145
|
+
}
|
|
26146
|
+
};
|
|
26147
|
+
c.stdout.on("data", onData);
|
|
26148
|
+
c.once("error", reject);
|
|
26149
|
+
});
|
|
26150
|
+
}
|
|
26151
|
+
};
|
|
26152
|
+
|
|
24915
26153
|
// src/service.ts
|
|
26154
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
24916
26155
|
var HostPluginRuntime = class {
|
|
24917
26156
|
constructor(config, identities, apiProxy, logger, localGateway, fileViewerHost) {
|
|
24918
26157
|
this.config = config;
|
|
@@ -24921,11 +26160,13 @@ var HostPluginRuntime = class {
|
|
|
24921
26160
|
this.logger = logger;
|
|
24922
26161
|
this.localGateway = localGateway;
|
|
24923
26162
|
this.fileViewerHost = fileViewerHost;
|
|
26163
|
+
this.terminalEnabled = config.terminal.enabled;
|
|
26164
|
+
this.loopback = new LoopbackHost(config.loopback.ports);
|
|
24924
26165
|
this.codex = new CodexRemoteDomain(config.codex, logger);
|
|
24925
26166
|
this.connections = new ConnectionController(this.identities, (context, send) => {
|
|
24926
26167
|
const harnessApi = this.apiProxy === void 0 ? void 0 : new HarnessApiBridge(
|
|
24927
26168
|
this.apiProxy,
|
|
24928
|
-
(event,
|
|
26169
|
+
(event, data2) => send(createEvent(event, data2)),
|
|
24929
26170
|
void 0,
|
|
24930
26171
|
this.logger,
|
|
24931
26172
|
this.localGateway,
|
|
@@ -24933,8 +26174,10 @@ var HostPluginRuntime = class {
|
|
|
24933
26174
|
);
|
|
24934
26175
|
const harnessRemote = this.localGateway?.supportsCarrier === true ? new HarnessRemoteBridge(
|
|
24935
26176
|
this.localGateway,
|
|
24936
|
-
(event,
|
|
24937
|
-
this.logger
|
|
26177
|
+
(event, data2) => send(createEvent(event, data2)),
|
|
26178
|
+
this.logger,
|
|
26179
|
+
this.harnessVersion,
|
|
26180
|
+
new TerminalPolicy(() => this.terminalEnabled, context.peerDeviceId, this.terminalOwners)
|
|
24938
26181
|
) : void 0;
|
|
24939
26182
|
const fileViewer = new RemoteFileViewerBridge(
|
|
24940
26183
|
() => this.fileViewerHost?.(),
|
|
@@ -24942,8 +26185,10 @@ var HostPluginRuntime = class {
|
|
|
24942
26185
|
);
|
|
24943
26186
|
const codex = this.codex.createPeer(
|
|
24944
26187
|
context,
|
|
24945
|
-
(event,
|
|
26188
|
+
(event, data2) => send(createEvent(event, data2))
|
|
24946
26189
|
);
|
|
26190
|
+
const adapters = (config.acp?.backends ?? []).filter((item) => item.enabled && this.acpAvailable(item.command)).map((item) => new StdioAcpAdapter(item));
|
|
26191
|
+
const acp = config.acp?.enabled && adapters.length > 0 ? new AcpGateway(adapters) : void 0;
|
|
24947
26192
|
return new RpcRouter(
|
|
24948
26193
|
harnessApi,
|
|
24949
26194
|
void 0,
|
|
@@ -24951,7 +26196,9 @@ var HostPluginRuntime = class {
|
|
|
24951
26196
|
fileViewer,
|
|
24952
26197
|
harnessRemote,
|
|
24953
26198
|
() => this.hostCapabilities(),
|
|
24954
|
-
codex
|
|
26199
|
+
codex,
|
|
26200
|
+
acp,
|
|
26201
|
+
this.loopback
|
|
24955
26202
|
);
|
|
24956
26203
|
}, this.logger);
|
|
24957
26204
|
if (config.serverUrl !== void 0) {
|
|
@@ -24959,6 +26206,9 @@ var HostPluginRuntime = class {
|
|
|
24959
26206
|
}
|
|
24960
26207
|
}
|
|
24961
26208
|
connections;
|
|
26209
|
+
terminalOwners = /* @__PURE__ */ new Map();
|
|
26210
|
+
terminalEnabled;
|
|
26211
|
+
loopback;
|
|
24962
26212
|
identity;
|
|
24963
26213
|
serverApi;
|
|
24964
26214
|
serverConnection;
|
|
@@ -24967,6 +26217,12 @@ var HostPluginRuntime = class {
|
|
|
24967
26217
|
codex;
|
|
24968
26218
|
localCodexPeer;
|
|
24969
26219
|
localCodexPublish = async () => void 0;
|
|
26220
|
+
setTerminalEnabled(enabled) {
|
|
26221
|
+
this.terminalEnabled = enabled;
|
|
26222
|
+
}
|
|
26223
|
+
setLoopbackPorts(ports) {
|
|
26224
|
+
this.loopback.setPorts(ports);
|
|
26225
|
+
}
|
|
24970
26226
|
async start() {
|
|
24971
26227
|
if (this.closed) throw new Error("remote runtime is closed");
|
|
24972
26228
|
this.identity = await this.identities.loadOrCreate(this.config.deviceName);
|
|
@@ -25004,9 +26260,21 @@ var HostPluginRuntime = class {
|
|
|
25004
26260
|
...error === void 0 ? {} : { error },
|
|
25005
26261
|
...authorization?.account === void 0 ? {} : { account: authorization.account },
|
|
25006
26262
|
authorized: authorization !== void 0,
|
|
25007
|
-
accountRequired: error === "ACCOUNT_AUTH_REQUIRED" || error === "AUTH_INVALID" || error === "TOKEN_EXPIRED"
|
|
26263
|
+
accountRequired: error === "ACCOUNT_AUTH_REQUIRED" || error === "AUTH_INVALID" || error === "TOKEN_EXPIRED",
|
|
26264
|
+
connectedClients: this.listConnectedClients()
|
|
25008
26265
|
};
|
|
25009
26266
|
}
|
|
26267
|
+
listConnectedClients() {
|
|
26268
|
+
return this.connections.connectedPeers().map((peer) => {
|
|
26269
|
+
const trusted = this.identities.trustedPeer(peer.deviceId);
|
|
26270
|
+
return {
|
|
26271
|
+
deviceId: peer.deviceId,
|
|
26272
|
+
name: trusted?.name.trim() ?? "",
|
|
26273
|
+
...trusted === void 0 ? {} : { platform: trusted.platform },
|
|
26274
|
+
...peer.mode === void 0 ? {} : { mode: peer.mode }
|
|
26275
|
+
};
|
|
26276
|
+
});
|
|
26277
|
+
}
|
|
25010
26278
|
localHarnessVersion() {
|
|
25011
26279
|
return this.harnessVersion;
|
|
25012
26280
|
}
|
|
@@ -25162,7 +26430,7 @@ var HostPluginRuntime = class {
|
|
|
25162
26430
|
let reportedVersion;
|
|
25163
26431
|
let errorCode5;
|
|
25164
26432
|
try {
|
|
25165
|
-
const response = await this.apiProxy?.host.describe({ rpcId:
|
|
26433
|
+
const response = await this.apiProxy?.host.describe({ rpcId: randomUUID3(), payload: {} });
|
|
25166
26434
|
if (response === void 0) throw new Error("ApiProxy is unavailable");
|
|
25167
26435
|
if (!response.result.ok) {
|
|
25168
26436
|
errorCode5 = response.result.error.code;
|
|
@@ -25179,19 +26447,32 @@ var HostPluginRuntime = class {
|
|
|
25179
26447
|
}
|
|
25180
26448
|
hostCapabilities() {
|
|
25181
26449
|
const capabilities = [];
|
|
26450
|
+
if (this.loopback.hasPorts()) capabilities.push("loopback.http-ws.v1");
|
|
25182
26451
|
if (this.localGateway?.supportsCarrier === true) {
|
|
25183
26452
|
capabilities.push(
|
|
25184
26453
|
harnessSessionGeneration(this.harnessVersion) === "v3" ? "harness.remote.v3" : "harness.remote.v1",
|
|
25185
26454
|
"harness.remote.transfer.v1"
|
|
25186
26455
|
);
|
|
25187
26456
|
}
|
|
26457
|
+
if (this.localGateway?.supportsCarrier && this.terminalEnabled) capabilities.push("harness.terminal.v1");
|
|
25188
26458
|
if (this.apiProxy !== void 0) {
|
|
25189
26459
|
capabilities.push("harness.api.v1", "harness.api.transfer.v1");
|
|
25190
26460
|
}
|
|
25191
26461
|
if (this.fileViewerHost?.() !== void 0) capabilities.push("fileviewer.read.v1");
|
|
25192
26462
|
if (this.codex.isAvailable()) capabilities.push("codex.appserver.v1", "codex.appserver.transfer.v1");
|
|
26463
|
+
if (this.config.acp?.enabled) {
|
|
26464
|
+
for (const item of this.config.acp.backends) if (item.enabled && this.acpAvailable(item.command)) capabilities.push(`agent.acp.v1.${item.id}`);
|
|
26465
|
+
}
|
|
25193
26466
|
return capabilities;
|
|
25194
26467
|
}
|
|
26468
|
+
acpAvailable(command) {
|
|
26469
|
+
try {
|
|
26470
|
+
execFileSync2(process.platform === "win32" ? "where" : "which", [command], { stdio: "ignore" });
|
|
26471
|
+
return true;
|
|
26472
|
+
} catch {
|
|
26473
|
+
return false;
|
|
26474
|
+
}
|
|
26475
|
+
}
|
|
25195
26476
|
requireLocalCodexPeer() {
|
|
25196
26477
|
if (!this.codex.isAvailable()) {
|
|
25197
26478
|
throw new RpcError("CODEX_UNAVAILABLE", "Local CodeX is disabled or unavailable on this Host.");
|
|
@@ -25201,7 +26482,7 @@ var HostPluginRuntime = class {
|
|
|
25201
26482
|
const peer = this.codex.createPeer({
|
|
25202
26483
|
connectionId: `loopback:${identity.deviceId}`,
|
|
25203
26484
|
peerDeviceId: identity.deviceId
|
|
25204
|
-
}, (event,
|
|
26485
|
+
}, (event, data2) => this.localCodexPublish(event, data2));
|
|
25205
26486
|
if (peer === void 0) {
|
|
25206
26487
|
throw new RpcError("CODEX_UNAVAILABLE", "Local CodeX is disabled or unavailable on this Host.");
|
|
25207
26488
|
}
|
|
@@ -25459,11 +26740,11 @@ function helpText() {
|
|
|
25459
26740
|
""
|
|
25460
26741
|
].join("\n");
|
|
25461
26742
|
}
|
|
25462
|
-
function write(
|
|
25463
|
-
|
|
26743
|
+
function write(target2, value) {
|
|
26744
|
+
target2.write(value);
|
|
25464
26745
|
}
|
|
25465
|
-
function terminalLink(url,
|
|
25466
|
-
if (
|
|
26746
|
+
function terminalLink(url, target2) {
|
|
26747
|
+
if (target2.isTTY !== true) return url;
|
|
25467
26748
|
return `\x1B]8;;${url}\x07${url}\x1B]8;;\x07`;
|
|
25468
26749
|
}
|
|
25469
26750
|
async function exists3(path) {
|
|
@@ -25524,10 +26805,10 @@ function installTuiRemoteCommand(ctx, resolveTarget) {
|
|
|
25524
26805
|
return { kind: "success" };
|
|
25525
26806
|
}
|
|
25526
26807
|
if (command === "logout" && args.length === 1) {
|
|
25527
|
-
const
|
|
25528
|
-
if (
|
|
26808
|
+
const target2 = resolveTarget();
|
|
26809
|
+
if (target2 === void 0) return remoteNotReady();
|
|
25529
26810
|
try {
|
|
25530
|
-
await
|
|
26811
|
+
await target2.runtime.clearHostAuthorization();
|
|
25531
26812
|
return {
|
|
25532
26813
|
kind: "success",
|
|
25533
26814
|
text: "Remote Host logged out and its local device identity was rotated."
|
|
@@ -25624,11 +26905,11 @@ function remoteCommandChildren(canonicalPath) {
|
|
|
25624
26905
|
}
|
|
25625
26906
|
return [];
|
|
25626
26907
|
}
|
|
25627
|
-
function remoteStatusLines(
|
|
25628
|
-
if (
|
|
26908
|
+
function remoteStatusLines(target2) {
|
|
26909
|
+
if (target2 === void 0) {
|
|
25629
26910
|
return ["Remote Host is disabled or still starting. Check the ds-harness-remote settings and retry."];
|
|
25630
26911
|
}
|
|
25631
|
-
const { runtime, config } =
|
|
26912
|
+
const { runtime, config } = target2;
|
|
25632
26913
|
const status2 = runtime.hostStatus();
|
|
25633
26914
|
const diagnostics = runtime.diagnostics();
|
|
25634
26915
|
const codex = runtime.codexStatus();
|
|
@@ -25640,6 +26921,8 @@ function remoteStatusLines(target) {
|
|
|
25640
26921
|
`Device: ${status2.deviceId ?? "not initialized"}`,
|
|
25641
26922
|
`Authorization: ${status2.authorized ? status2.account === void 0 ? "logged in" : `logged in (${status2.account})` : "logged out"}`,
|
|
25642
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]."] : [],
|
|
25643
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"}`,
|
|
25644
26927
|
`Remote clients: ${diagnostics.activeConnections}`,
|
|
25645
26928
|
`Codex Remote: ${codex.enabled ? codex.state : "disabled"}`,
|
|
@@ -25647,8 +26930,8 @@ function remoteStatusLines(target) {
|
|
|
25647
26930
|
"Commands: /remote login [github|zhihu] \xB7 /remote status \xB7 /remote logout"
|
|
25648
26931
|
];
|
|
25649
26932
|
}
|
|
25650
|
-
function formatRemoteStatusInline(
|
|
25651
|
-
return remoteStatusLines(
|
|
26933
|
+
function formatRemoteStatusInline(target2) {
|
|
26934
|
+
return remoteStatusLines(target2).filter(Boolean).join(" \xB7 ");
|
|
25652
26935
|
}
|
|
25653
26936
|
function remoteCommandUsage() {
|
|
25654
26937
|
return [
|
|
@@ -25689,16 +26972,16 @@ var RemoteLoginController = class {
|
|
|
25689
26972
|
}
|
|
25690
26973
|
async run(attempt, provider) {
|
|
25691
26974
|
try {
|
|
25692
|
-
const
|
|
25693
|
-
if (
|
|
25694
|
-
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);
|
|
25695
26978
|
const [qr, compactQr] = await Promise.all([
|
|
25696
26979
|
renderTerminalQr(session.scanUrl),
|
|
25697
26980
|
renderCompactTerminalQr(session.scanUrl)
|
|
25698
26981
|
]);
|
|
25699
26982
|
if (attempt !== this.attempt) return;
|
|
25700
26983
|
this.update({ phase: "waiting", provider, qr, compactQr, scanUrl: session.scanUrl });
|
|
25701
|
-
await this.poll(attempt, provider, session,
|
|
26984
|
+
await this.poll(attempt, provider, session, target2.runtime);
|
|
25702
26985
|
} catch (error) {
|
|
25703
26986
|
if (attempt !== this.attempt) return;
|
|
25704
26987
|
this.update({ phase: "error", provider, error: errorCode4(error) });
|
|
@@ -26108,6 +27391,7 @@ function isPlainRecord2(value) {
|
|
|
26108
27391
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
26109
27392
|
}
|
|
26110
27393
|
export {
|
|
27394
|
+
AcpGateway,
|
|
26111
27395
|
ApiProxySwitch,
|
|
26112
27396
|
CODEX_APP_ALLOWLIST,
|
|
26113
27397
|
ClientModeError,
|