ds-harness-remote 0.4.16 → 0.4.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client-runtime.js +1 -1
- package/dist/client-runtime.js.map +1 -1
- package/dist/client.github.js +57 -18
- package/dist/client.js +57 -18
- package/dist/client.js.map +1 -1
- package/dist/codex/domain.d.ts +2 -0
- package/dist/codex/domain.d.ts.map +1 -1
- package/dist/codex/domain.js +15 -0
- package/dist/codex/domain.js.map +1 -1
- package/dist/codex/session-id.d.ts +10 -0
- package/dist/codex/session-id.d.ts.map +1 -0
- package/dist/codex/session-id.js +15 -0
- package/dist/codex/session-id.js.map +1 -0
- package/dist/codex/virtual-harness.d.ts +3 -2
- package/dist/codex/virtual-harness.d.ts.map +1 -1
- package/dist/codex/virtual-harness.js +32 -3
- package/dist/codex/virtual-harness.js.map +1 -1
- package/dist/codex-workspace-bridge.d.ts +108 -0
- package/dist/codex-workspace-bridge.d.ts.map +1 -0
- package/dist/codex-workspace-bridge.js +475 -0
- package/dist/codex-workspace-bridge.js.map +1 -0
- package/dist/harness-remote-bridge.d.ts +4 -2
- package/dist/harness-remote-bridge.d.ts.map +1 -1
- package/dist/harness-remote-bridge.js +22 -2
- package/dist/harness-remote-bridge.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +616 -78
- package/dist/index.js.map +4 -4
- package/dist/service.d.ts +7 -1
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +8 -2
- package/dist/service.js.map +1 -1
- package/dist/terminal-policy.d.ts.map +1 -1
- package/dist/terminal-policy.js +8 -2
- package/dist/terminal-policy.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 +1 -1
package/dist/index.js
CHANGED
|
@@ -4695,7 +4695,7 @@ var LoopbackHost = class {
|
|
|
4695
4695
|
const response = handle.response;
|
|
4696
4696
|
let chunk = response.read(Math.min(response.readableLength || LOOPBACK_CHUNK_BYTES, LOOPBACK_CHUNK_BYTES));
|
|
4697
4697
|
if (chunk === null && !response.readableEnded && !response.destroyed) {
|
|
4698
|
-
await new Promise((
|
|
4698
|
+
await new Promise((resolve4, reject) => {
|
|
4699
4699
|
const timer = setTimeout(() => finish(new Error("timeout")), 2e4);
|
|
4700
4700
|
const ready = () => finish();
|
|
4701
4701
|
const fail4 = () => finish(new Error("closed"));
|
|
@@ -4705,7 +4705,7 @@ var LoopbackHost = class {
|
|
|
4705
4705
|
response.off("end", ready);
|
|
4706
4706
|
response.off("close", ready);
|
|
4707
4707
|
response.off("error", fail4);
|
|
4708
|
-
error === void 0 ?
|
|
4708
|
+
error === void 0 ? resolve4() : reject(error);
|
|
4709
4709
|
};
|
|
4710
4710
|
response.once("readable", ready);
|
|
4711
4711
|
response.once("end", ready);
|
|
@@ -4735,22 +4735,22 @@ var LoopbackHost = class {
|
|
|
4735
4735
|
this.close(value.id);
|
|
4736
4736
|
throw new RpcError("RATE_LIMITED", "Preview WebSocket buffer exceeded its limit.");
|
|
4737
4737
|
}
|
|
4738
|
-
await new Promise((
|
|
4738
|
+
await new Promise((resolve4, reject) => handle.socket.send(data2, { binary: value.binary }, (error) => error ? reject(new RpcError("LOOPBACK_CLOSED", "Preview socket closed.")) : resolve4()));
|
|
4739
4739
|
return { sent: true };
|
|
4740
4740
|
}
|
|
4741
4741
|
if (value.op === "ws.read") {
|
|
4742
4742
|
if (handle.reading) throw new RpcError("REQUEST_CONFLICT", "Only one preview read may be pending.");
|
|
4743
4743
|
handle.reading = true;
|
|
4744
4744
|
try {
|
|
4745
|
-
if (handle.messages.length === 0 && !handle.closed) await new Promise((
|
|
4745
|
+
if (handle.messages.length === 0 && !handle.closed) await new Promise((resolve4) => {
|
|
4746
4746
|
const timer = setTimeout(() => {
|
|
4747
4747
|
handle.wake = void 0;
|
|
4748
|
-
|
|
4748
|
+
resolve4();
|
|
4749
4749
|
}, 2e4);
|
|
4750
4750
|
handle.wake = () => {
|
|
4751
4751
|
clearTimeout(timer);
|
|
4752
4752
|
handle.wake = void 0;
|
|
4753
|
-
|
|
4753
|
+
resolve4();
|
|
4754
4754
|
};
|
|
4755
4755
|
});
|
|
4756
4756
|
let bytes = 0;
|
|
@@ -4792,7 +4792,7 @@ var LoopbackHost = class {
|
|
|
4792
4792
|
openHttp(value) {
|
|
4793
4793
|
const body = value.body === void 0 ? void 0 : Buffer.from(value.body, "base64");
|
|
4794
4794
|
if ((body?.length ?? 0) > LOOPBACK_MAX_BODY_BYTES) throw new Error("body limit");
|
|
4795
|
-
return new Promise((
|
|
4795
|
+
return new Promise((resolve4, reject) => {
|
|
4796
4796
|
const headers = Object.fromEntries(cleanHeaders(value.headers));
|
|
4797
4797
|
const request = httpRequest({
|
|
4798
4798
|
hostname: "127.0.0.1",
|
|
@@ -4811,7 +4811,7 @@ var LoopbackHost = class {
|
|
|
4811
4811
|
handle.response = response;
|
|
4812
4812
|
response.on("error", () => void 0);
|
|
4813
4813
|
clearTimeout(timer);
|
|
4814
|
-
|
|
4814
|
+
resolve4({ status: response.statusCode ?? 502, headers: cleanHeaders(headerPairs(response.headers)) });
|
|
4815
4815
|
});
|
|
4816
4816
|
const timer = setTimeout(() => {
|
|
4817
4817
|
request.destroy();
|
|
@@ -4826,7 +4826,7 @@ var LoopbackHost = class {
|
|
|
4826
4826
|
});
|
|
4827
4827
|
}
|
|
4828
4828
|
openWs(value) {
|
|
4829
|
-
return new Promise((
|
|
4829
|
+
return new Promise((resolve4, reject) => {
|
|
4830
4830
|
const socket = new WebSocket2(`ws://127.0.0.1:${value.port}${value.path}`, value.protocols, {
|
|
4831
4831
|
headers: Object.fromEntries(cleanHeaders(value.headers)),
|
|
4832
4832
|
followRedirects: false,
|
|
@@ -4836,7 +4836,7 @@ var LoopbackHost = class {
|
|
|
4836
4836
|
});
|
|
4837
4837
|
const handle = { kind: "ws", socket, messages: [], bytes: 0, reading: false, closed: false, touched: Date.now() };
|
|
4838
4838
|
this.handles.set(value.id, handle);
|
|
4839
|
-
socket.once("open", () =>
|
|
4839
|
+
socket.once("open", () => resolve4({ protocol: socket.protocol }));
|
|
4840
4840
|
socket.on("message", (data2, binary) => {
|
|
4841
4841
|
const message = { data: Buffer.from(data2).toString("base64"), binary };
|
|
4842
4842
|
if (handle.bytes + message.data.length > 1024 * 1024 || handle.messages.length >= 256) {
|
|
@@ -4894,7 +4894,7 @@ var LoopbackPreview = class {
|
|
|
4894
4894
|
for (const client of running.ws.clients) client.terminate();
|
|
4895
4895
|
running.ws.close();
|
|
4896
4896
|
running.server.closeAllConnections();
|
|
4897
|
-
await new Promise((
|
|
4897
|
+
await new Promise((resolve4) => running.server.close(() => resolve4()));
|
|
4898
4898
|
}
|
|
4899
4899
|
this.servers.clear();
|
|
4900
4900
|
}
|
|
@@ -4949,11 +4949,11 @@ var LoopbackPreview = class {
|
|
|
4949
4949
|
void this.release(id4);
|
|
4950
4950
|
});
|
|
4951
4951
|
});
|
|
4952
|
-
await new Promise((
|
|
4952
|
+
await new Promise((resolve4, reject) => {
|
|
4953
4953
|
server.once("error", reject);
|
|
4954
4954
|
server.listen(0, "127.0.0.1", () => {
|
|
4955
4955
|
server.off("error", reject);
|
|
4956
|
-
|
|
4956
|
+
resolve4();
|
|
4957
4957
|
});
|
|
4958
4958
|
});
|
|
4959
4959
|
const address = server.address();
|
|
@@ -5019,10 +5019,10 @@ var LoopbackPreview = class {
|
|
|
5019
5019
|
while (!signal.aborted) {
|
|
5020
5020
|
const chunk = await this.client.rpc("loopback.call", { op: "http.read", id: id4 }, signal);
|
|
5021
5021
|
if (chunk.data !== "" && !res.write(Buffer.from(chunk.data, "base64"))) {
|
|
5022
|
-
await new Promise((
|
|
5022
|
+
await new Promise((resolve4, reject) => {
|
|
5023
5023
|
const ready = () => {
|
|
5024
5024
|
cleanup();
|
|
5025
|
-
|
|
5025
|
+
resolve4();
|
|
5026
5026
|
};
|
|
5027
5027
|
const closed = () => {
|
|
5028
5028
|
cleanup();
|
|
@@ -5082,7 +5082,7 @@ var LoopbackPreview = class {
|
|
|
5082
5082
|
const result = await this.client.rpc("loopback.call", { op: "ws.read", id: id4 }, signal);
|
|
5083
5083
|
for (const message of result.messages) {
|
|
5084
5084
|
if (socket.bufferedAmount > 1024 * 1024) throw new Error("slow preview");
|
|
5085
|
-
await new Promise((
|
|
5085
|
+
await new Promise((resolve4, reject) => socket.send(Buffer.from(message.data, "base64"), { binary: message.binary }, (error) => error ? reject(error) : resolve4()));
|
|
5086
5086
|
}
|
|
5087
5087
|
if (result.closed) break;
|
|
5088
5088
|
}
|
|
@@ -5719,17 +5719,18 @@ var RemoteClientCore = class {
|
|
|
5719
5719
|
throw error;
|
|
5720
5720
|
}
|
|
5721
5721
|
}
|
|
5722
|
-
async rpc(method, params, signal) {
|
|
5722
|
+
async rpc(method, params, signal, options) {
|
|
5723
5723
|
if (signal?.aborted)
|
|
5724
5724
|
throw rpcAbortedError(method, signal.reason);
|
|
5725
|
+
const timeoutMs = callTimeoutMs(this.timeoutMs, options);
|
|
5725
5726
|
const request = createRpcRequest(method, params);
|
|
5726
|
-
const result = new Promise((
|
|
5727
|
+
const result = new Promise((resolve4, reject) => {
|
|
5727
5728
|
const timer = setTimeout(() => {
|
|
5728
|
-
this.rejectPending(request.id, new RemoteClientError("RPC_TIMEOUT", `RPC ${method} timed out after ${
|
|
5729
|
-
},
|
|
5729
|
+
this.rejectPending(request.id, new RemoteClientError("RPC_TIMEOUT", `RPC ${method} timed out after ${timeoutMs}ms`));
|
|
5730
|
+
}, timeoutMs);
|
|
5730
5731
|
const pending = {
|
|
5731
5732
|
method,
|
|
5732
|
-
resolve:
|
|
5733
|
+
resolve: resolve4,
|
|
5733
5734
|
reject,
|
|
5734
5735
|
timer
|
|
5735
5736
|
};
|
|
@@ -5836,6 +5837,12 @@ var RemoteClientCore = class {
|
|
|
5836
5837
|
function rpcAbortedError(method, reason) {
|
|
5837
5838
|
return new RemoteClientError("RPC_ABORTED", `RPC ${method} was aborted`, reason === void 0 ? void 0 : { cause: reason });
|
|
5838
5839
|
}
|
|
5840
|
+
function callTimeoutMs(fallback, options) {
|
|
5841
|
+
const timeoutMs = options?.timeoutMs;
|
|
5842
|
+
if (typeof timeoutMs !== "number")
|
|
5843
|
+
return fallback;
|
|
5844
|
+
return Number.isSafeInteger(timeoutMs) && timeoutMs > 0 && timeoutMs <= 2147483647 ? timeoutMs : fallback;
|
|
5845
|
+
}
|
|
5839
5846
|
function transportSendError(error) {
|
|
5840
5847
|
return error instanceof Error ? error : new Error("remote transport send failed", { cause: error });
|
|
5841
5848
|
}
|
|
@@ -5846,7 +5853,7 @@ async function waitForRelayCapacity(socket) {
|
|
|
5846
5853
|
while ((socket.bufferedAmount ?? 0) > 512 * 1024) {
|
|
5847
5854
|
if (socket.readyState !== 1 || Date.now() - started > 1e4)
|
|
5848
5855
|
throw new Error("Relay consumer is too slow or disconnected");
|
|
5849
|
-
await new Promise((
|
|
5856
|
+
await new Promise((resolve4) => setTimeout(resolve4, 10));
|
|
5850
5857
|
}
|
|
5851
5858
|
if (socket.readyState !== 1)
|
|
5852
5859
|
throw new Error("Relay transport closed");
|
|
@@ -6305,8 +6312,8 @@ var RtcDataChannelTransport = class {
|
|
|
6305
6312
|
if (this.connectPromise !== void 0)
|
|
6306
6313
|
return this.connectPromise;
|
|
6307
6314
|
this.armAbort(signal);
|
|
6308
|
-
this.connectPromise = new Promise((
|
|
6309
|
-
this.openResolve =
|
|
6315
|
+
this.connectPromise = new Promise((resolve4, reject) => {
|
|
6316
|
+
this.openResolve = resolve4;
|
|
6310
6317
|
this.openReject = reject;
|
|
6311
6318
|
this.negotiateTimer = setTimeout(() => {
|
|
6312
6319
|
void this.failOpenAfterStats(new RtcConnectError("RTC_CONNECT_TIMEOUT", `WebRTC negotiation timed out after ${this.negotiateTimeoutMs}ms.`));
|
|
@@ -6521,9 +6528,9 @@ var RtcDataChannelTransport = class {
|
|
|
6521
6528
|
if (this.closed || this.opened)
|
|
6522
6529
|
return;
|
|
6523
6530
|
this.opened = true;
|
|
6524
|
-
const
|
|
6531
|
+
const resolve4 = this.openResolve;
|
|
6525
6532
|
this.clearNegotiation();
|
|
6526
|
-
void this.resolveSelectedTransport().then(() =>
|
|
6533
|
+
void this.resolveSelectedTransport().then(() => resolve4?.());
|
|
6527
6534
|
};
|
|
6528
6535
|
channel.onmessage = (event) => {
|
|
6529
6536
|
if (this.closed || !this.opened)
|
|
@@ -6917,7 +6924,7 @@ function asError(error) {
|
|
|
6917
6924
|
return error instanceof Error ? error : new RtcConnectError("RTC_FAILED", "WebRTC negotiation failed.");
|
|
6918
6925
|
}
|
|
6919
6926
|
function sleep(ms) {
|
|
6920
|
-
return new Promise((
|
|
6927
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
6921
6928
|
}
|
|
6922
6929
|
|
|
6923
6930
|
// ../webrtc/dist/adaptive-transport.js
|
|
@@ -6955,8 +6962,8 @@ var AdaptiveTransport = class extends BaseTransport {
|
|
|
6955
6962
|
this.socket = new WebSocket(this.url);
|
|
6956
6963
|
this.controlFrameLimits = {};
|
|
6957
6964
|
this.socket.binaryType = "arraybuffer";
|
|
6958
|
-
await new Promise((
|
|
6959
|
-
this.readyResolve =
|
|
6965
|
+
await new Promise((resolve4, reject) => {
|
|
6966
|
+
this.readyResolve = resolve4;
|
|
6960
6967
|
this.readyReject = reject;
|
|
6961
6968
|
this.handshakeTimer = setTimeout(() => this.failConnection(new Error("Adaptive control handshake timed out")), this.options.handshakeTimeoutMs ?? 15e3);
|
|
6962
6969
|
const socket = this.socket;
|
|
@@ -14558,7 +14565,7 @@ var ClientSecureTransport = class {
|
|
|
14558
14565
|
}
|
|
14559
14566
|
};
|
|
14560
14567
|
async function waitForResponder(inner, noise) {
|
|
14561
|
-
await new Promise((
|
|
14568
|
+
await new Promise((resolve4, reject) => {
|
|
14562
14569
|
let settled = false;
|
|
14563
14570
|
const timer = setTimeout(() => finish(new Error("Noise IK handshake timed out.")), 1e4);
|
|
14564
14571
|
const unsubscribe = inner.onHandshake((step, data2) => {
|
|
@@ -14577,7 +14584,7 @@ async function waitForResponder(inner, noise) {
|
|
|
14577
14584
|
settled = true;
|
|
14578
14585
|
clearTimeout(timer);
|
|
14579
14586
|
unsubscribe();
|
|
14580
|
-
if (error === void 0)
|
|
14587
|
+
if (error === void 0) resolve4();
|
|
14581
14588
|
else reject(error);
|
|
14582
14589
|
};
|
|
14583
14590
|
void inner.sendHandshake(1, noise.writeHandshake()).catch((error) => {
|
|
@@ -15022,7 +15029,7 @@ var AsyncFrameQueue = class {
|
|
|
15022
15029
|
continue;
|
|
15023
15030
|
}
|
|
15024
15031
|
if (this.closed) return;
|
|
15025
|
-
const next = await new Promise((
|
|
15032
|
+
const next = await new Promise((resolve4) => this.waiters.push(resolve4));
|
|
15026
15033
|
if (next.done) return;
|
|
15027
15034
|
yield next.value;
|
|
15028
15035
|
}
|
|
@@ -15387,7 +15394,7 @@ var AsyncValueQueue = class {
|
|
|
15387
15394
|
if (this.error !== void 0) throw this.error;
|
|
15388
15395
|
return;
|
|
15389
15396
|
}
|
|
15390
|
-
const next = await new Promise((
|
|
15397
|
+
const next = await new Promise((resolve4) => this.waiters.push(resolve4));
|
|
15391
15398
|
if (next.done) {
|
|
15392
15399
|
if (this.error !== void 0) throw this.error;
|
|
15393
15400
|
return;
|
|
@@ -15497,6 +15504,29 @@ var MAX_CODEX_IMAGE_BASE64 = 288 * 1024 * 1024;
|
|
|
15497
15504
|
var CODEX_IMAGE_MEDIA_TYPES2 = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/webp", "image/gif"]);
|
|
15498
15505
|
var CODEX_IMAGE_ATTACHMENT_PREFIX = "codex-image:";
|
|
15499
15506
|
var DATA_IMAGE_URL2 = /^data:(image\/(?:png|jpeg|webp|gif));base64,([A-Za-z0-9+/]+={0,2})$/u;
|
|
15507
|
+
var HOST_WORKSPACE_ENDPOINTS = /* @__PURE__ */ new Set([
|
|
15508
|
+
"workspaceFiles/list",
|
|
15509
|
+
"workspaceFiles/stat",
|
|
15510
|
+
"workspaceFiles/read",
|
|
15511
|
+
"workspaceFiles/readBytes",
|
|
15512
|
+
"workspaceFiles/readAll",
|
|
15513
|
+
"workspaceFiles/readRelated",
|
|
15514
|
+
"terminal/environment",
|
|
15515
|
+
"terminal/shells",
|
|
15516
|
+
"terminal/list",
|
|
15517
|
+
"terminal/create",
|
|
15518
|
+
"terminal/write",
|
|
15519
|
+
"terminal/resize",
|
|
15520
|
+
"terminal/rename",
|
|
15521
|
+
"terminal/close"
|
|
15522
|
+
]);
|
|
15523
|
+
var HOST_WORKSPACE_STREAM_ENDPOINTS = /* @__PURE__ */ new Set(["workspaceFiles/changes", "terminal/follow", "terminal/retain"]);
|
|
15524
|
+
function isHostWorkspaceEndpoint(endpoint) {
|
|
15525
|
+
return HOST_WORKSPACE_ENDPOINTS.has(endpoint);
|
|
15526
|
+
}
|
|
15527
|
+
function isHostWorkspaceStreamEndpoint(endpoint) {
|
|
15528
|
+
return HOST_WORKSPACE_STREAM_ENDPOINTS.has(endpoint);
|
|
15529
|
+
}
|
|
15500
15530
|
function codexProjectWorkspaceId(projectId) {
|
|
15501
15531
|
return `${CODEX_WORKSPACE_PREFIX}project:${projectId}`;
|
|
15502
15532
|
}
|
|
@@ -15504,10 +15534,11 @@ async function discoverCodexVirtualWorkspaces(client, signal) {
|
|
|
15504
15534
|
return (await loadCatalog(client, signal)).workspaces;
|
|
15505
15535
|
}
|
|
15506
15536
|
var CodexVirtualHarness = class _CodexVirtualHarness {
|
|
15507
|
-
constructor(client, host, sessionGeneration = "legacy") {
|
|
15537
|
+
constructor(client, host, sessionGeneration = "legacy", hostCarrier) {
|
|
15508
15538
|
this.client = client;
|
|
15509
15539
|
this.host = host;
|
|
15510
15540
|
this.sessionGeneration = sessionGeneration;
|
|
15541
|
+
this.hostCarrier = hostCarrier;
|
|
15511
15542
|
this.api = this.createApiProxy();
|
|
15512
15543
|
}
|
|
15513
15544
|
api;
|
|
@@ -15531,8 +15562,8 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
|
|
|
15531
15562
|
commandSeq = 0;
|
|
15532
15563
|
selectedWorkspaceId;
|
|
15533
15564
|
closed = false;
|
|
15534
|
-
static remote(core, host, sessionGeneration = "legacy") {
|
|
15535
|
-
return new _CodexVirtualHarness(new CodexRemoteClient(core), host, sessionGeneration);
|
|
15565
|
+
static remote(core, host, sessionGeneration = "legacy", hostCarrier) {
|
|
15566
|
+
return new _CodexVirtualHarness(new CodexRemoteClient(core), host, sessionGeneration, hostCarrier);
|
|
15536
15567
|
}
|
|
15537
15568
|
async workspaces(signal) {
|
|
15538
15569
|
return (await this.refreshCatalog(signal)).workspaces;
|
|
@@ -15579,6 +15610,10 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
|
|
|
15579
15610
|
}
|
|
15580
15611
|
async dispatch(endpoint, payload, signal) {
|
|
15581
15612
|
try {
|
|
15613
|
+
if (isHostWorkspaceEndpoint(endpoint)) {
|
|
15614
|
+
if (this.hostCarrier === void 0) return fail("method-not-found", `CodeX virtual Harness does not implement ${endpoint}.`);
|
|
15615
|
+
return await this.hostCarrier.dispatch(endpoint, payload, signal);
|
|
15616
|
+
}
|
|
15582
15617
|
const args = carrierArgs(payload);
|
|
15583
15618
|
switch (endpoint) {
|
|
15584
15619
|
case "$events/result":
|
|
@@ -15665,6 +15700,16 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
|
|
|
15665
15700
|
}
|
|
15666
15701
|
}
|
|
15667
15702
|
async open(endpoint, payload, signal) {
|
|
15703
|
+
if (isHostWorkspaceStreamEndpoint(endpoint)) {
|
|
15704
|
+
if (this.hostCarrier === void 0) {
|
|
15705
|
+
throw Object.assign(new Error(`CodeX virtual Harness does not implement stream ${endpoint}.`), {
|
|
15706
|
+
isDSHRemoteError: true,
|
|
15707
|
+
code: "method-not-found",
|
|
15708
|
+
details: {}
|
|
15709
|
+
});
|
|
15710
|
+
}
|
|
15711
|
+
return this.hostCarrier.open(endpoint, payload, signal);
|
|
15712
|
+
}
|
|
15668
15713
|
const args = carrierArgs(payload);
|
|
15669
15714
|
if (endpoint === "workspace/follow") return this.workspaceFollow(signal);
|
|
15670
15715
|
if (endpoint === "session/control") return this.sessionControl(signal);
|
|
@@ -17983,7 +18028,7 @@ var AsyncValueQueue2 = class {
|
|
|
17983
18028
|
continue;
|
|
17984
18029
|
}
|
|
17985
18030
|
if (this.closed) return;
|
|
17986
|
-
const next = await new Promise((
|
|
18031
|
+
const next = await new Promise((resolve4) => this.waiters.push(resolve4));
|
|
17987
18032
|
if (next.done) return;
|
|
17988
18033
|
yield next.value;
|
|
17989
18034
|
}
|
|
@@ -18027,7 +18072,7 @@ var ServerCredentialStore = class {
|
|
|
18027
18072
|
} catch (error) {
|
|
18028
18073
|
if (!(error instanceof Error) || !("code" in error) || error.code !== "EEXIST") throw error;
|
|
18029
18074
|
if (Date.now() >= deadline) throw new ServerCredentialsBusyError();
|
|
18030
|
-
await new Promise((
|
|
18075
|
+
await new Promise((resolve4) => setTimeout(resolve4, 50));
|
|
18031
18076
|
}
|
|
18032
18077
|
}
|
|
18033
18078
|
try {
|
|
@@ -18198,7 +18243,7 @@ function normalizeServerUrl(value) {
|
|
|
18198
18243
|
}
|
|
18199
18244
|
|
|
18200
18245
|
// src/version.ts
|
|
18201
|
-
var PLUGIN_VERSION = "0.4.
|
|
18246
|
+
var PLUGIN_VERSION = "0.4.17";
|
|
18202
18247
|
|
|
18203
18248
|
// src/server-api.ts
|
|
18204
18249
|
var TERMINAL_CONTROL_CHARACTERS = /[\u0000-\u001f\u007f-\u009f]/u;
|
|
@@ -18867,10 +18912,10 @@ function isUsableExternalNode(candidate, requireFrom) {
|
|
|
18867
18912
|
}
|
|
18868
18913
|
function isExecutableFile(candidate) {
|
|
18869
18914
|
try {
|
|
18870
|
-
const
|
|
18871
|
-
if (!
|
|
18915
|
+
const stat7 = statSync(candidate);
|
|
18916
|
+
if (!stat7.isFile()) return false;
|
|
18872
18917
|
if (process.platform === "win32") return true;
|
|
18873
|
-
return (
|
|
18918
|
+
return (stat7.mode & 73) !== 0;
|
|
18874
18919
|
} catch {
|
|
18875
18920
|
return false;
|
|
18876
18921
|
}
|
|
@@ -18959,8 +19004,8 @@ var ExternalNativePeerConnection = class {
|
|
|
18959
19004
|
request(method, payload) {
|
|
18960
19005
|
if (this.closed) return Promise.reject(new Error("native rtc helper is closed"));
|
|
18961
19006
|
const id4 = this.nextRequestId++;
|
|
18962
|
-
const promise = new Promise((
|
|
18963
|
-
this.pending.set(id4, { resolve:
|
|
19007
|
+
const promise = new Promise((resolve4, reject) => {
|
|
19008
|
+
this.pending.set(id4, { resolve: resolve4, reject });
|
|
18964
19009
|
});
|
|
18965
19010
|
this.write({ id: id4, method, payload });
|
|
18966
19011
|
return promise;
|
|
@@ -19754,10 +19799,10 @@ function parseRouteTarget(value) {
|
|
|
19754
19799
|
async function detectRouteHostIpv4(target2, timeoutMs) {
|
|
19755
19800
|
const socket = createSocket("udp4");
|
|
19756
19801
|
try {
|
|
19757
|
-
return await new Promise((
|
|
19802
|
+
return await new Promise((resolve4, reject) => {
|
|
19758
19803
|
const timer = setTimeout(() => {
|
|
19759
19804
|
cleanup();
|
|
19760
|
-
|
|
19805
|
+
resolve4(void 0);
|
|
19761
19806
|
}, timeoutMs);
|
|
19762
19807
|
const cleanup = () => {
|
|
19763
19808
|
clearTimeout(timer);
|
|
@@ -19771,7 +19816,7 @@ async function detectRouteHostIpv4(target2, timeoutMs) {
|
|
|
19771
19816
|
socket.connect(target2.port, target2.host, () => {
|
|
19772
19817
|
cleanup();
|
|
19773
19818
|
const address = socket.address();
|
|
19774
|
-
|
|
19819
|
+
resolve4(typeof address === "string" ? void 0 : address.address);
|
|
19775
19820
|
});
|
|
19776
19821
|
});
|
|
19777
19822
|
} finally {
|
|
@@ -20097,7 +20142,7 @@ var ClientModeRuntime = class {
|
|
|
20097
20142
|
const virtual = CodexVirtualHarness.remote(remote.client, {
|
|
20098
20143
|
deviceId: remote.target.deviceId,
|
|
20099
20144
|
name: remote.target.name
|
|
20100
|
-
}, harnessSessionGeneration(this.host?.localHarnessVersion?.()));
|
|
20145
|
+
}, harnessSessionGeneration(this.host?.localHarnessVersion?.()), new RemoteTypertGateway2(remote.client));
|
|
20101
20146
|
let workspace;
|
|
20102
20147
|
try {
|
|
20103
20148
|
workspace = await virtual.selectWorkspace(workspaceId, signal);
|
|
@@ -20870,7 +20915,7 @@ async function probeRemoteHostFeatures(client, clientVersion) {
|
|
|
20870
20915
|
}
|
|
20871
20916
|
async function waitForCodexFrames(stream, signal) {
|
|
20872
20917
|
if (signal?.aborted) throw new ClientModeError("RPC_ABORTED", "The Codex event poll was cancelled.");
|
|
20873
|
-
await new Promise((
|
|
20918
|
+
await new Promise((resolve4, reject) => {
|
|
20874
20919
|
const previousWake = stream.wake;
|
|
20875
20920
|
const timer = setTimeout(done, 25e3);
|
|
20876
20921
|
const onAbort = () => {
|
|
@@ -20884,7 +20929,7 @@ async function waitForCodexFrames(stream, signal) {
|
|
|
20884
20929
|
}
|
|
20885
20930
|
function done() {
|
|
20886
20931
|
cleanup();
|
|
20887
|
-
|
|
20932
|
+
resolve4();
|
|
20888
20933
|
}
|
|
20889
20934
|
stream.wake = () => {
|
|
20890
20935
|
previousWake();
|
|
@@ -21608,6 +21653,13 @@ function redact(value, key = "") {
|
|
|
21608
21653
|
return Object.fromEntries(Object.entries(value).map(([childKey, child]) => [childKey, redact(child, childKey)]));
|
|
21609
21654
|
}
|
|
21610
21655
|
|
|
21656
|
+
// src/codex/session-id.ts
|
|
21657
|
+
function parseCodexSessionId(value) {
|
|
21658
|
+
if (typeof value !== "string") return void 0;
|
|
21659
|
+
const match = /^codex:([A-Za-z0-9][A-Za-z0-9._-]{0,255})$/.exec(value);
|
|
21660
|
+
return match === null ? void 0 : { sessionId: value, threadId: match[1] };
|
|
21661
|
+
}
|
|
21662
|
+
|
|
21611
21663
|
// src/terminal-policy.ts
|
|
21612
21664
|
var TERMINAL_CALLS = /* @__PURE__ */ new Set([
|
|
21613
21665
|
"terminal/environment",
|
|
@@ -21620,7 +21672,7 @@ var TERMINAL_CALLS = /* @__PURE__ */ new Set([
|
|
|
21620
21672
|
"terminal/close"
|
|
21621
21673
|
]);
|
|
21622
21674
|
var TERMINAL_STREAMS = /* @__PURE__ */ new Set(["terminal/follow", "terminal/retain"]);
|
|
21623
|
-
var id2 = external_exports.string().regex(/^[
|
|
21675
|
+
var id2 = external_exports.string().regex(/^[A-Za-z0-9_:-]{1,256}$/);
|
|
21624
21676
|
var TerminalPolicy = class {
|
|
21625
21677
|
constructor(enabled, deviceId, owners) {
|
|
21626
21678
|
this.enabled = enabled;
|
|
@@ -21634,7 +21686,10 @@ var TerminalPolicy = class {
|
|
|
21634
21686
|
"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
21687
|
);
|
|
21636
21688
|
const args = external_exports.object({ args: external_exports.record(external_exports.unknown()) }).strict().parse(payload).args;
|
|
21637
|
-
const
|
|
21689
|
+
const rawSessionId = args.agentId ?? args.sessionId;
|
|
21690
|
+
const sessionId = id2.parse(rawSessionId);
|
|
21691
|
+
const codexSession = parseCodexSessionId(sessionId);
|
|
21692
|
+
void codexSession;
|
|
21638
21693
|
if (endpoint === "terminal/environment" || endpoint === "terminal/shells" || endpoint === "terminal/list") return {};
|
|
21639
21694
|
const request = endpoint === "terminal/create" ? external_exports.object({ id: id2 }).passthrough().parse(args.request) : void 0;
|
|
21640
21695
|
const terminalId = id2.parse(request?.id ?? args.id);
|
|
@@ -22334,7 +22389,7 @@ var HostServerConnection = class {
|
|
|
22334
22389
|
this.controlFrameLimits = {};
|
|
22335
22390
|
let acknowledged = false;
|
|
22336
22391
|
let messageQueue = Promise.resolve();
|
|
22337
|
-
await new Promise((
|
|
22392
|
+
await new Promise((resolve4, reject) => {
|
|
22338
22393
|
let settled = false;
|
|
22339
22394
|
const helloTimer = setTimeout(() => socket.close(4001, "hello timeout"), 1e4);
|
|
22340
22395
|
const finish = (error) => {
|
|
@@ -22343,7 +22398,7 @@ var HostServerConnection = class {
|
|
|
22343
22398
|
clearTimeout(helloTimer);
|
|
22344
22399
|
this.online = false;
|
|
22345
22400
|
if (this.socket === socket) this.socket = void 0;
|
|
22346
|
-
void this.dropTunnels().finally(() => error === void 0 ?
|
|
22401
|
+
void this.dropTunnels().finally(() => error === void 0 ? resolve4() : reject(error));
|
|
22347
22402
|
};
|
|
22348
22403
|
socket.onopen = () => {
|
|
22349
22404
|
this.sendControl("hello", {
|
|
@@ -22881,14 +22936,14 @@ var HostServerConnection = class {
|
|
|
22881
22936
|
waitBeforeRetry(baseDelay) {
|
|
22882
22937
|
const spread = baseDelay * this.config.reconnect.jitter;
|
|
22883
22938
|
const delay = Math.max(0, Math.round(baseDelay - spread + Math.random() * spread * 2));
|
|
22884
|
-
return new Promise((
|
|
22939
|
+
return new Promise((resolve4) => {
|
|
22885
22940
|
const timer = setTimeout(() => {
|
|
22886
22941
|
this.retryWake = void 0;
|
|
22887
|
-
|
|
22942
|
+
resolve4();
|
|
22888
22943
|
}, delay);
|
|
22889
22944
|
this.retryWake = () => {
|
|
22890
22945
|
clearTimeout(timer);
|
|
22891
|
-
|
|
22946
|
+
resolve4();
|
|
22892
22947
|
};
|
|
22893
22948
|
});
|
|
22894
22949
|
}
|
|
@@ -23921,7 +23976,7 @@ function historyRequestPayload(payload, maxMessages) {
|
|
|
23921
23976
|
return { ...payload, maxMessages };
|
|
23922
23977
|
}
|
|
23923
23978
|
function withTimeout(promise, ms, message) {
|
|
23924
|
-
return new Promise((
|
|
23979
|
+
return new Promise((resolve4, reject) => {
|
|
23925
23980
|
const timer = setTimeout(() => {
|
|
23926
23981
|
reject(new RpcError("TIMEOUT", message, void 0, true));
|
|
23927
23982
|
}, ms);
|
|
@@ -23929,7 +23984,7 @@ function withTimeout(promise, ms, message) {
|
|
|
23929
23984
|
promise.then(
|
|
23930
23985
|
(value) => {
|
|
23931
23986
|
clearTimeout(timer);
|
|
23932
|
-
|
|
23987
|
+
resolve4(value);
|
|
23933
23988
|
},
|
|
23934
23989
|
(error) => {
|
|
23935
23990
|
clearTimeout(timer);
|
|
@@ -24032,6 +24087,7 @@ var HARNESS_REMOTE_ALLOWLIST = [
|
|
|
24032
24087
|
"messageFeedback/list",
|
|
24033
24088
|
"messageFeedback/put",
|
|
24034
24089
|
"pluginInventory/list",
|
|
24090
|
+
"permissionPresets/catalog",
|
|
24035
24091
|
"session/attachment",
|
|
24036
24092
|
"session/cancel",
|
|
24037
24093
|
"session/canOpenWorkspacePath",
|
|
@@ -24075,12 +24131,13 @@ var HARNESS_REMOTE_ALLOWLIST = [
|
|
|
24075
24131
|
];
|
|
24076
24132
|
var allowedEndpoints = new Set(HARNESS_REMOTE_ALLOWLIST);
|
|
24077
24133
|
var HarnessRemoteBridge = class {
|
|
24078
|
-
constructor(gateway, publish, logger, harnessVersion, terminal = new TerminalPolicy(() => false, "", /* @__PURE__ */ new Map())) {
|
|
24134
|
+
constructor(gateway, publish, logger, harnessVersion, terminal = new TerminalPolicy(() => false, "", /* @__PURE__ */ new Map()), codexWorkspace) {
|
|
24079
24135
|
this.gateway = gateway;
|
|
24080
24136
|
this.publish = publish;
|
|
24081
24137
|
this.logger = logger;
|
|
24082
24138
|
this.harnessVersion = harnessVersion;
|
|
24083
24139
|
this.terminal = terminal;
|
|
24140
|
+
this.codexWorkspace = codexWorkspace;
|
|
24084
24141
|
}
|
|
24085
24142
|
streams = /* @__PURE__ */ new Map();
|
|
24086
24143
|
incomingTransfers = /* @__PURE__ */ new Map();
|
|
@@ -24096,6 +24153,18 @@ var HarnessRemoteBridge = class {
|
|
|
24096
24153
|
}
|
|
24097
24154
|
}
|
|
24098
24155
|
const reservation = params.endpoint.startsWith("terminal/") ? this.terminal.check(params.endpoint, params.payload) : void 0;
|
|
24156
|
+
let codexResult;
|
|
24157
|
+
if (this.codexWorkspace !== void 0) {
|
|
24158
|
+
try {
|
|
24159
|
+
codexResult = await this.codexWorkspace.call(params.endpoint, params.payload, AbortSignal.timeout(6e4));
|
|
24160
|
+
} catch (error) {
|
|
24161
|
+
if (reservation !== void 0) {
|
|
24162
|
+
this.terminal.result(params.endpoint, params.payload, { ok: false, error: { code: "FAILED", message: "", details: {} } }, reservation);
|
|
24163
|
+
}
|
|
24164
|
+
throw error;
|
|
24165
|
+
}
|
|
24166
|
+
}
|
|
24167
|
+
if (codexResult !== void 0) return reservation === void 0 ? codexResult : this.terminal.result(params.endpoint, params.payload, codexResult, reservation);
|
|
24099
24168
|
if (params.endpoint === "session/canOpenWorkspacePath") {
|
|
24100
24169
|
return { ok: true, value: true };
|
|
24101
24170
|
}
|
|
@@ -24248,7 +24317,7 @@ var HarnessRemoteBridge = class {
|
|
|
24248
24317
|
this.streams.set(params.streamId, { controller });
|
|
24249
24318
|
let source;
|
|
24250
24319
|
try {
|
|
24251
|
-
source = await this.gateway.open(params.endpoint, params.payload, controller.signal);
|
|
24320
|
+
source = (this.codexWorkspace === void 0 ? void 0 : await this.codexWorkspace.open(params.endpoint, params.payload, controller.signal)) ?? await this.gateway.open(params.endpoint, params.payload, controller.signal);
|
|
24252
24321
|
controller.signal.throwIfAborted();
|
|
24253
24322
|
} catch (error) {
|
|
24254
24323
|
this.streams.delete(params.streamId);
|
|
@@ -24273,6 +24342,7 @@ var HarnessRemoteBridge = class {
|
|
|
24273
24342
|
this.incomingTransfers.clear();
|
|
24274
24343
|
this.outgoingTransfers.clear();
|
|
24275
24344
|
for (const [, stream] of streams) stream.controller.abort(reason);
|
|
24345
|
+
await this.codexWorkspace?.closeAll();
|
|
24276
24346
|
}
|
|
24277
24347
|
assertAllowed(endpoint) {
|
|
24278
24348
|
if (!allowedEndpoints.has(endpoint) && !TERMINAL_CALLS.has(endpoint) && !TERMINAL_STREAMS.has(endpoint)) {
|
|
@@ -24445,15 +24515,15 @@ var CodexAppServerClient = class {
|
|
|
24445
24515
|
const child = this.process;
|
|
24446
24516
|
this.process = void 0;
|
|
24447
24517
|
if (child === void 0 || child.exitCode !== null || child.killed) return;
|
|
24448
|
-
await new Promise((
|
|
24518
|
+
await new Promise((resolve4) => {
|
|
24449
24519
|
const timer = setTimeout(() => {
|
|
24450
24520
|
child.kill("SIGKILL");
|
|
24451
|
-
|
|
24521
|
+
resolve4();
|
|
24452
24522
|
}, 2e3);
|
|
24453
24523
|
timer.unref?.();
|
|
24454
24524
|
child.once("exit", () => {
|
|
24455
24525
|
clearTimeout(timer);
|
|
24456
|
-
|
|
24526
|
+
resolve4();
|
|
24457
24527
|
});
|
|
24458
24528
|
child.kill("SIGTERM");
|
|
24459
24529
|
});
|
|
@@ -24509,13 +24579,13 @@ var CodexAppServerClient = class {
|
|
|
24509
24579
|
}
|
|
24510
24580
|
request(method, params, timeoutMs) {
|
|
24511
24581
|
const id4 = this.nextId++;
|
|
24512
|
-
const result = new Promise((
|
|
24582
|
+
const result = new Promise((resolve4, reject) => {
|
|
24513
24583
|
const timer = setTimeout(() => {
|
|
24514
24584
|
this.pending.delete(id4);
|
|
24515
24585
|
reject(new CodexAppServerError("CODEX_REQUEST_TIMEOUT", "Codex App Server request timed out."));
|
|
24516
24586
|
}, timeoutMs);
|
|
24517
24587
|
timer.unref?.();
|
|
24518
|
-
this.pending.set(id4, { resolve:
|
|
24588
|
+
this.pending.set(id4, { resolve: resolve4, reject, timer });
|
|
24519
24589
|
});
|
|
24520
24590
|
try {
|
|
24521
24591
|
this.write({ id: id4, method, params });
|
|
@@ -25053,6 +25123,19 @@ var CodexRemoteDomain = class {
|
|
|
25053
25123
|
...this.unavailableCode === void 0 ? {} : { error: this.unavailableCode }
|
|
25054
25124
|
};
|
|
25055
25125
|
}
|
|
25126
|
+
/** Resolve a thread cwd for Host-owned workspace/terminal carriers. */
|
|
25127
|
+
async resolveThreadWorkspace(connectionId, threadId) {
|
|
25128
|
+
if (!this.peers.has(connectionId)) throw new RpcError("CODEX_THREAD_UNAVAILABLE", "The CodeX thread is not available on this connection.");
|
|
25129
|
+
try {
|
|
25130
|
+
const known = await this.readKnownThread(threadId);
|
|
25131
|
+
return known.cwd;
|
|
25132
|
+
} catch (error) {
|
|
25133
|
+
if (error instanceof RpcError && error.code === "CODEX_THREAD_NOT_ALLOWED") {
|
|
25134
|
+
throw new RpcError("CODEX_THREAD_UNAVAILABLE", "The CodeX thread is not available.");
|
|
25135
|
+
}
|
|
25136
|
+
throw new RpcError("CODEX_THREAD_UNAVAILABLE", "The CodeX thread is not available.");
|
|
25137
|
+
}
|
|
25138
|
+
}
|
|
25056
25139
|
createPeer(context, publish) {
|
|
25057
25140
|
if (!this.config.enabled) return void 0;
|
|
25058
25141
|
const bridge = new CodexPeerBridge(this, context, publish, this.logger);
|
|
@@ -26127,7 +26210,7 @@ var StdioAcpAdapter = class {
|
|
|
26127
26210
|
const c = this.ensure();
|
|
26128
26211
|
const id4 = this.nextId++;
|
|
26129
26212
|
c.stdin.write(JSON.stringify({ jsonrpc: "2.0", id: id4, method, params }) + "\n");
|
|
26130
|
-
return new Promise((
|
|
26213
|
+
return new Promise((resolve4, reject) => {
|
|
26131
26214
|
let buf = "";
|
|
26132
26215
|
const onData = async (d) => {
|
|
26133
26216
|
buf += d;
|
|
@@ -26138,7 +26221,7 @@ var StdioAcpAdapter = class {
|
|
|
26138
26221
|
const m = JSON.parse(l);
|
|
26139
26222
|
if (m.id === id4) {
|
|
26140
26223
|
c.stdout.off("data", onData);
|
|
26141
|
-
m.error ? reject(new Error(m.error.message ?? "ACP error")) :
|
|
26224
|
+
m.error ? reject(new Error(m.error.message ?? "ACP error")) : resolve4(m.result);
|
|
26142
26225
|
} else if (m.method && onNotification) await onNotification(m.params);
|
|
26143
26226
|
} catch {
|
|
26144
26227
|
}
|
|
@@ -26152,14 +26235,460 @@ var StdioAcpAdapter = class {
|
|
|
26152
26235
|
|
|
26153
26236
|
// src/service.ts
|
|
26154
26237
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
26238
|
+
|
|
26239
|
+
// src/codex-workspace-bridge.ts
|
|
26240
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
26241
|
+
import { realpath as realpath2, lstat, readdir as readdir3, readFile as readFile4, stat as stat5, watch } from "node:fs/promises";
|
|
26242
|
+
import { isAbsolute as isAbsolute4, join as join6, relative as relative2, resolve as resolve3 } from "node:path";
|
|
26243
|
+
var MAX_READ_BYTES = 4 * 1024 * 1024;
|
|
26244
|
+
var MAX_INPUT_BYTES = 64 * 1024;
|
|
26245
|
+
var MAX_COLS = 240;
|
|
26246
|
+
var MAX_ROWS = 100;
|
|
26247
|
+
var MAX_TERMINALS = 256;
|
|
26248
|
+
var MAX_SCREEN_BYTES = 256 * 1024;
|
|
26249
|
+
var TERMINAL_SCROLLBACK = 2e3;
|
|
26250
|
+
var TERMINAL_TYPE = "xterm-256color";
|
|
26251
|
+
var CodexWorkspaceState = class {
|
|
26252
|
+
terminals = /* @__PURE__ */ new Map();
|
|
26253
|
+
};
|
|
26254
|
+
var DEFAULT_SHELL = process.platform === "win32" ? { path: "cmd.exe", args: [], name: "Command Prompt" } : { path: "/bin/sh", args: [], name: "sh" };
|
|
26255
|
+
var AsyncQueue = class {
|
|
26256
|
+
values = [];
|
|
26257
|
+
waiters = [];
|
|
26258
|
+
ended = false;
|
|
26259
|
+
push(value) {
|
|
26260
|
+
if (this.ended) return;
|
|
26261
|
+
const waiter = this.waiters.shift();
|
|
26262
|
+
if (waiter) waiter({ done: false, value });
|
|
26263
|
+
else this.values.push(value);
|
|
26264
|
+
}
|
|
26265
|
+
end() {
|
|
26266
|
+
this.ended = true;
|
|
26267
|
+
while (this.waiters.length) this.waiters.shift()({ done: true, value: void 0 });
|
|
26268
|
+
}
|
|
26269
|
+
next() {
|
|
26270
|
+
const value = this.values.shift();
|
|
26271
|
+
if (value !== void 0) return Promise.resolve({ done: false, value });
|
|
26272
|
+
if (this.ended) return Promise.resolve({ done: true, value: void 0 });
|
|
26273
|
+
return new Promise((resolve4) => this.waiters.push(resolve4));
|
|
26274
|
+
}
|
|
26275
|
+
[Symbol.asyncIterator]() {
|
|
26276
|
+
return this;
|
|
26277
|
+
}
|
|
26278
|
+
};
|
|
26279
|
+
var CodexWorkspaceBridge = class {
|
|
26280
|
+
constructor(resolveCwd, terminalEnabled, state = new CodexWorkspaceState(), spawnTerminal = pipeTerminalSpawner()) {
|
|
26281
|
+
this.resolveCwd = resolveCwd;
|
|
26282
|
+
this.terminalEnabled = terminalEnabled;
|
|
26283
|
+
this.terminals = state.terminals;
|
|
26284
|
+
this.spawnTerminal = spawnTerminal;
|
|
26285
|
+
}
|
|
26286
|
+
terminals;
|
|
26287
|
+
ownedSubscribers = /* @__PURE__ */ new Set();
|
|
26288
|
+
spawnTerminal;
|
|
26289
|
+
isCodeXScope(value) {
|
|
26290
|
+
return parseCodexSessionId(value) !== void 0;
|
|
26291
|
+
}
|
|
26292
|
+
async call(endpoint, payload, signal) {
|
|
26293
|
+
const args = argsOf(payload);
|
|
26294
|
+
const scope = args.workspaceFileScopeId;
|
|
26295
|
+
const session = args.agentId ?? args.sessionId;
|
|
26296
|
+
const raw = scope ?? session;
|
|
26297
|
+
const codex = parseCodexSessionId(raw);
|
|
26298
|
+
if (codex === void 0) {
|
|
26299
|
+
if (typeof raw === "string" && raw.startsWith("codex:")) throw new RpcError("CODEX_SESSION_INVALID", "The CodeX session identifier is invalid.");
|
|
26300
|
+
return void 0;
|
|
26301
|
+
}
|
|
26302
|
+
if (endpoint.startsWith("workspaceFiles/")) return this.fileCall(endpoint, codex.sessionId, args, signal);
|
|
26303
|
+
if (endpoint.startsWith("terminal/")) return this.terminalCall(endpoint, codex.sessionId, args, signal);
|
|
26304
|
+
return void 0;
|
|
26305
|
+
}
|
|
26306
|
+
async open(endpoint, payload, signal) {
|
|
26307
|
+
const args = argsOf(payload);
|
|
26308
|
+
const raw = args.workspaceFileScopeId ?? args.agentId ?? args.sessionId;
|
|
26309
|
+
const codex = parseCodexSessionId(raw);
|
|
26310
|
+
if (codex === void 0) {
|
|
26311
|
+
if (typeof raw === "string" && raw.startsWith("codex:")) throw new RpcError("CODEX_SESSION_INVALID", "The CodeX session identifier is invalid.");
|
|
26312
|
+
return void 0;
|
|
26313
|
+
}
|
|
26314
|
+
if (endpoint === "workspaceFiles/changes") return this.watchChanges(codex.sessionId, args, signal);
|
|
26315
|
+
if (endpoint === "terminal/retain") return this.retain(codex.sessionId, args, signal);
|
|
26316
|
+
if (endpoint === "terminal/follow") return this.follow(codex.sessionId, args, signal);
|
|
26317
|
+
return void 0;
|
|
26318
|
+
}
|
|
26319
|
+
async closeAll() {
|
|
26320
|
+
for (const queue of this.ownedSubscribers) queue.end();
|
|
26321
|
+
this.ownedSubscribers.clear();
|
|
26322
|
+
}
|
|
26323
|
+
async fileCall(endpoint, sessionId, args, signal) {
|
|
26324
|
+
const root = await this.rootFor(sessionId, signal);
|
|
26325
|
+
const path = typeof args.path === "string" ? args.path : ".";
|
|
26326
|
+
const target2 = await this.safePath(root, path, endpoint === "workspaceFiles/list");
|
|
26327
|
+
try {
|
|
26328
|
+
if (endpoint === "workspaceFiles/list") {
|
|
26329
|
+
const entries = await readdir3(target2, { withFileTypes: true });
|
|
26330
|
+
const result = [];
|
|
26331
|
+
for (const entry of entries.slice(0, 500)) {
|
|
26332
|
+
const item = join6(target2, entry.name);
|
|
26333
|
+
const info2 = await lstat(item);
|
|
26334
|
+
if (info2.isSymbolicLink()) continue;
|
|
26335
|
+
result.push({ name: entry.name, type: info2.isDirectory() ? "directory" : info2.isFile() ? "file" : "other", ...info2.isFile() ? { size: info2.size } : {} });
|
|
26336
|
+
}
|
|
26337
|
+
return { ok: true, value: { path, entries: result, truncated: entries.length > 500 } };
|
|
26338
|
+
}
|
|
26339
|
+
const info = await stat5(target2);
|
|
26340
|
+
if (endpoint === "workspaceFiles/stat") return { ok: true, value: { path, type: info.isDirectory() ? "directory" : info.isFile() ? "file" : "other", size: info.size, modifiedAt: info.mtimeMs } };
|
|
26341
|
+
if (!info.isFile()) throw new RpcError("CODEX_WORKSPACE_INVALID_PATH", "The requested workspace path is not a file.");
|
|
26342
|
+
const offset = readOffset(args);
|
|
26343
|
+
const limit = readLimit(args);
|
|
26344
|
+
const bytes = await readFile4(target2);
|
|
26345
|
+
if (bytes.byteLength > MAX_READ_BYTES) throw new RpcError("CODEX_WORKSPACE_TOO_LARGE", "The requested workspace file is too large.");
|
|
26346
|
+
if (endpoint === "workspaceFiles/readBytes") {
|
|
26347
|
+
const slice2 = bytes.subarray(offset, Math.min(offset + limit, bytes.length));
|
|
26348
|
+
return { ok: true, value: { path, offset, bytes: slice2.toString("base64"), eof: offset + slice2.length >= bytes.length } };
|
|
26349
|
+
}
|
|
26350
|
+
const text = bytes.toString("utf8");
|
|
26351
|
+
const slice = text.slice(offset, offset + limit);
|
|
26352
|
+
return { ok: true, value: { absolutePath: target2, version: `${info.mtimeMs}:${info.size}`, text: slice, offset, lines: slice.split("\n").length, eof: offset + slice.length >= text.length } };
|
|
26353
|
+
} catch (error) {
|
|
26354
|
+
if (error instanceof RpcError) throw error;
|
|
26355
|
+
throw new RpcError("CODEX_WORKSPACE_UNAVAILABLE", "The CodeX workspace file is unavailable.");
|
|
26356
|
+
}
|
|
26357
|
+
}
|
|
26358
|
+
async watchChanges(sessionId, args, signal) {
|
|
26359
|
+
const root = await this.rootFor(sessionId, signal);
|
|
26360
|
+
const target2 = await this.safePath(root, typeof args.path === "string" ? args.path : ".", true);
|
|
26361
|
+
const queue = new AsyncQueue();
|
|
26362
|
+
const watcher = watch(target2, { recursive: false });
|
|
26363
|
+
const abort = () => {
|
|
26364
|
+
watcher.return?.();
|
|
26365
|
+
queue.end();
|
|
26366
|
+
};
|
|
26367
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
26368
|
+
void (async () => {
|
|
26369
|
+
try {
|
|
26370
|
+
for await (const event of watcher) queue.push({ type: event.eventType, path: event.filename ?? "" });
|
|
26371
|
+
} catch {
|
|
26372
|
+
} finally {
|
|
26373
|
+
signal.removeEventListener("abort", abort);
|
|
26374
|
+
queue.end();
|
|
26375
|
+
}
|
|
26376
|
+
})();
|
|
26377
|
+
return queue;
|
|
26378
|
+
}
|
|
26379
|
+
/**
|
|
26380
|
+
* `terminal/retain` only acknowledges the retention window: it never takes
|
|
26381
|
+
* input ownership and never replays output. Recovery reads the next snapshot.
|
|
26382
|
+
*/
|
|
26383
|
+
async retain(sessionId, args, signal) {
|
|
26384
|
+
this.assertTerminalEnabled();
|
|
26385
|
+
await this.rootFor(sessionId, signal);
|
|
26386
|
+
this.requireTerminal(sessionId, String(args.id));
|
|
26387
|
+
const queue = new AsyncQueue();
|
|
26388
|
+
queue.push({ type: "retained" });
|
|
26389
|
+
this.ownedSubscribers.add(queue);
|
|
26390
|
+
signal.addEventListener("abort", () => {
|
|
26391
|
+
this.ownedSubscribers.delete(queue);
|
|
26392
|
+
queue.end();
|
|
26393
|
+
}, { once: true });
|
|
26394
|
+
return queue;
|
|
26395
|
+
}
|
|
26396
|
+
/** `terminal/follow` takes input ownership and starts with a screen snapshot. */
|
|
26397
|
+
async follow(sessionId, args, signal) {
|
|
26398
|
+
this.assertTerminalEnabled();
|
|
26399
|
+
await this.rootFor(sessionId, signal);
|
|
26400
|
+
const attachmentId = typeof args.attachmentId === "string" && args.attachmentId.length > 0 ? args.attachmentId : void 0;
|
|
26401
|
+
if (attachmentId === void 0) throw new RpcError("INVALID_MESSAGE", "The terminal attachment is invalid.");
|
|
26402
|
+
const terminal = this.requireTerminal(sessionId, String(args.id));
|
|
26403
|
+
const queue = new AsyncQueue();
|
|
26404
|
+
if (terminal.controllerId !== attachmentId) {
|
|
26405
|
+
terminal.controllerId = attachmentId;
|
|
26406
|
+
this.emit(terminal, { type: "state", info: terminalInfo(terminal) });
|
|
26407
|
+
}
|
|
26408
|
+
queue.push({ type: "snapshot", sequence: terminal.sequence, screen: screenOf(terminal), info: terminalInfo(terminal) });
|
|
26409
|
+
terminal.subscribers.add(queue);
|
|
26410
|
+
this.ownedSubscribers.add(queue);
|
|
26411
|
+
signal.addEventListener("abort", () => {
|
|
26412
|
+
terminal.subscribers.delete(queue);
|
|
26413
|
+
this.ownedSubscribers.delete(queue);
|
|
26414
|
+
queue.end();
|
|
26415
|
+
if (terminal.controllerId === attachmentId) {
|
|
26416
|
+
terminal.controllerId = void 0;
|
|
26417
|
+
this.emit(terminal, { type: "state", info: terminalInfo(terminal) });
|
|
26418
|
+
}
|
|
26419
|
+
}, { once: true });
|
|
26420
|
+
return queue;
|
|
26421
|
+
}
|
|
26422
|
+
async terminalCall(endpoint, sessionId, args, signal) {
|
|
26423
|
+
this.assertTerminalEnabled();
|
|
26424
|
+
const cwd = await this.rootFor(sessionId, signal);
|
|
26425
|
+
if (endpoint === "terminal/environment") {
|
|
26426
|
+
return { ok: true, value: { cwd, maxInputBytes: MAX_INPUT_BYTES, maxCols: MAX_COLS, maxRows: MAX_ROWS, scrollback: TERMINAL_SCROLLBACK } };
|
|
26427
|
+
}
|
|
26428
|
+
if (endpoint === "terminal/shells") return { ok: true, value: [shellInfo(DEFAULT_SHELL)] };
|
|
26429
|
+
if (endpoint === "terminal/list") return { ok: true, value: [...this.terminals.values()].filter((item) => item.sessionId === sessionId).map(terminalInfo) };
|
|
26430
|
+
if (endpoint === "terminal/create") return this.createTerminal(sessionId, cwd, args);
|
|
26431
|
+
const id4 = stringId(args.id);
|
|
26432
|
+
const terminal = this.requireTerminal(sessionId, id4);
|
|
26433
|
+
if (endpoint === "terminal/write") {
|
|
26434
|
+
const data2 = typeof args.data === "string" ? args.data : "";
|
|
26435
|
+
if (Buffer.byteLength(data2) > MAX_INPUT_BYTES) throw new RpcError("INVALID_MESSAGE", "Terminal input is too large.");
|
|
26436
|
+
if (terminal.process === void 0) throw new RpcError("CODEX_TERMINAL_NOT_FOUND", "The CodeX terminal is no longer available.");
|
|
26437
|
+
await terminal.process.write(data2);
|
|
26438
|
+
return { ok: true };
|
|
26439
|
+
}
|
|
26440
|
+
if (endpoint === "terminal/resize") {
|
|
26441
|
+
terminal.cols = bounded(args.cols, terminal.cols, MAX_COLS);
|
|
26442
|
+
terminal.rows = bounded(args.rows, terminal.rows, MAX_ROWS);
|
|
26443
|
+
await terminal.process?.resize(terminal.cols, terminal.rows);
|
|
26444
|
+
return { ok: true };
|
|
26445
|
+
}
|
|
26446
|
+
if (endpoint === "terminal/rename") {
|
|
26447
|
+
if (typeof args.title === "string" && args.title.length > 0) terminal.title = args.title.slice(0, 128);
|
|
26448
|
+
return { ok: true };
|
|
26449
|
+
}
|
|
26450
|
+
if (endpoint === "terminal/close") {
|
|
26451
|
+
this.disposeTerminal(terminal);
|
|
26452
|
+
this.terminals.delete(`${sessionId}/${id4}`);
|
|
26453
|
+
return { ok: true };
|
|
26454
|
+
}
|
|
26455
|
+
throw new RpcError("METHOD_NOT_FOUND", "The requested terminal method does not exist.");
|
|
26456
|
+
}
|
|
26457
|
+
async createTerminal(sessionId, cwd, args) {
|
|
26458
|
+
const request = isRecord13(args.request) ? args.request : {};
|
|
26459
|
+
const id4 = stringId(request.id);
|
|
26460
|
+
if ([...this.terminals.values()].some((item) => item.sessionId === sessionId && item.id === id4)) throw new RpcError("REQUEST_CONFLICT", "The terminal id is already active.");
|
|
26461
|
+
if (this.terminals.size >= MAX_TERMINALS) throw new RpcError("RATE_LIMITED", "Too many remote terminals are active.", void 0, true);
|
|
26462
|
+
if (request.shellPath !== void 0 && request.shellPath !== DEFAULT_SHELL.path) throw new RpcError("INVALID_MESSAGE", "The requested shell is not available for this workspace.");
|
|
26463
|
+
const cols = bounded(request.cols, 80, MAX_COLS);
|
|
26464
|
+
const rows = bounded(request.rows, 24, MAX_ROWS);
|
|
26465
|
+
const terminal = {
|
|
26466
|
+
sessionId,
|
|
26467
|
+
id: id4,
|
|
26468
|
+
title: DEFAULT_SHELL.name,
|
|
26469
|
+
shell: DEFAULT_SHELL,
|
|
26470
|
+
cwd,
|
|
26471
|
+
cols,
|
|
26472
|
+
rows,
|
|
26473
|
+
sequence: 0,
|
|
26474
|
+
screen: [],
|
|
26475
|
+
screenBytes: 0,
|
|
26476
|
+
truncated: false,
|
|
26477
|
+
subscribers: /* @__PURE__ */ new Set(),
|
|
26478
|
+
state: "running",
|
|
26479
|
+
exitCode: null
|
|
26480
|
+
};
|
|
26481
|
+
this.terminals.set(`${sessionId}/${id4}`, terminal);
|
|
26482
|
+
try {
|
|
26483
|
+
terminal.process = await this.spawnTerminal({
|
|
26484
|
+
argv: [DEFAULT_SHELL.path, ...DEFAULT_SHELL.args],
|
|
26485
|
+
cwd,
|
|
26486
|
+
cols,
|
|
26487
|
+
rows,
|
|
26488
|
+
terminalType: TERMINAL_TYPE,
|
|
26489
|
+
env: { DSH_SESSION_ID: sessionId }
|
|
26490
|
+
});
|
|
26491
|
+
} catch {
|
|
26492
|
+
this.terminals.delete(`${sessionId}/${id4}`);
|
|
26493
|
+
throw new RpcError("CODEX_TERMINAL_UNAVAILABLE", "The CodeX terminal could not be started.");
|
|
26494
|
+
}
|
|
26495
|
+
void this.pump(terminal, terminal.process);
|
|
26496
|
+
return { ok: true, value: terminalInfo(terminal) };
|
|
26497
|
+
}
|
|
26498
|
+
/** Streams process output as ordered `output` frames, then reports the exit. */
|
|
26499
|
+
async pump(terminal, process2) {
|
|
26500
|
+
try {
|
|
26501
|
+
for await (const chunk of process2.output) {
|
|
26502
|
+
if (chunk.length === 0) continue;
|
|
26503
|
+
terminal.sequence += 1;
|
|
26504
|
+
this.appendScreen(terminal, chunk);
|
|
26505
|
+
this.emit(terminal, { type: "output", sequence: terminal.sequence, data: chunk });
|
|
26506
|
+
}
|
|
26507
|
+
} catch {
|
|
26508
|
+
terminal.state = "failed";
|
|
26509
|
+
terminal.error = "The terminal output stream failed.";
|
|
26510
|
+
}
|
|
26511
|
+
const outcome = await process2.completed;
|
|
26512
|
+
terminal.exitCode = outcome.exitCode;
|
|
26513
|
+
if (terminal.state === "running") terminal.state = "exited";
|
|
26514
|
+
this.emit(terminal, { type: "state", info: terminalInfo(terminal) });
|
|
26515
|
+
this.endSubscribers(terminal);
|
|
26516
|
+
}
|
|
26517
|
+
appendScreen(terminal, chunk) {
|
|
26518
|
+
terminal.screen.push(chunk);
|
|
26519
|
+
terminal.screenBytes += Buffer.byteLength(chunk);
|
|
26520
|
+
while (terminal.screenBytes > MAX_SCREEN_BYTES && terminal.screen.length > 1) {
|
|
26521
|
+
terminal.screenBytes -= Buffer.byteLength(terminal.screen.shift());
|
|
26522
|
+
terminal.truncated = true;
|
|
26523
|
+
}
|
|
26524
|
+
}
|
|
26525
|
+
async rootFor(sessionId, signal) {
|
|
26526
|
+
const parsed = parseCodexSessionId(sessionId);
|
|
26527
|
+
if (!parsed) throw new RpcError("CODEX_SESSION_INVALID", "The CodeX session identifier is invalid.");
|
|
26528
|
+
const cwd = await this.resolveCwd(parsed.threadId, signal);
|
|
26529
|
+
if (!cwd) throw new RpcError("CODEX_WORKSPACE_UNAVAILABLE", "The CodeX thread has no available workspace.");
|
|
26530
|
+
try {
|
|
26531
|
+
const root = await realpath2(cwd);
|
|
26532
|
+
const info = await stat5(root);
|
|
26533
|
+
if (!info.isDirectory()) throw new Error();
|
|
26534
|
+
return root;
|
|
26535
|
+
} catch {
|
|
26536
|
+
throw new RpcError("CODEX_WORKSPACE_UNAVAILABLE", "The CodeX workspace is unavailable.");
|
|
26537
|
+
}
|
|
26538
|
+
}
|
|
26539
|
+
async safePath(root, path, directory) {
|
|
26540
|
+
if (isAbsolute4(path)) throw new RpcError("CODEX_WORKSPACE_PATH_DENIED", "The requested workspace path is outside the CodeX workspace.");
|
|
26541
|
+
const candidate = resolve3(root, path);
|
|
26542
|
+
const rel = relative2(root, candidate);
|
|
26543
|
+
if (rel.startsWith("..") || isAbsolute4(rel)) throw new RpcError("CODEX_WORKSPACE_PATH_DENIED", "The requested workspace path is outside the CodeX workspace.");
|
|
26544
|
+
try {
|
|
26545
|
+
const info = await lstat(candidate);
|
|
26546
|
+
if (info.isSymbolicLink()) throw new Error();
|
|
26547
|
+
const canonical = await realpath2(candidate);
|
|
26548
|
+
const canonicalRel = relative2(root, canonical);
|
|
26549
|
+
if (canonicalRel.startsWith("..") || isAbsolute4(canonicalRel)) throw new Error();
|
|
26550
|
+
if (directory && !info.isDirectory()) throw new Error();
|
|
26551
|
+
return canonical;
|
|
26552
|
+
} catch {
|
|
26553
|
+
throw new RpcError("CODEX_WORKSPACE_PATH_DENIED", "The requested workspace path is unavailable.");
|
|
26554
|
+
}
|
|
26555
|
+
}
|
|
26556
|
+
emit(terminal, value) {
|
|
26557
|
+
for (const subscriber of terminal.subscribers) subscriber.push(value);
|
|
26558
|
+
}
|
|
26559
|
+
endSubscribers(terminal) {
|
|
26560
|
+
for (const subscriber of terminal.subscribers) subscriber.end();
|
|
26561
|
+
terminal.subscribers.clear();
|
|
26562
|
+
}
|
|
26563
|
+
requireTerminal(sessionId, id4) {
|
|
26564
|
+
const terminal = this.terminals.get(`${sessionId}/${id4}`);
|
|
26565
|
+
if (!terminal) throw new RpcError("CODEX_TERMINAL_NOT_FOUND", "The CodeX terminal is no longer available.");
|
|
26566
|
+
return terminal;
|
|
26567
|
+
}
|
|
26568
|
+
assertTerminalEnabled() {
|
|
26569
|
+
if (!this.terminalEnabled()) throw new RpcError("TERMINAL_DISABLED", "Remote terminal is disabled on this Host.");
|
|
26570
|
+
}
|
|
26571
|
+
disposeTerminal(terminal) {
|
|
26572
|
+
void Promise.resolve(terminal.process?.terminate()).catch(() => void 0);
|
|
26573
|
+
if (terminal.state === "running") {
|
|
26574
|
+
terminal.state = "exited";
|
|
26575
|
+
this.emit(terminal, { type: "state", info: terminalInfo(terminal) });
|
|
26576
|
+
}
|
|
26577
|
+
this.endSubscribers(terminal);
|
|
26578
|
+
}
|
|
26579
|
+
};
|
|
26580
|
+
function pipeTerminalSpawner() {
|
|
26581
|
+
return async (spec) => {
|
|
26582
|
+
const child = spawn4(spec.argv[0], spec.argv.slice(1), {
|
|
26583
|
+
cwd: spec.cwd,
|
|
26584
|
+
stdio: "pipe",
|
|
26585
|
+
windowsHide: true,
|
|
26586
|
+
env: { ...process.env, ...spec.env }
|
|
26587
|
+
});
|
|
26588
|
+
const queue = new AsyncQueue();
|
|
26589
|
+
child.stdout.on("data", (data2) => queue.push(Buffer.from(data2).toString("utf8")));
|
|
26590
|
+
child.stderr.on("data", (data2) => queue.push(Buffer.from(data2).toString("utf8")));
|
|
26591
|
+
child.on("error", () => queue.end());
|
|
26592
|
+
const completed = new Promise((resolve4) => {
|
|
26593
|
+
child.on("error", () => {
|
|
26594
|
+
queue.end();
|
|
26595
|
+
resolve4({ exitCode: null });
|
|
26596
|
+
});
|
|
26597
|
+
child.on("exit", (code) => {
|
|
26598
|
+
queue.end();
|
|
26599
|
+
resolve4({ exitCode: code });
|
|
26600
|
+
});
|
|
26601
|
+
});
|
|
26602
|
+
return {
|
|
26603
|
+
output: queue,
|
|
26604
|
+
write: (data2) => {
|
|
26605
|
+
child.stdin.write(data2);
|
|
26606
|
+
},
|
|
26607
|
+
resize: () => void 0,
|
|
26608
|
+
terminate: () => {
|
|
26609
|
+
if (!child.killed) child.kill();
|
|
26610
|
+
},
|
|
26611
|
+
completed
|
|
26612
|
+
};
|
|
26613
|
+
};
|
|
26614
|
+
}
|
|
26615
|
+
function subprocessTerminalSpawner(subprocess) {
|
|
26616
|
+
return async (spec) => {
|
|
26617
|
+
const handle = await subprocess.spawnTerminal({
|
|
26618
|
+
argv: [...spec.argv],
|
|
26619
|
+
cwd: spec.cwd,
|
|
26620
|
+
cols: spec.cols,
|
|
26621
|
+
rows: spec.rows,
|
|
26622
|
+
terminalType: spec.terminalType,
|
|
26623
|
+
env: spec.env
|
|
26624
|
+
});
|
|
26625
|
+
const queue = new AsyncQueue();
|
|
26626
|
+
handle.output.setEncoding?.("utf8");
|
|
26627
|
+
handle.output.on("data", (chunk) => queue.push(typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8")));
|
|
26628
|
+
handle.output.once("close", () => queue.end());
|
|
26629
|
+
handle.output.once("error", () => queue.end());
|
|
26630
|
+
return {
|
|
26631
|
+
output: queue,
|
|
26632
|
+
write: (data2) => handle.write(data2),
|
|
26633
|
+
resize: (cols, rows) => handle.resize(cols, rows),
|
|
26634
|
+
terminate: () => handle.terminate(),
|
|
26635
|
+
completed: handle.done.then((outcome) => ({ exitCode: typeof outcome?.exitCode === "number" ? outcome.exitCode : null })).catch(() => ({ exitCode: null }))
|
|
26636
|
+
};
|
|
26637
|
+
};
|
|
26638
|
+
}
|
|
26639
|
+
function shellInfo(shell) {
|
|
26640
|
+
return { path: shell.path, args: [...shell.args], name: shell.name };
|
|
26641
|
+
}
|
|
26642
|
+
function terminalInfo(terminal) {
|
|
26643
|
+
return {
|
|
26644
|
+
id: terminal.id,
|
|
26645
|
+
title: terminal.title,
|
|
26646
|
+
shell: shellInfo(terminal.shell),
|
|
26647
|
+
cwd: terminal.cwd,
|
|
26648
|
+
cols: terminal.cols,
|
|
26649
|
+
rows: terminal.rows,
|
|
26650
|
+
state: terminal.state,
|
|
26651
|
+
exitCode: terminal.exitCode,
|
|
26652
|
+
...terminal.error === void 0 ? {} : { error: terminal.error },
|
|
26653
|
+
...terminal.controllerId === void 0 ? {} : { controllerId: terminal.controllerId }
|
|
26654
|
+
};
|
|
26655
|
+
}
|
|
26656
|
+
function screenOf(terminal) {
|
|
26657
|
+
return (terminal.truncated ? "\x1Bc" : "") + terminal.screen.join("");
|
|
26658
|
+
}
|
|
26659
|
+
function argsOf(payload) {
|
|
26660
|
+
const value = isRecord13(payload) ? payload : {};
|
|
26661
|
+
return isRecord13(value.args) ? value.args : value;
|
|
26662
|
+
}
|
|
26663
|
+
function isRecord13(value) {
|
|
26664
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
26665
|
+
}
|
|
26666
|
+
function stringId(value) {
|
|
26667
|
+
if (typeof value !== "string" || !/^[A-Za-z0-9_.:-]{1,128}$/.test(value)) throw new RpcError("INVALID_MESSAGE", "The terminal identifier is invalid.");
|
|
26668
|
+
return value;
|
|
26669
|
+
}
|
|
26670
|
+
function bounded(value, fallback, max) {
|
|
26671
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0 ? Math.min(value, max) : fallback;
|
|
26672
|
+
}
|
|
26673
|
+
function readOffset(args) {
|
|
26674
|
+
const range = isRecord13(args.range) ? args.range : args;
|
|
26675
|
+
return typeof range.offset === "number" && Number.isInteger(range.offset) && range.offset >= 0 ? range.offset : 0;
|
|
26676
|
+
}
|
|
26677
|
+
function readLimit(args) {
|
|
26678
|
+
const range = isRecord13(args.range) ? args.range : args;
|
|
26679
|
+
return typeof range.limit === "number" && Number.isInteger(range.limit) && range.limit > 0 ? Math.min(range.limit, MAX_READ_BYTES) : MAX_READ_BYTES;
|
|
26680
|
+
}
|
|
26681
|
+
|
|
26682
|
+
// src/service.ts
|
|
26155
26683
|
var HostPluginRuntime = class {
|
|
26156
|
-
constructor(config, identities, apiProxy, logger, localGateway, fileViewerHost) {
|
|
26684
|
+
constructor(config, identities, apiProxy, logger, localGateway, fileViewerHost, terminalSpawner) {
|
|
26157
26685
|
this.config = config;
|
|
26158
26686
|
this.identities = identities;
|
|
26159
26687
|
this.apiProxy = apiProxy;
|
|
26160
26688
|
this.logger = logger;
|
|
26161
26689
|
this.localGateway = localGateway;
|
|
26162
26690
|
this.fileViewerHost = fileViewerHost;
|
|
26691
|
+
this.terminalSpawner = terminalSpawner;
|
|
26163
26692
|
this.terminalEnabled = config.terminal.enabled;
|
|
26164
26693
|
this.loopback = new LoopbackHost(config.loopback.ports);
|
|
26165
26694
|
this.codex = new CodexRemoteDomain(config.codex, logger);
|
|
@@ -26177,7 +26706,13 @@ var HostPluginRuntime = class {
|
|
|
26177
26706
|
(event, data2) => send(createEvent(event, data2)),
|
|
26178
26707
|
this.logger,
|
|
26179
26708
|
this.harnessVersion,
|
|
26180
|
-
new TerminalPolicy(() => this.terminalEnabled, context.peerDeviceId, this.terminalOwners)
|
|
26709
|
+
new TerminalPolicy(() => this.terminalEnabled, context.peerDeviceId, this.terminalOwners),
|
|
26710
|
+
new CodexWorkspaceBridge(
|
|
26711
|
+
(threadId, signal) => this.codex.resolveThreadWorkspace(context.connectionId, threadId),
|
|
26712
|
+
() => this.terminalEnabled,
|
|
26713
|
+
this.codexWorkspaceState,
|
|
26714
|
+
this.terminalSpawner
|
|
26715
|
+
)
|
|
26181
26716
|
) : void 0;
|
|
26182
26717
|
const fileViewer = new RemoteFileViewerBridge(
|
|
26183
26718
|
() => this.fileViewerHost?.(),
|
|
@@ -26215,6 +26750,7 @@ var HostPluginRuntime = class {
|
|
|
26215
26750
|
harnessVersion;
|
|
26216
26751
|
closed = false;
|
|
26217
26752
|
codex;
|
|
26753
|
+
codexWorkspaceState = new CodexWorkspaceState();
|
|
26218
26754
|
localCodexPeer;
|
|
26219
26755
|
localCodexPublish = async () => void 0;
|
|
26220
26756
|
setTerminalEnabled(enabled) {
|
|
@@ -26498,9 +27034,9 @@ function isPlainRecord(value) {
|
|
|
26498
27034
|
}
|
|
26499
27035
|
|
|
26500
27036
|
// src/cli.ts
|
|
26501
|
-
import { stat as
|
|
27037
|
+
import { stat as stat6 } from "node:fs/promises";
|
|
26502
27038
|
import { hostname as hostname3 } from "node:os";
|
|
26503
|
-
import { join as
|
|
27039
|
+
import { join as join7 } from "node:path";
|
|
26504
27040
|
var QR_POLL_INTERVAL_MS = 2e3;
|
|
26505
27041
|
var TERMINAL_QR_MARGIN = 4;
|
|
26506
27042
|
async function runCli(args = process.argv.slice(2), dependencies = {}) {
|
|
@@ -26577,7 +27113,7 @@ async function status(args, runtime) {
|
|
|
26577
27113
|
`Server: ${serverUrl}`,
|
|
26578
27114
|
"Host control: enabled (dsh-TUI default)"
|
|
26579
27115
|
];
|
|
26580
|
-
if (!await exists3(
|
|
27116
|
+
if (!await exists3(join7(directory, "device.json"))) {
|
|
26581
27117
|
lines.push("Device: not initialized", "Authorization: logged out", "Credential: unavailable");
|
|
26582
27118
|
write(runtime.stdout, `${lines.join("\n")}
|
|
26583
27119
|
|
|
@@ -26621,7 +27157,7 @@ async function logout(args, runtime) {
|
|
|
26621
27157
|
const serverUrl = selectedServer();
|
|
26622
27158
|
const root = new IdentityStore({ env: runtime.env }).directory;
|
|
26623
27159
|
const directory = serverStorageDirectory(root, serverUrl, "host");
|
|
26624
|
-
if (!await exists3(
|
|
27160
|
+
if (!await exists3(join7(directory, "device.json"))) {
|
|
26625
27161
|
await new ServerCredentialStore(directory).clear();
|
|
26626
27162
|
write(runtime.stdout, "This Host is already logged out.\n");
|
|
26627
27163
|
return 0;
|
|
@@ -26663,7 +27199,7 @@ function resolveDependencies(input2) {
|
|
|
26663
27199
|
stdout: input2.stdout ?? process.stdout,
|
|
26664
27200
|
stderr: input2.stderr ?? process.stderr,
|
|
26665
27201
|
now: input2.now ?? Date.now,
|
|
26666
|
-
wait: input2.wait ?? ((milliseconds) => new Promise((
|
|
27202
|
+
wait: input2.wait ?? ((milliseconds) => new Promise((resolve4) => setTimeout(resolve4, milliseconds))),
|
|
26667
27203
|
renderQr: input2.renderQr ?? renderTerminalQr,
|
|
26668
27204
|
createIdentityStore: input2.createIdentityStore ?? ((options) => new IdentityStore(options)),
|
|
26669
27205
|
createHostApi: input2.createHostApi ?? ((serverUrl, store) => new HostServerApi(serverUrl, store))
|
|
@@ -26749,7 +27285,7 @@ function terminalLink(url, target2) {
|
|
|
26749
27285
|
}
|
|
26750
27286
|
async function exists3(path) {
|
|
26751
27287
|
try {
|
|
26752
|
-
await
|
|
27288
|
+
await stat6(path);
|
|
26753
27289
|
return true;
|
|
26754
27290
|
} catch (error) {
|
|
26755
27291
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") return false;
|
|
@@ -27108,7 +27644,7 @@ function errorCode4(error) {
|
|
|
27108
27644
|
return "CONNECTION_FAILED";
|
|
27109
27645
|
}
|
|
27110
27646
|
function wait(milliseconds) {
|
|
27111
|
-
return new Promise((
|
|
27647
|
+
return new Promise((resolve4) => setTimeout(resolve4, milliseconds));
|
|
27112
27648
|
}
|
|
27113
27649
|
|
|
27114
27650
|
// src/remote-file-content-provider.ts
|
|
@@ -27277,13 +27813,15 @@ async function activate(ctx, input2, tuiBinding) {
|
|
|
27277
27813
|
const apiProxy = ctx.get("apiProxy");
|
|
27278
27814
|
const nativeTypertGateway = ctx.get("typertGateway");
|
|
27279
27815
|
const localTypertGateway = new TypertGatewaySwitch(nativeTypertGateway).local();
|
|
27816
|
+
const subprocess = ctx.get("subprocess", false);
|
|
27280
27817
|
const runtime = new HostPluginRuntime(
|
|
27281
27818
|
config,
|
|
27282
27819
|
hostIdentities,
|
|
27283
27820
|
apiProxy,
|
|
27284
27821
|
logger,
|
|
27285
27822
|
localTypertGateway,
|
|
27286
|
-
() => ctx.get("fileViewerHost")
|
|
27823
|
+
() => ctx.get("fileViewerHost"),
|
|
27824
|
+
subprocess === void 0 ? void 0 : subprocessTerminalSpawner(subprocess)
|
|
27287
27825
|
);
|
|
27288
27826
|
let clientRuntime;
|
|
27289
27827
|
const hostControl = runtime;
|