@blue-soda/dsh-remote 0.5.7 → 0.5.9
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 +17 -4
- package/dist/index.js +1051 -431
- package/dist/index.js.map +4 -4
- package/dsh-plugin.json +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,17 +4,10 @@ var __export = (target2, all) => {
|
|
|
4
4
|
__defProp(target2, name2, { get: all[name2], enumerable: true });
|
|
5
5
|
};
|
|
6
6
|
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
if (socket.readyState !== 1 || Date.now() - started > 1e4)
|
|
12
|
-
throw new Error("Relay consumer is too slow or disconnected");
|
|
13
|
-
await new Promise((resolve4) => setTimeout(resolve4, 10));
|
|
14
|
-
}
|
|
15
|
-
if (socket.readyState !== 1)
|
|
16
|
-
throw new Error("Relay transport closed");
|
|
17
|
-
}
|
|
7
|
+
// src/client-runtime.ts
|
|
8
|
+
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
9
|
+
import { homedir as homedir2 } from "node:os";
|
|
10
|
+
import { join as join6 } from "node:path";
|
|
18
11
|
|
|
19
12
|
// ../../node_modules/zod/v3/external.js
|
|
20
13
|
var external_exports = {};
|
|
@@ -177,8 +170,8 @@ var util;
|
|
|
177
170
|
return void 0;
|
|
178
171
|
};
|
|
179
172
|
util2.isInteger = typeof Number.isInteger === "function" ? (val) => Number.isInteger(val) : (val) => typeof val === "number" && Number.isFinite(val) && Math.floor(val) === val;
|
|
180
|
-
function joinValues(
|
|
181
|
-
return
|
|
173
|
+
function joinValues(array2, separator = " | ") {
|
|
174
|
+
return array2.map((val) => typeof val === "string" ? `'${val}'` : val).join(separator);
|
|
182
175
|
}
|
|
183
176
|
util2.joinValues = joinValues;
|
|
184
177
|
util2.jsonStringifyReplacer = (_, value) => {
|
|
@@ -4594,6 +4587,32 @@ function cryptoRandomId() {
|
|
|
4594
4587
|
return `msg_${Date.now().toString(36)}_${Math.random().toString(36).slice(2)}`;
|
|
4595
4588
|
}
|
|
4596
4589
|
|
|
4590
|
+
// src/history-policy.ts
|
|
4591
|
+
var HISTORY_PAGE_SIZES = [50, 30, 20, 12, 6, 3, 1];
|
|
4592
|
+
var RESPONSE_ENVELOPE_BYTES = 8 * 1024;
|
|
4593
|
+
var HISTORY_BUDGET_BYTES = MAX_SECURE_MESSAGE_BYTES - RESPONSE_ENVELOPE_BYTES;
|
|
4594
|
+
function historyPageSizes(start = HISTORY_PAGE_SIZES[0]) {
|
|
4595
|
+
const sizes = [start];
|
|
4596
|
+
for (const size of HISTORY_PAGE_SIZES) if (size < start) sizes.push(size);
|
|
4597
|
+
return sizes;
|
|
4598
|
+
}
|
|
4599
|
+
function codexHistoryStatsFileEnabled(env = process.env) {
|
|
4600
|
+
const value = env.DSH_CODEX_HISTORY_STATS?.trim().toLowerCase();
|
|
4601
|
+
return value === "1" || value === "true" || value === "on" || value === "yes";
|
|
4602
|
+
}
|
|
4603
|
+
|
|
4604
|
+
// ../webrtc/dist/websocket-backpressure.js
|
|
4605
|
+
async function waitForRelayCapacity(socket) {
|
|
4606
|
+
const started = Date.now();
|
|
4607
|
+
while ((socket.bufferedAmount ?? 0) > 512 * 1024) {
|
|
4608
|
+
if (socket.readyState !== 1 || Date.now() - started > 1e4)
|
|
4609
|
+
throw new Error("Relay consumer is too slow or disconnected");
|
|
4610
|
+
await new Promise((resolve5) => setTimeout(resolve5, 10));
|
|
4611
|
+
}
|
|
4612
|
+
if (socket.readyState !== 1)
|
|
4613
|
+
throw new Error("Relay transport closed");
|
|
4614
|
+
}
|
|
4615
|
+
|
|
4597
4616
|
// ../webrtc/dist/transport.js
|
|
4598
4617
|
var BaseTransport = class {
|
|
4599
4618
|
handlers = /* @__PURE__ */ new Set();
|
|
@@ -5066,8 +5085,8 @@ var RtcDataChannelTransport = class {
|
|
|
5066
5085
|
if (this.connectPromise !== void 0)
|
|
5067
5086
|
return this.connectPromise;
|
|
5068
5087
|
this.armAbort(signal);
|
|
5069
|
-
this.connectPromise = new Promise((
|
|
5070
|
-
this.openResolve =
|
|
5088
|
+
this.connectPromise = new Promise((resolve5, reject) => {
|
|
5089
|
+
this.openResolve = resolve5;
|
|
5071
5090
|
this.openReject = reject;
|
|
5072
5091
|
this.negotiateTimer = setTimeout(() => {
|
|
5073
5092
|
void this.failOpenAfterStats(new RtcConnectError("RTC_CONNECT_TIMEOUT", `WebRTC negotiation timed out after ${this.negotiateTimeoutMs}ms.`));
|
|
@@ -5282,9 +5301,9 @@ var RtcDataChannelTransport = class {
|
|
|
5282
5301
|
if (this.closed || this.opened)
|
|
5283
5302
|
return;
|
|
5284
5303
|
this.opened = true;
|
|
5285
|
-
const
|
|
5304
|
+
const resolve5 = this.openResolve;
|
|
5286
5305
|
this.clearNegotiation();
|
|
5287
|
-
void this.resolveSelectedTransport().then(() =>
|
|
5306
|
+
void this.resolveSelectedTransport().then(() => resolve5?.());
|
|
5288
5307
|
};
|
|
5289
5308
|
channel.onmessage = (event) => {
|
|
5290
5309
|
if (this.closed || !this.opened)
|
|
@@ -5678,7 +5697,7 @@ function asError(error) {
|
|
|
5678
5697
|
return error instanceof Error ? error : new RtcConnectError("RTC_FAILED", "WebRTC negotiation failed.");
|
|
5679
5698
|
}
|
|
5680
5699
|
function sleep(ms) {
|
|
5681
|
-
return new Promise((
|
|
5700
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
5682
5701
|
}
|
|
5683
5702
|
|
|
5684
5703
|
// ../webrtc/dist/adaptive-transport.js
|
|
@@ -5719,8 +5738,8 @@ var AdaptiveTransport = class extends BaseTransport {
|
|
|
5719
5738
|
this.socket = new WebSocket(this.url);
|
|
5720
5739
|
this.controlFrameLimits = {};
|
|
5721
5740
|
this.socket.binaryType = "arraybuffer";
|
|
5722
|
-
await new Promise((
|
|
5723
|
-
this.readyResolve =
|
|
5741
|
+
await new Promise((resolve5, reject) => {
|
|
5742
|
+
this.readyResolve = resolve5;
|
|
5724
5743
|
this.readyReject = reject;
|
|
5725
5744
|
this.handshakeTimer = setTimeout(() => this.failConnection(new Error("Adaptive control handshake timed out")), this.options.handshakeTimeoutMs ?? 15e3);
|
|
5726
5745
|
const socket = this.socket;
|
|
@@ -6242,7 +6261,7 @@ function hexToBytes(hex) {
|
|
|
6242
6261
|
const al = hl / 2;
|
|
6243
6262
|
if (hl % 2)
|
|
6244
6263
|
throw new Error("hex string expected, got unpadded hex of length " + hl);
|
|
6245
|
-
const
|
|
6264
|
+
const array2 = new Uint8Array(al);
|
|
6246
6265
|
for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
|
|
6247
6266
|
const n1 = asciiToBase16(hex.charCodeAt(hi));
|
|
6248
6267
|
const n2 = asciiToBase16(hex.charCodeAt(hi + 1));
|
|
@@ -6250,9 +6269,9 @@ function hexToBytes(hex) {
|
|
|
6250
6269
|
const char = hex[hi] + hex[hi + 1];
|
|
6251
6270
|
throw new Error('hex string expected, got non-hex character "' + char + '" at index ' + hi);
|
|
6252
6271
|
}
|
|
6253
|
-
|
|
6272
|
+
array2[ai] = n1 * 16 + n2;
|
|
6254
6273
|
}
|
|
6255
|
-
return
|
|
6274
|
+
return array2;
|
|
6256
6275
|
}
|
|
6257
6276
|
function utf8ToBytes(str) {
|
|
6258
6277
|
if (typeof str !== "string")
|
|
@@ -8959,7 +8978,7 @@ function hexToBytes2(hex) {
|
|
|
8959
8978
|
const al = hl / 2;
|
|
8960
8979
|
if (hl % 2)
|
|
8961
8980
|
throw new RangeError("hex string expected, got unpadded hex of length " + hl);
|
|
8962
|
-
const
|
|
8981
|
+
const array2 = new Uint8Array(al);
|
|
8963
8982
|
for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
|
|
8964
8983
|
const n1 = asciiToBase162(hex.charCodeAt(hi));
|
|
8965
8984
|
const n2 = asciiToBase162(hex.charCodeAt(hi + 1));
|
|
@@ -8967,9 +8986,9 @@ function hexToBytes2(hex) {
|
|
|
8967
8986
|
const char = hex[hi] + hex[hi + 1];
|
|
8968
8987
|
throw new RangeError('hex string expected, got non-hex character "' + char + '" at index ' + hi);
|
|
8969
8988
|
}
|
|
8970
|
-
|
|
8989
|
+
array2[ai] = n1 * 16 + n2;
|
|
8971
8990
|
}
|
|
8972
|
-
return
|
|
8991
|
+
return array2;
|
|
8973
8992
|
}
|
|
8974
8993
|
function concatBytes2(...arrays) {
|
|
8975
8994
|
let sum = 0;
|
|
@@ -13213,8 +13232,8 @@ function errorCode(error) {
|
|
|
13213
13232
|
return typeof code === "string" ? code : void 0;
|
|
13214
13233
|
}
|
|
13215
13234
|
function sleep2(ms) {
|
|
13216
|
-
return new Promise((
|
|
13217
|
-
setTimeout(
|
|
13235
|
+
return new Promise((resolve5) => {
|
|
13236
|
+
setTimeout(resolve5, ms);
|
|
13218
13237
|
});
|
|
13219
13238
|
}
|
|
13220
13239
|
|
|
@@ -13279,7 +13298,7 @@ var ServerCredentialStore = class {
|
|
|
13279
13298
|
if (!(error instanceof Error) || !("code" in error) || error.code !== "EEXIST") throw error;
|
|
13280
13299
|
if (await reclaimAbandonedRefreshLock(lock)) continue;
|
|
13281
13300
|
if (Date.now() >= deadline) throw new ServerCredentialsBusyError();
|
|
13282
|
-
await new Promise((
|
|
13301
|
+
await new Promise((resolve5) => setTimeout(resolve5, 50));
|
|
13283
13302
|
}
|
|
13284
13303
|
}
|
|
13285
13304
|
try {
|
|
@@ -13418,7 +13437,8 @@ var entryConfigSchema = s.object({
|
|
|
13418
13437
|
]),
|
|
13419
13438
|
codex: s.object({
|
|
13420
13439
|
enabled: s.boolean(),
|
|
13421
|
-
binary: s.string()
|
|
13440
|
+
binary: s.string(),
|
|
13441
|
+
showUnprojectedSessions: s.boolean()
|
|
13422
13442
|
}),
|
|
13423
13443
|
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() })) })
|
|
13424
13444
|
});
|
|
@@ -13451,7 +13471,8 @@ var configSchema = external_exports.object({
|
|
|
13451
13471
|
reconnect: reconnectSchema.optional(),
|
|
13452
13472
|
codex: external_exports.object({
|
|
13453
13473
|
enabled: external_exports.boolean().optional(),
|
|
13454
|
-
binary: external_exports.string().trim().min(1).max(4096).optional()
|
|
13474
|
+
binary: external_exports.string().trim().min(1).max(4096).optional(),
|
|
13475
|
+
showUnprojectedSessions: external_exports.boolean().optional()
|
|
13455
13476
|
}).strict().optional(),
|
|
13456
13477
|
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()
|
|
13457
13478
|
}).strict();
|
|
@@ -13493,7 +13514,8 @@ function resolveConfig(input2 = {}, env = process.env) {
|
|
|
13493
13514
|
// an account and, while unavailable, retries every minute. The bundled profile patch says the same thing, so
|
|
13494
13515
|
// an install that never touches the switch has it off.
|
|
13495
13516
|
enabled: parsed.codex?.enabled ?? false,
|
|
13496
|
-
binary: parsed.codex?.binary ?? "codex"
|
|
13517
|
+
binary: parsed.codex?.binary ?? "codex",
|
|
13518
|
+
showUnprojectedSessions: parsed.codex?.showUnprojectedSessions ?? true
|
|
13497
13519
|
},
|
|
13498
13520
|
acp: { enabled: parsed.acp?.enabled ?? true, backends: [.../* @__PURE__ */ new Set(["codex", "cursor", "kimi", ...parsed.acp?.backends?.map((item) => item.id) ?? []])].map((id4) => {
|
|
13499
13521
|
const d = parsed.acp?.backends?.find((x) => x.id === id4);
|
|
@@ -13521,7 +13543,7 @@ function normalizeServerUrl(value) {
|
|
|
13521
13543
|
}
|
|
13522
13544
|
|
|
13523
13545
|
// src/version.ts
|
|
13524
|
-
var PLUGIN_VERSION = "0.5.
|
|
13546
|
+
var PLUGIN_VERSION = "0.5.9";
|
|
13525
13547
|
|
|
13526
13548
|
// src/server-api.ts
|
|
13527
13549
|
var ENABLED_QR_PROVIDERS = ["github"];
|
|
@@ -14144,7 +14166,7 @@ var HostServerConnection = class {
|
|
|
14144
14166
|
this.controlFrameLimits = {};
|
|
14145
14167
|
let acknowledged = false;
|
|
14146
14168
|
let messageQueue = Promise.resolve();
|
|
14147
|
-
await new Promise((
|
|
14169
|
+
await new Promise((resolve5, reject) => {
|
|
14148
14170
|
let settled = false;
|
|
14149
14171
|
const helloTimer = setTimeout(() => socket.close(4001, "hello timeout"), 1e4);
|
|
14150
14172
|
const finish = (error) => {
|
|
@@ -14153,7 +14175,7 @@ var HostServerConnection = class {
|
|
|
14153
14175
|
clearTimeout(helloTimer);
|
|
14154
14176
|
this.online = false;
|
|
14155
14177
|
if (this.socket === socket) this.socket = void 0;
|
|
14156
|
-
void this.dropTunnels().finally(() => error === void 0 ?
|
|
14178
|
+
void this.dropTunnels().finally(() => error === void 0 ? resolve5() : reject(error));
|
|
14157
14179
|
};
|
|
14158
14180
|
socket.onopen = () => {
|
|
14159
14181
|
this.sendControl("hello", {
|
|
@@ -14713,14 +14735,14 @@ var HostServerConnection = class {
|
|
|
14713
14735
|
waitBeforeRetry(baseDelay, exact = false) {
|
|
14714
14736
|
const spread = exact ? 0 : baseDelay * this.config.reconnect.jitter;
|
|
14715
14737
|
const delay = Math.max(0, Math.round(baseDelay - spread + Math.random() * spread * 2));
|
|
14716
|
-
return new Promise((
|
|
14738
|
+
return new Promise((resolve5) => {
|
|
14717
14739
|
const timer = setTimeout(() => {
|
|
14718
14740
|
this.retryWake = void 0;
|
|
14719
|
-
|
|
14741
|
+
resolve5();
|
|
14720
14742
|
}, delay);
|
|
14721
14743
|
this.retryWake = () => {
|
|
14722
14744
|
clearTimeout(timer);
|
|
14723
|
-
|
|
14745
|
+
resolve5();
|
|
14724
14746
|
};
|
|
14725
14747
|
});
|
|
14726
14748
|
}
|
|
@@ -14731,8 +14753,8 @@ async function drainWithin(promise, ms) {
|
|
|
14731
14753
|
try {
|
|
14732
14754
|
await Promise.race([
|
|
14733
14755
|
promise.catch(() => void 0),
|
|
14734
|
-
new Promise((
|
|
14735
|
-
timer = setTimeout(
|
|
14756
|
+
new Promise((resolve5) => {
|
|
14757
|
+
timer = setTimeout(resolve5, ms);
|
|
14736
14758
|
timer.unref?.();
|
|
14737
14759
|
})
|
|
14738
14760
|
]);
|
|
@@ -15086,7 +15108,7 @@ var LoopbackHost = class {
|
|
|
15086
15108
|
const response = handle.response;
|
|
15087
15109
|
let chunk = response.read(Math.min(response.readableLength || LOOPBACK_CHUNK_BYTES, LOOPBACK_CHUNK_BYTES));
|
|
15088
15110
|
if (chunk === null && !response.readableEnded && !response.destroyed) {
|
|
15089
|
-
await new Promise((
|
|
15111
|
+
await new Promise((resolve5, reject) => {
|
|
15090
15112
|
const timer = setTimeout(() => finish(new Error("timeout")), 2e4);
|
|
15091
15113
|
const ready = () => finish();
|
|
15092
15114
|
const fail4 = () => finish(new Error("closed"));
|
|
@@ -15096,7 +15118,7 @@ var LoopbackHost = class {
|
|
|
15096
15118
|
response.off("end", ready);
|
|
15097
15119
|
response.off("close", ready);
|
|
15098
15120
|
response.off("error", fail4);
|
|
15099
|
-
error === void 0 ?
|
|
15121
|
+
error === void 0 ? resolve5() : reject(error);
|
|
15100
15122
|
};
|
|
15101
15123
|
response.once("readable", ready);
|
|
15102
15124
|
response.once("end", ready);
|
|
@@ -15127,22 +15149,22 @@ var LoopbackHost = class {
|
|
|
15127
15149
|
this.close(value.id);
|
|
15128
15150
|
throw new RpcError("RATE_LIMITED", "Preview WebSocket buffer exceeded its limit.");
|
|
15129
15151
|
}
|
|
15130
|
-
await new Promise((
|
|
15152
|
+
await new Promise((resolve5, reject) => handle.socket.send(data2, { binary: value.binary }, (error) => error ? reject(new RpcError("LOOPBACK_CLOSED", "Preview socket closed.")) : resolve5()));
|
|
15131
15153
|
return { sent: true };
|
|
15132
15154
|
}
|
|
15133
15155
|
if (value.op === "ws.read") {
|
|
15134
15156
|
if (handle.reading) throw new RpcError("REQUEST_CONFLICT", "Only one preview read may be pending.");
|
|
15135
15157
|
handle.reading = true;
|
|
15136
15158
|
try {
|
|
15137
|
-
if (handle.messages.length === 0 && !handle.closed) await new Promise((
|
|
15159
|
+
if (handle.messages.length === 0 && !handle.closed) await new Promise((resolve5) => {
|
|
15138
15160
|
const timer = setTimeout(() => {
|
|
15139
15161
|
handle.wake = void 0;
|
|
15140
|
-
|
|
15162
|
+
resolve5();
|
|
15141
15163
|
}, 2e4);
|
|
15142
15164
|
handle.wake = () => {
|
|
15143
15165
|
clearTimeout(timer);
|
|
15144
15166
|
handle.wake = void 0;
|
|
15145
|
-
|
|
15167
|
+
resolve5();
|
|
15146
15168
|
};
|
|
15147
15169
|
});
|
|
15148
15170
|
let bytes = 0;
|
|
@@ -15189,7 +15211,7 @@ var LoopbackHost = class {
|
|
|
15189
15211
|
openHttp(value) {
|
|
15190
15212
|
const body = value.body === void 0 ? void 0 : Buffer.from(value.body, "base64");
|
|
15191
15213
|
if ((body?.length ?? 0) > LOOPBACK_MAX_BODY_BYTES) throw new Error("body limit");
|
|
15192
|
-
return new Promise((
|
|
15214
|
+
return new Promise((resolve5, reject) => {
|
|
15193
15215
|
const headers = Object.fromEntries(cleanHeaders(value.headers));
|
|
15194
15216
|
let handle;
|
|
15195
15217
|
const request = httpRequest({
|
|
@@ -15208,7 +15230,7 @@ var LoopbackHost = class {
|
|
|
15208
15230
|
handle.response = response;
|
|
15209
15231
|
response.on("error", () => void 0);
|
|
15210
15232
|
clearTimeout(timer);
|
|
15211
|
-
|
|
15233
|
+
resolve5({ status: response.statusCode ?? 502, headers: cleanHeaders(headerPairs(response.headers)) });
|
|
15212
15234
|
});
|
|
15213
15235
|
const timer = setTimeout(() => {
|
|
15214
15236
|
request.destroy();
|
|
@@ -15224,7 +15246,7 @@ var LoopbackHost = class {
|
|
|
15224
15246
|
});
|
|
15225
15247
|
}
|
|
15226
15248
|
openWs(value) {
|
|
15227
|
-
return new Promise((
|
|
15249
|
+
return new Promise((resolve5, reject) => {
|
|
15228
15250
|
const socket = new WebSocket2(`ws://127.0.0.1:${value.port}${value.path}`, value.protocols, {
|
|
15229
15251
|
headers: Object.fromEntries(cleanHeaders(value.headers)),
|
|
15230
15252
|
followRedirects: false,
|
|
@@ -15234,7 +15256,7 @@ var LoopbackHost = class {
|
|
|
15234
15256
|
});
|
|
15235
15257
|
const handle = { kind: "ws", port: value.port, socket, messages: [], bytes: 0, reading: false, closed: false, touched: Date.now() };
|
|
15236
15258
|
this.handles.set(value.id, handle);
|
|
15237
|
-
socket.once("open", () =>
|
|
15259
|
+
socket.once("open", () => resolve5({ protocol: socket.protocol }));
|
|
15238
15260
|
socket.on("message", (data2, binary) => {
|
|
15239
15261
|
if (handle.closed) return;
|
|
15240
15262
|
const message = { data: Buffer.from(data2).toString("base64"), binary };
|
|
@@ -15293,7 +15315,7 @@ var LoopbackPreview = class {
|
|
|
15293
15315
|
for (const client of running.ws.clients) client.terminate();
|
|
15294
15316
|
running.ws.close();
|
|
15295
15317
|
running.server.closeAllConnections();
|
|
15296
|
-
await new Promise((
|
|
15318
|
+
await new Promise((resolve5) => running.server.close(() => resolve5()));
|
|
15297
15319
|
}
|
|
15298
15320
|
this.servers.clear();
|
|
15299
15321
|
}
|
|
@@ -15348,11 +15370,11 @@ var LoopbackPreview = class {
|
|
|
15348
15370
|
void this.release(id4);
|
|
15349
15371
|
});
|
|
15350
15372
|
});
|
|
15351
|
-
await new Promise((
|
|
15373
|
+
await new Promise((resolve5, reject) => {
|
|
15352
15374
|
server.once("error", reject);
|
|
15353
15375
|
server.listen(0, "127.0.0.1", () => {
|
|
15354
15376
|
server.off("error", reject);
|
|
15355
|
-
|
|
15377
|
+
resolve5();
|
|
15356
15378
|
});
|
|
15357
15379
|
});
|
|
15358
15380
|
const address = server.address();
|
|
@@ -15418,10 +15440,10 @@ var LoopbackPreview = class {
|
|
|
15418
15440
|
while (!signal.aborted) {
|
|
15419
15441
|
const chunk = await this.client.rpc("loopback.call", { op: "http.read", id: id4 }, signal);
|
|
15420
15442
|
if (chunk.data !== "" && !res.write(Buffer.from(chunk.data, "base64"))) {
|
|
15421
|
-
await new Promise((
|
|
15443
|
+
await new Promise((resolve5, reject) => {
|
|
15422
15444
|
const ready = () => {
|
|
15423
15445
|
cleanup();
|
|
15424
|
-
|
|
15446
|
+
resolve5();
|
|
15425
15447
|
};
|
|
15426
15448
|
const closed = () => {
|
|
15427
15449
|
cleanup();
|
|
@@ -15481,7 +15503,7 @@ var LoopbackPreview = class {
|
|
|
15481
15503
|
const result = await this.client.rpc("loopback.call", { op: "ws.read", id: id4 }, signal);
|
|
15482
15504
|
for (const message of result.messages) {
|
|
15483
15505
|
if (socket.bufferedAmount > 1024 * 1024) throw new Error("slow preview");
|
|
15484
|
-
await new Promise((
|
|
15506
|
+
await new Promise((resolve5, reject) => socket.send(Buffer.from(message.data, "base64"), { binary: message.binary }, (error) => error ? reject(error) : resolve5()));
|
|
15485
15507
|
}
|
|
15486
15508
|
if (result.closed) break;
|
|
15487
15509
|
}
|
|
@@ -16169,13 +16191,13 @@ var RemoteClientCore = class {
|
|
|
16169
16191
|
throw rpcAbortedError(method, signal.reason);
|
|
16170
16192
|
const timeoutMs = callTimeoutMs(this.timeoutMs, options);
|
|
16171
16193
|
const request = createRpcRequest(method, params);
|
|
16172
|
-
const result = new Promise((
|
|
16194
|
+
const result = new Promise((resolve5, reject) => {
|
|
16173
16195
|
const timer = setTimeout(() => {
|
|
16174
16196
|
this.rejectPending(request.id, new RemoteClientError("RPC_TIMEOUT", `RPC ${method} timed out after ${timeoutMs}ms`));
|
|
16175
16197
|
}, timeoutMs);
|
|
16176
16198
|
const pending = {
|
|
16177
16199
|
method,
|
|
16178
|
-
resolve:
|
|
16200
|
+
resolve: resolve5,
|
|
16179
16201
|
reject,
|
|
16180
16202
|
timer
|
|
16181
16203
|
};
|
|
@@ -16484,7 +16506,7 @@ var ClientSecureTransport = class {
|
|
|
16484
16506
|
}
|
|
16485
16507
|
};
|
|
16486
16508
|
async function waitForResponder(inner, noise) {
|
|
16487
|
-
await new Promise((
|
|
16509
|
+
await new Promise((resolve5, reject) => {
|
|
16488
16510
|
let settled = false;
|
|
16489
16511
|
const timer = setTimeout(() => finish(new Error("Noise IK handshake timed out.")), 1e4);
|
|
16490
16512
|
const unsubscribe = inner.onHandshake((step, data2) => {
|
|
@@ -16503,7 +16525,7 @@ async function waitForResponder(inner, noise) {
|
|
|
16503
16525
|
settled = true;
|
|
16504
16526
|
clearTimeout(timer);
|
|
16505
16527
|
unsubscribe();
|
|
16506
|
-
if (error === void 0)
|
|
16528
|
+
if (error === void 0) resolve5();
|
|
16507
16529
|
else reject(error);
|
|
16508
16530
|
};
|
|
16509
16531
|
void inner.sendHandshake(1, noise.writeHandshake()).catch((error) => {
|
|
@@ -17099,7 +17121,7 @@ var AsyncFrameQueue = class {
|
|
|
17099
17121
|
continue;
|
|
17100
17122
|
}
|
|
17101
17123
|
if (this.closed) return;
|
|
17102
|
-
const next = await new Promise((
|
|
17124
|
+
const next = await new Promise((resolve5) => this.waiters.push(resolve5));
|
|
17103
17125
|
if (next.done) return;
|
|
17104
17126
|
yield next.value;
|
|
17105
17127
|
}
|
|
@@ -17504,7 +17526,7 @@ var AsyncValueQueue = class {
|
|
|
17504
17526
|
if (this.error !== void 0) throw this.error;
|
|
17505
17527
|
return;
|
|
17506
17528
|
}
|
|
17507
|
-
const next = await new Promise((
|
|
17529
|
+
const next = await new Promise((resolve5) => this.waiters.push(resolve5));
|
|
17508
17530
|
if (next.done) {
|
|
17509
17531
|
if (this.error !== void 0) throw this.error;
|
|
17510
17532
|
return;
|
|
@@ -17595,6 +17617,11 @@ function patchWelcomeDescribe2(value) {
|
|
|
17595
17617
|
return { ...value, namespaces };
|
|
17596
17618
|
}
|
|
17597
17619
|
|
|
17620
|
+
// src/codex/virtual-harness.ts
|
|
17621
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
17622
|
+
import { homedir } from "node:os";
|
|
17623
|
+
import { join as join4 } from "node:path";
|
|
17624
|
+
|
|
17598
17625
|
// src/codex/permissions.ts
|
|
17599
17626
|
function codexPermissionPresetFromResponse(value) {
|
|
17600
17627
|
const root = record(value);
|
|
@@ -17625,135 +17652,6 @@ function resolveCodexThreadKey(key) {
|
|
|
17625
17652
|
return codexThreadIdFromKey(key) ?? key;
|
|
17626
17653
|
}
|
|
17627
17654
|
|
|
17628
|
-
// src/codex/method-policy.ts
|
|
17629
|
-
var id2 = external_exports.string().min(1).max(256);
|
|
17630
|
-
var cursor = external_exports.string().min(1).max(4096).nullable().optional();
|
|
17631
|
-
var textInput = external_exports.object({
|
|
17632
|
-
type: external_exports.literal("text"),
|
|
17633
|
-
text: external_exports.string().min(1).max(256 * 1024)
|
|
17634
|
-
}).strict();
|
|
17635
|
-
var imageMediaType = external_exports.enum(["image/png", "image/jpeg", "image/webp", "image/gif"]);
|
|
17636
|
-
var canonicalBase64 = external_exports.string().min(4).max(288 * 1024 * 1024).refine((value) => value.length % 4 === 0 && /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u.test(value), {
|
|
17637
|
-
message: "Image data must use canonical base64."
|
|
17638
|
-
});
|
|
17639
|
-
var imageInput = external_exports.object({
|
|
17640
|
-
type: external_exports.literal("image"),
|
|
17641
|
-
mediaType: imageMediaType,
|
|
17642
|
-
data: canonicalBase64
|
|
17643
|
-
}).strict();
|
|
17644
|
-
var input = external_exports.array(external_exports.union([textInput, imageInput])).min(1).max(16);
|
|
17645
|
-
var permissionPreset = external_exports.enum(["workspace-write", "danger-full-access"]);
|
|
17646
|
-
var projectRoot = external_exports.object({
|
|
17647
|
-
path: external_exports.string().min(1).max(4096)
|
|
17648
|
-
}).strict();
|
|
17649
|
-
var CODEX_HISTORY_MAX_MESSAGES = 200;
|
|
17650
|
-
var schemas = {
|
|
17651
|
-
"account/read": external_exports.object({ refreshToken: external_exports.literal(false).optional() }).strict(),
|
|
17652
|
-
"model/list": external_exports.object({
|
|
17653
|
-
cursor,
|
|
17654
|
-
limit: external_exports.number().int().min(1).max(100).optional(),
|
|
17655
|
-
includeHidden: external_exports.boolean().optional()
|
|
17656
|
-
}).strict(),
|
|
17657
|
-
"project/list": external_exports.object({
|
|
17658
|
-
cursor,
|
|
17659
|
-
limit: external_exports.number().int().min(1).max(100).optional()
|
|
17660
|
-
}).strict(),
|
|
17661
|
-
"project/create": external_exports.object({
|
|
17662
|
-
name: external_exports.string().trim().min(1).max(256),
|
|
17663
|
-
roots: external_exports.array(projectRoot).length(1),
|
|
17664
|
-
idempotencyKey: external_exports.string().min(16).max(256)
|
|
17665
|
-
}).strict(),
|
|
17666
|
-
"thread/list": external_exports.object({
|
|
17667
|
-
cursor,
|
|
17668
|
-
limit: external_exports.number().int().min(1).max(100).optional(),
|
|
17669
|
-
sortKey: external_exports.enum(["created_at", "updated_at", "recency_at"]).optional(),
|
|
17670
|
-
sortDirection: external_exports.enum(["asc", "desc"]).optional(),
|
|
17671
|
-
modelProviders: external_exports.array(external_exports.string().min(1).max(128)).max(32).nullable().optional(),
|
|
17672
|
-
sourceKinds: external_exports.array(external_exports.enum(["cli", "vscode", "exec", "appServer", "unknown"])).max(8).optional(),
|
|
17673
|
-
archived: external_exports.boolean().optional(),
|
|
17674
|
-
isPinned: external_exports.boolean().optional(),
|
|
17675
|
-
cwd: external_exports.union([external_exports.string().min(1).max(4096), external_exports.array(external_exports.string().min(1).max(4096)).min(1).max(32)]).optional(),
|
|
17676
|
-
useStateDbOnly: external_exports.boolean().optional(),
|
|
17677
|
-
searchTerm: external_exports.string().max(1024).optional(),
|
|
17678
|
-
// Upstream `ThreadListParams` declares these two as well. Refusing them turned a
|
|
17679
|
-
// legitimate client query into "The CodeX call parameters are invalid.".
|
|
17680
|
-
originators: external_exports.array(external_exports.string().min(1).max(256)).max(32).nullable().optional(),
|
|
17681
|
-
sectionId: external_exports.string().max(256).nullable().optional()
|
|
17682
|
-
}).strict(),
|
|
17683
|
-
"thread/read": external_exports.object({ threadId: id2, includeTurns: external_exports.boolean().optional() }).strict(),
|
|
17684
|
-
"dsh/sessionHistory": external_exports.object({
|
|
17685
|
-
threadId: id2,
|
|
17686
|
-
beforeSeq: external_exports.number().int().nonnegative().optional(),
|
|
17687
|
-
throughSeq: external_exports.number().int().min(-1).optional(),
|
|
17688
|
-
maxMessages: external_exports.number().int().min(1).max(CODEX_HISTORY_MAX_MESSAGES).optional()
|
|
17689
|
-
}).strict(),
|
|
17690
|
-
"dsh/directoryList": external_exports.object({
|
|
17691
|
-
path: external_exports.string().min(1).max(4096)
|
|
17692
|
-
}).strict(),
|
|
17693
|
-
"thread/start": external_exports.object({
|
|
17694
|
-
cwd: external_exports.string().min(1).max(4096),
|
|
17695
|
-
model: external_exports.string().min(1).max(128).optional(),
|
|
17696
|
-
personality: external_exports.string().min(1).max(64).optional(),
|
|
17697
|
-
permissionPreset: permissionPreset.optional()
|
|
17698
|
-
}).strict(),
|
|
17699
|
-
"thread/resume": external_exports.object({
|
|
17700
|
-
threadId: id2,
|
|
17701
|
-
model: external_exports.string().min(1).max(128).optional(),
|
|
17702
|
-
permissionPreset: permissionPreset.optional()
|
|
17703
|
-
}).strict(),
|
|
17704
|
-
"thread/fork": external_exports.object({
|
|
17705
|
-
threadId: id2,
|
|
17706
|
-
lastTurnId: id2.optional(),
|
|
17707
|
-
permissionPreset: permissionPreset.optional()
|
|
17708
|
-
}).strict(),
|
|
17709
|
-
// Upstream declares a plain string and uses the empty value to clear a name, so the
|
|
17710
|
-
// trim-based minimum rejected a legitimate rename.
|
|
17711
|
-
"thread/name/set": external_exports.object({ threadId: id2, name: external_exports.string().max(256) }).strict(),
|
|
17712
|
-
"thread/archive": external_exports.object({ threadId: id2 }).strict(),
|
|
17713
|
-
"thread/unarchive": external_exports.object({ threadId: id2 }).strict(),
|
|
17714
|
-
"thread/unsubscribe": external_exports.object({ threadId: id2 }).strict(),
|
|
17715
|
-
"turn/start": external_exports.object({
|
|
17716
|
-
threadId: id2,
|
|
17717
|
-
input,
|
|
17718
|
-
model: external_exports.string().min(1).max(128).optional(),
|
|
17719
|
-
effort: external_exports.enum(["none", "minimal", "low", "medium", "high", "xhigh", "max", "ultra"]).optional(),
|
|
17720
|
-
summary: external_exports.enum(["auto", "concise", "detailed", "none"]).optional(),
|
|
17721
|
-
personality: external_exports.string().min(1).max(64).optional(),
|
|
17722
|
-
permissionPreset: permissionPreset.optional()
|
|
17723
|
-
}).strict(),
|
|
17724
|
-
"turn/steer": external_exports.object({
|
|
17725
|
-
threadId: id2,
|
|
17726
|
-
input,
|
|
17727
|
-
expectedTurnId: id2,
|
|
17728
|
-
// Declared by upstream `TurnSteerParams`; see the thread/list note above.
|
|
17729
|
-
clientUserMessageId: external_exports.string().max(256).nullable().optional()
|
|
17730
|
-
}).strict(),
|
|
17731
|
-
"turn/interrupt": external_exports.object({ threadId: id2, turnId: id2 }).strict()
|
|
17732
|
-
};
|
|
17733
|
-
var CODEX_APP_ALLOWLIST = Object.freeze(Object.keys(schemas));
|
|
17734
|
-
function parseCodexCall(method, params) {
|
|
17735
|
-
if (!Object.prototype.hasOwnProperty.call(schemas, method)) {
|
|
17736
|
-
throw new RpcError("METHOD_NOT_ALLOWED", "The requested Codex method is not available over Remote.");
|
|
17737
|
-
}
|
|
17738
|
-
const schema = schemas[method];
|
|
17739
|
-
const parsed = schema.safeParse(params);
|
|
17740
|
-
if (!parsed.success) {
|
|
17741
|
-
const detail = parsed.error.issues.slice(0, 4).map((issue) => {
|
|
17742
|
-
const path = issue.path.length === 0 ? "(root)" : issue.path.join(".");
|
|
17743
|
-
const keys = "keys" in issue && Array.isArray(issue.keys) ? ` [${issue.keys.join(", ")}]` : "";
|
|
17744
|
-
return `${path}${keys}: ${issue.code}`;
|
|
17745
|
-
}).join(", ");
|
|
17746
|
-
throw new RpcError("INVALID_MESSAGE", `The CodeX call parameters are invalid. (${method} \u2192 ${detail})`);
|
|
17747
|
-
}
|
|
17748
|
-
return { method, params: parsed.data };
|
|
17749
|
-
}
|
|
17750
|
-
function isThreadMutation(method) {
|
|
17751
|
-
return method === "turn/start" || method === "turn/steer" || method === "turn/interrupt";
|
|
17752
|
-
}
|
|
17753
|
-
function threadIdFromParams(params) {
|
|
17754
|
-
return typeof params.threadId === "string" ? params.threadId : void 0;
|
|
17755
|
-
}
|
|
17756
|
-
|
|
17757
17655
|
// src/codex/codex-image-codec.ts
|
|
17758
17656
|
var CODEX_IMAGE_MEDIA_TYPES2 = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/webp", "image/gif"]);
|
|
17759
17657
|
var CODEX_IMAGE_ATTACHMENT_PREFIX = "codex-image:";
|
|
@@ -17988,9 +17886,10 @@ var CODEX_MODEL = "codex";
|
|
|
17988
17886
|
var CODEX_PAGE_LIMIT = 100;
|
|
17989
17887
|
var MAX_CODEX_PAGES = 32;
|
|
17990
17888
|
var MAX_LIVE_TOOL_OUTPUT = 128 * 1024;
|
|
17991
|
-
var DEFAULT_HISTORY_MESSAGES = 50;
|
|
17992
17889
|
var SESSION_SEARCH_RESULT_LIMIT = 20;
|
|
17993
17890
|
var SESSION_SEARCH_SNIPPET_CODE_POINTS = 240;
|
|
17891
|
+
var HISTORY_PAGE_CACHE_TTL_MS = 6e4;
|
|
17892
|
+
var HISTORY_PAGE_CACHE_LIMIT = 24;
|
|
17994
17893
|
var CODEX_DEFAULT_PERMISSION = "workspace-write";
|
|
17995
17894
|
var MAX_CODEX_PROMPT_PARTS = 16;
|
|
17996
17895
|
var MAX_CODEX_PROMPT_TEXT = 256 * 1024;
|
|
@@ -18037,6 +17936,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
|
|
|
18037
17936
|
this.sessionGeneration = sessionGeneration;
|
|
18038
17937
|
this.hostCarrier = hostCarrier;
|
|
18039
17938
|
this.api = this.createApiProxy();
|
|
17939
|
+
this.installHistoryCacheInspector();
|
|
18040
17940
|
this.stopCapabilityWatch = this.client.watchCapability?.((available) => {
|
|
18041
17941
|
if (!available) this.onSessionLost?.("domain-notice");
|
|
18042
17942
|
});
|
|
@@ -18056,6 +17956,10 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
|
|
|
18056
17956
|
selectedModels = /* @__PURE__ */ new Map();
|
|
18057
17957
|
selectedPermissions = /* @__PURE__ */ new Map();
|
|
18058
17958
|
imageAttachments = /* @__PURE__ */ new Map();
|
|
17959
|
+
historyPageCache = /* @__PURE__ */ new Map();
|
|
17960
|
+
historyPageHits = 0;
|
|
17961
|
+
historyPageMisses = 0;
|
|
17962
|
+
historyCacheInspector;
|
|
18059
17963
|
modelDirectory;
|
|
18060
17964
|
modelDirectoryPromise;
|
|
18061
17965
|
lastProjectionSeq = 0;
|
|
@@ -18171,6 +18075,10 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
|
|
|
18171
18075
|
return business(await this.archiveSession(requestArg(args), signal));
|
|
18172
18076
|
case "session/list":
|
|
18173
18077
|
return business(success({ items: await this.sessionSummaries(signal) }));
|
|
18078
|
+
case "thread/list": {
|
|
18079
|
+
const catalog = await this.refreshCatalog(signal);
|
|
18080
|
+
return business(success({ data: catalog.threads, nextCursor: null }));
|
|
18081
|
+
}
|
|
18174
18082
|
case "session/search":
|
|
18175
18083
|
return business(await this.searchSessions(requestArg(args), signal));
|
|
18176
18084
|
case "session/create":
|
|
@@ -18284,6 +18192,17 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
|
|
|
18284
18192
|
this.selectedModels.clear();
|
|
18285
18193
|
this.selectedPermissions.clear();
|
|
18286
18194
|
this.imageAttachments.clear();
|
|
18195
|
+
this.historyPageCache.clear();
|
|
18196
|
+
const globalScope = globalThis;
|
|
18197
|
+
if (this.historyCacheInspector !== void 0 && globalScope.__dshCodexHistoryCacheStats === this.historyCacheInspector) {
|
|
18198
|
+
delete globalScope.__dshCodexHistoryCacheStats;
|
|
18199
|
+
}
|
|
18200
|
+
const scope = globalThis;
|
|
18201
|
+
if (scope.window !== void 0 && this.historyCacheInspector !== void 0 && scope.window.__dshCodexHistoryCacheStats === this.historyCacheInspector) {
|
|
18202
|
+
delete scope.window.__dshCodexHistoryCacheStats;
|
|
18203
|
+
}
|
|
18204
|
+
scope.document?.documentElement?.removeAttribute?.("data-dsh-codex-history-cache-stats");
|
|
18205
|
+
this.historyCacheInspector = void 0;
|
|
18287
18206
|
}
|
|
18288
18207
|
async refreshCatalog(signal) {
|
|
18289
18208
|
const catalog = await loadCatalog(this.client, signal, this.pendingThreads);
|
|
@@ -18557,6 +18476,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
|
|
|
18557
18476
|
});
|
|
18558
18477
|
}
|
|
18559
18478
|
acceptCodexFrame(follow, frame) {
|
|
18479
|
+
this.invalidateHistoryPages(follow.threadId);
|
|
18560
18480
|
const params = record3(frame.params);
|
|
18561
18481
|
if (frame.method === "thread/name/updated") {
|
|
18562
18482
|
const name2 = string2(params.threadName);
|
|
@@ -19209,29 +19129,27 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
|
|
|
19209
19129
|
return thread;
|
|
19210
19130
|
}
|
|
19211
19131
|
async readHistoryPage(threadId, page, signal) {
|
|
19212
|
-
const maxMessages =
|
|
19213
|
-
|
|
19214
|
-
|
|
19215
|
-
|
|
19216
|
-
|
|
19217
|
-
|
|
19218
|
-
|
|
19219
|
-
|
|
19220
|
-
|
|
19221
|
-
|
|
19222
|
-
|
|
19223
|
-
|
|
19224
|
-
|
|
19225
|
-
|
|
19226
|
-
|
|
19227
|
-
|
|
19228
|
-
|
|
19229
|
-
|
|
19230
|
-
|
|
19231
|
-
|
|
19232
|
-
|
|
19233
|
-
);
|
|
19234
|
-
}
|
|
19132
|
+
const maxMessages = Math.min(
|
|
19133
|
+
HISTORY_PAGE_SIZES[0],
|
|
19134
|
+
Math.max(1, page.maxMessages ?? HISTORY_PAGE_SIZES[0])
|
|
19135
|
+
);
|
|
19136
|
+
const cacheKey = `${this.host.deviceId}|${threadId}|${page.beforeSeq ?? ""}|${page.throughSeq ?? ""}|${maxMessages}`;
|
|
19137
|
+
const cached = this.historyPageCache.get(cacheKey);
|
|
19138
|
+
if (cached !== void 0 && Date.now() - cached.at < HISTORY_PAGE_CACHE_TTL_MS) {
|
|
19139
|
+
const ageMs = Date.now() - cached.at;
|
|
19140
|
+
this.historyPageHits += 1;
|
|
19141
|
+
this.cacheHistoryImages(cached.page);
|
|
19142
|
+
this.publishHistoryCacheStats();
|
|
19143
|
+
this.logHistoryCache("hit", threadId, { ...page, maxMessages }, cached.page.records.length, ageMs);
|
|
19144
|
+
return cached.page;
|
|
19145
|
+
}
|
|
19146
|
+
this.historyPageMisses += 1;
|
|
19147
|
+
let value = record3(await this.client.request("dsh/sessionHistory", {
|
|
19148
|
+
threadId,
|
|
19149
|
+
...page.beforeSeq === void 0 ? {} : { beforeSeq: page.beforeSeq },
|
|
19150
|
+
...page.throughSeq === void 0 ? {} : { throughSeq: page.throughSeq },
|
|
19151
|
+
maxMessages
|
|
19152
|
+
}, signal));
|
|
19235
19153
|
value = adaptCodexHistoryPage(value, this.sessionGeneration);
|
|
19236
19154
|
if (!isCodexNativeHistoryPage(value, `codex:${threadId}`)) {
|
|
19237
19155
|
throw new Error("CodeX Remote returned an invalid paginated History.");
|
|
@@ -19241,8 +19159,115 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
|
|
|
19241
19159
|
this.setPermissionSelection(`codex:${threadId}`, preset === "workspace-write" || preset === "danger-full-access" ? preset : void 0);
|
|
19242
19160
|
}
|
|
19243
19161
|
this.cacheHistoryImages(value);
|
|
19162
|
+
this.historyPageCache.set(cacheKey, { page: value, at: Date.now() });
|
|
19163
|
+
if (this.historyPageCache.size > HISTORY_PAGE_CACHE_LIMIT) {
|
|
19164
|
+
const oldest = this.historyPageCache.keys().next().value;
|
|
19165
|
+
if (oldest !== void 0) this.historyPageCache.delete(oldest);
|
|
19166
|
+
}
|
|
19167
|
+
this.publishHistoryCacheStats();
|
|
19168
|
+
this.logHistoryCache("miss", threadId, { ...page, maxMessages }, value.records.length);
|
|
19244
19169
|
return value;
|
|
19245
19170
|
}
|
|
19171
|
+
/** A live change or a new turn makes the cached page stale. */
|
|
19172
|
+
invalidateHistoryPages(threadId) {
|
|
19173
|
+
const marker = `|${threadId}|`;
|
|
19174
|
+
let removed = false;
|
|
19175
|
+
for (const key of [...this.historyPageCache.keys()]) {
|
|
19176
|
+
if (key.includes(marker)) {
|
|
19177
|
+
this.historyPageCache.delete(key);
|
|
19178
|
+
removed = true;
|
|
19179
|
+
}
|
|
19180
|
+
}
|
|
19181
|
+
if (removed) this.publishHistoryCacheStats();
|
|
19182
|
+
}
|
|
19183
|
+
/**
|
|
19184
|
+
* Expose cache metadata to the current page's DevTools as
|
|
19185
|
+
* `__dshCodexHistoryCacheStats()`. The function is metadata only: it never
|
|
19186
|
+
* returns message text or event data, so the Session content stays private.
|
|
19187
|
+
* It is installed on both the realm global and `window`, and mirrored into a
|
|
19188
|
+
* DOM data attribute so a console in another realm can read it too.
|
|
19189
|
+
*/
|
|
19190
|
+
installHistoryCacheInspector() {
|
|
19191
|
+
const globalScope = globalThis;
|
|
19192
|
+
const scope = globalThis;
|
|
19193
|
+
const inspector = () => this.historyCacheStats();
|
|
19194
|
+
globalScope.__dshCodexHistoryCacheStats = inspector;
|
|
19195
|
+
if (scope.window !== void 0) scope.window.__dshCodexHistoryCacheStats = inspector;
|
|
19196
|
+
this.historyCacheInspector = inspector;
|
|
19197
|
+
this.publishHistoryCacheStats();
|
|
19198
|
+
}
|
|
19199
|
+
publishHistoryCacheStats() {
|
|
19200
|
+
const stats = this.historyCacheStats();
|
|
19201
|
+
this.writeHistoryCacheStatsFile(stats);
|
|
19202
|
+
const scope = globalThis;
|
|
19203
|
+
const root = scope.document?.documentElement;
|
|
19204
|
+
if (root?.setAttribute === void 0) return;
|
|
19205
|
+
try {
|
|
19206
|
+
root.setAttribute("data-dsh-codex-history-cache-stats", JSON.stringify(stats));
|
|
19207
|
+
} catch {
|
|
19208
|
+
}
|
|
19209
|
+
}
|
|
19210
|
+
/**
|
|
19211
|
+
* The Codex virtual harness runs in the DSH client's Node process, not in the
|
|
19212
|
+
* browser page, so DevTools cannot see its globals. Mirror the same metadata to
|
|
19213
|
+
* the client home for inspection without any message content.
|
|
19214
|
+
*/
|
|
19215
|
+
writeHistoryCacheStatsFile(stats) {
|
|
19216
|
+
if (!codexHistoryStatsFileEnabled() || process.env.VITEST !== void 0) return;
|
|
19217
|
+
try {
|
|
19218
|
+
const home = process.env.DSH_HOME ?? join4(homedir(), ".dsh");
|
|
19219
|
+
mkdirSync(home, { recursive: true });
|
|
19220
|
+
writeFileSync(
|
|
19221
|
+
join4(home, "codex-history-cache-stats.json"),
|
|
19222
|
+
JSON.stringify({
|
|
19223
|
+
...stats,
|
|
19224
|
+
pid: process.pid,
|
|
19225
|
+
source: "codex-harness",
|
|
19226
|
+
writtenAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
19227
|
+
}, null, 2)
|
|
19228
|
+
);
|
|
19229
|
+
} catch {
|
|
19230
|
+
}
|
|
19231
|
+
}
|
|
19232
|
+
logHistoryCache(event, threadId, page, records, ageMs) {
|
|
19233
|
+
if (!codexHistoryStatsFileEnabled()) return;
|
|
19234
|
+
const scope = globalThis;
|
|
19235
|
+
scope.console?.info?.("[dsh-remote] codex history page cache " + event, {
|
|
19236
|
+
threadId,
|
|
19237
|
+
beforeSeq: page.beforeSeq ?? null,
|
|
19238
|
+
...page.maxMessages === void 0 ? {} : { maxMessages: page.maxMessages },
|
|
19239
|
+
...records === void 0 ? {} : { records },
|
|
19240
|
+
...ageMs === void 0 ? {} : { ageMs },
|
|
19241
|
+
hits: this.historyPageHits,
|
|
19242
|
+
misses: this.historyPageMisses
|
|
19243
|
+
});
|
|
19244
|
+
}
|
|
19245
|
+
historyCacheStats() {
|
|
19246
|
+
const now = Date.now();
|
|
19247
|
+
return {
|
|
19248
|
+
ttlMs: HISTORY_PAGE_CACHE_TTL_MS,
|
|
19249
|
+
hits: this.historyPageHits,
|
|
19250
|
+
misses: this.historyPageMisses,
|
|
19251
|
+
active: true,
|
|
19252
|
+
entries: [...this.historyPageCache.entries()].map(([key, { page, at }]) => {
|
|
19253
|
+
const records = page.records;
|
|
19254
|
+
const firstSeq = records[0]?.event.seq;
|
|
19255
|
+
const lastSeq = records.at(-1)?.event.seq;
|
|
19256
|
+
return {
|
|
19257
|
+
key,
|
|
19258
|
+
ageMs: now - at,
|
|
19259
|
+
expired: now - at >= HISTORY_PAGE_CACHE_TTL_MS,
|
|
19260
|
+
sessionId: page.header.id,
|
|
19261
|
+
records: records.length,
|
|
19262
|
+
cursor: page.cursor,
|
|
19263
|
+
nextTurn: page.nextTurn,
|
|
19264
|
+
hasMore: page.hasMore,
|
|
19265
|
+
...firstSeq === void 0 ? {} : { firstSeq },
|
|
19266
|
+
...lastSeq === void 0 ? {} : { lastSeq }
|
|
19267
|
+
};
|
|
19268
|
+
})
|
|
19269
|
+
};
|
|
19270
|
+
}
|
|
19246
19271
|
cacheHistoryImages(history) {
|
|
19247
19272
|
const sessionId = history.header.id;
|
|
19248
19273
|
const entries = "entries" in history ? history.entries : history.records;
|
|
@@ -19504,12 +19529,26 @@ async function loadCatalog(client, signal, pendingThreads) {
|
|
|
19504
19529
|
const workspaceByProjectId = new Map(projects.map((project, index) => [project.id, projectWorkspaces[index]]));
|
|
19505
19530
|
const visibleSessionIds = /* @__PURE__ */ new Set();
|
|
19506
19531
|
const visibleThreads = [];
|
|
19532
|
+
const unmatched = [];
|
|
19507
19533
|
for (const { thread, session } of projected) {
|
|
19508
19534
|
const projectWorkspaceForSession = findProjectWorkspace(thread, session, projects, workspaceByProjectId);
|
|
19509
19535
|
if (projectWorkspaceForSession !== void 0) {
|
|
19536
|
+
rebaseSessionCwdOnProjectRoot(session, projectWorkspaceForSession.path);
|
|
19537
|
+
rebaseThreadCwdOnProjectRoot(thread, projectWorkspaceForSession.path);
|
|
19510
19538
|
addSessionToWorkspace(projectWorkspaceForSession, session);
|
|
19511
19539
|
visibleSessionIds.add(session.id);
|
|
19512
19540
|
visibleThreads.push(thread);
|
|
19541
|
+
} else {
|
|
19542
|
+
unmatched.push({ thread, session });
|
|
19543
|
+
}
|
|
19544
|
+
}
|
|
19545
|
+
if (unmatched.length > 0) {
|
|
19546
|
+
const workspace = defaultCodexWorkspace(unmatched.map((value) => value.session));
|
|
19547
|
+
projectWorkspaces.push(workspace);
|
|
19548
|
+
for (const { thread, session } of unmatched) {
|
|
19549
|
+
addSessionToWorkspace(workspace, session);
|
|
19550
|
+
visibleSessionIds.add(session.id);
|
|
19551
|
+
visibleThreads.push(thread);
|
|
19513
19552
|
}
|
|
19514
19553
|
}
|
|
19515
19554
|
const sessions = projected.map((value) => value.session).filter((session) => visibleSessionIds.has(session.id));
|
|
@@ -19600,13 +19639,17 @@ async function loadModelDirectory(client, signal) {
|
|
|
19600
19639
|
models
|
|
19601
19640
|
};
|
|
19602
19641
|
}
|
|
19603
|
-
function projectCodexNativeHistory(thread, sessionId, sessionGeneration = "legacy") {
|
|
19642
|
+
function projectCodexNativeHistory(thread, sessionId, sessionGeneration = "legacy", start = {}) {
|
|
19604
19643
|
const entries = [];
|
|
19605
|
-
let seq = 0;
|
|
19606
|
-
let turnNumber = 0;
|
|
19644
|
+
let seq = start.seq ?? 0;
|
|
19645
|
+
let turnNumber = start.turn ?? 0;
|
|
19646
|
+
let turnSeqBase = 0;
|
|
19647
|
+
let turnEventIndex = 0;
|
|
19648
|
+
let lastSeq = start.stable === true ? -1 : (start.seq ?? 0) - 1;
|
|
19607
19649
|
const active = activeTurnId(thread.turns);
|
|
19608
19650
|
const append = (type, data2, time = Date.now(), view, sourceEventSeqs) => {
|
|
19609
|
-
const eventSeq = seq++;
|
|
19651
|
+
const eventSeq = start.stable === true ? turnSeqBase + turnEventIndex++ : seq++;
|
|
19652
|
+
lastSeq = eventSeq;
|
|
19610
19653
|
entries.push({
|
|
19611
19654
|
type: "event",
|
|
19612
19655
|
event: {
|
|
@@ -19623,13 +19666,16 @@ function projectCodexNativeHistory(thread, sessionId, sessionGeneration = "legac
|
|
|
19623
19666
|
};
|
|
19624
19667
|
for (const rawTurn of array(thread.turns)) {
|
|
19625
19668
|
const turn = record3(rawTurn);
|
|
19669
|
+
const turnId = string2(turn.id);
|
|
19626
19670
|
const turnItems = array(turn.items).map(record3);
|
|
19627
19671
|
const toolImages = turnItems.flatMap(toolOutputImageBlocks);
|
|
19628
19672
|
let imageOwner = -1;
|
|
19629
19673
|
for (let index = 0; index < turnItems.length; index += 1) {
|
|
19630
19674
|
if (turnItems[index].type === "agentMessage") imageOwner = index;
|
|
19631
19675
|
}
|
|
19632
|
-
turnNumber
|
|
19676
|
+
turnNumber = (turnId === void 0 ? void 0 : start.turnNumbers?.get(turnId)) ?? turnNumber + 1;
|
|
19677
|
+
turnSeqBase = turnNumber * 4096;
|
|
19678
|
+
turnEventIndex = 0;
|
|
19633
19679
|
const turnActive = isActiveCodexTurn(turn);
|
|
19634
19680
|
const time = normalizeTime(turn.createdAt) || normalizeTime(turn.startedAt) || normalizeTime(thread.createdAt) || Date.now();
|
|
19635
19681
|
append("turn/start", { turn: turnNumber }, time);
|
|
@@ -19673,7 +19719,7 @@ function projectCodexNativeHistory(thread, sessionId, sessionGeneration = "legac
|
|
|
19673
19719
|
...sessionGeneration === "v3" ? { isSeeded: false } : {}
|
|
19674
19720
|
},
|
|
19675
19721
|
entries,
|
|
19676
|
-
lastSeq
|
|
19722
|
+
lastSeq,
|
|
19677
19723
|
nextTurn: turnNumber,
|
|
19678
19724
|
...active === void 0 ? {} : { activeTurnId: active }
|
|
19679
19725
|
};
|
|
@@ -19682,7 +19728,7 @@ function paginateCodexNativeHistory(history, request) {
|
|
|
19682
19728
|
const throughSeq = Math.min(request.throughSeq ?? history.lastSeq, history.lastSeq);
|
|
19683
19729
|
const endSeq = Math.min(throughSeq, request.beforeSeq === void 0 ? throughSeq : request.beforeSeq - 1);
|
|
19684
19730
|
const window = endSeq < 0 ? [] : history.entries.filter((entry) => entry.event.seq <= endSeq);
|
|
19685
|
-
const maxMessages = request.maxMessages ??
|
|
19731
|
+
const maxMessages = request.maxMessages ?? HISTORY_PAGE_SIZES[0];
|
|
19686
19732
|
let messages = 0;
|
|
19687
19733
|
let cut = 0;
|
|
19688
19734
|
for (let index = window.length - 1; index >= 0; index -= 1) {
|
|
@@ -19957,6 +20003,20 @@ function nativeWorkspace(view) {
|
|
|
19957
20003
|
function nativeVisibleWorkspaces(catalog, selectedWorkspaceId) {
|
|
19958
20004
|
return catalog.workspaces.filter((workspace) => workspace.sessionIds.length > 0 || workspace.workspaceId === selectedWorkspaceId);
|
|
19959
20005
|
}
|
|
20006
|
+
var DEFAULT_CODEX_WORKSPACE_ID = "codex-workspace:default";
|
|
20007
|
+
var DEFAULT_CODEX_WORKSPACE_TITLE = "\u9ED8\u8BA4\u5DE5\u4F5C\u533A";
|
|
20008
|
+
function defaultCodexWorkspace(sessions) {
|
|
20009
|
+
const first = sessions[0];
|
|
20010
|
+
return {
|
|
20011
|
+
workspaceId: DEFAULT_CODEX_WORKSPACE_ID,
|
|
20012
|
+
path: sessions.find((session) => session.cwd !== void 0)?.cwd ?? "/",
|
|
20013
|
+
title: DEFAULT_CODEX_WORKSPACE_TITLE,
|
|
20014
|
+
sessionIds: [],
|
|
20015
|
+
sessionCount: 0,
|
|
20016
|
+
createdAt: isoTime(first.createdAt),
|
|
20017
|
+
updatedAt: isoTime(first.updatedAt || first.createdAt)
|
|
20018
|
+
};
|
|
20019
|
+
}
|
|
19960
20020
|
function projectWorkspace(project) {
|
|
19961
20021
|
const root = project.roots[0];
|
|
19962
20022
|
return {
|
|
@@ -19998,6 +20058,28 @@ function findProjectWorkspace(thread, session, projects, workspaceByProjectId) {
|
|
|
19998
20058
|
})[0];
|
|
19999
20059
|
return match === void 0 ? void 0 : workspaceByProjectId.get(match.project.id);
|
|
20000
20060
|
}
|
|
20061
|
+
function rebasedCwdOnProjectRoot(cwd, projectRoot2) {
|
|
20062
|
+
if (process.platform !== "win32") return void 0;
|
|
20063
|
+
const root = projectRoot2.replace(/[\\/]+$/u, "");
|
|
20064
|
+
if (root.length === 0) return void 0;
|
|
20065
|
+
const normalizedRoot = root.toLowerCase();
|
|
20066
|
+
const normalizedCwd = cwd.toLowerCase();
|
|
20067
|
+
if (normalizedCwd === normalizedRoot) return root;
|
|
20068
|
+
if (normalizedCwd.startsWith(`${normalizedRoot}\\`) || normalizedCwd.startsWith(`${normalizedRoot}/`)) {
|
|
20069
|
+
return `${root}${cwd.slice(root.length)}`;
|
|
20070
|
+
}
|
|
20071
|
+
return void 0;
|
|
20072
|
+
}
|
|
20073
|
+
function rebaseSessionCwdOnProjectRoot(session, projectRoot2) {
|
|
20074
|
+
if (session.cwd === void 0) return;
|
|
20075
|
+
const next = rebasedCwdOnProjectRoot(session.cwd, projectRoot2);
|
|
20076
|
+
if (next !== void 0) session.cwd = next;
|
|
20077
|
+
}
|
|
20078
|
+
function rebaseThreadCwdOnProjectRoot(thread, projectRoot2) {
|
|
20079
|
+
if (typeof thread.cwd !== "string") return;
|
|
20080
|
+
const next = rebasedCwdOnProjectRoot(thread.cwd, projectRoot2);
|
|
20081
|
+
if (next !== void 0) thread.cwd = next;
|
|
20082
|
+
}
|
|
20001
20083
|
function addSessionToWorkspace(workspace, session) {
|
|
20002
20084
|
if (!workspace.sessionIds.includes(session.id)) {
|
|
20003
20085
|
workspace.sessionIds.push(session.id);
|
|
@@ -20014,7 +20096,8 @@ function containsPath(root, candidate) {
|
|
|
20014
20096
|
return normalizedCandidate === normalizedRoot || normalizedCandidate.startsWith(`${normalizedRoot}/`) || normalizedCandidate.startsWith(`${normalizedRoot}\\`);
|
|
20015
20097
|
}
|
|
20016
20098
|
function normalizePathForCompare(path) {
|
|
20017
|
-
|
|
20099
|
+
const trimmed = path.replace(/[\\/]+$/u, "") || path;
|
|
20100
|
+
return process.platform === "win32" ? trimmed.toLowerCase() : trimmed;
|
|
20018
20101
|
}
|
|
20019
20102
|
function modelSelection() {
|
|
20020
20103
|
return { provider: CODEX_PROVIDER, model: CODEX_MODEL };
|
|
@@ -20264,10 +20347,6 @@ function failFrom(error) {
|
|
|
20264
20347
|
const source = error instanceof Error ? error : new Error(String(error));
|
|
20265
20348
|
return fail(errorCode3(source) ?? "internal", source.message, errorDetails(source));
|
|
20266
20349
|
}
|
|
20267
|
-
function isLegacySessionHistoryUnsupported(error) {
|
|
20268
|
-
if (!(error instanceof Error)) return false;
|
|
20269
|
-
return errorCode3(error) === "METHOD_NOT_ALLOWED" || errorCode3(error) === "METHOD_NOT_FOUND" || error.message.includes("The requested Codex method is not available over Remote.");
|
|
20270
|
-
}
|
|
20271
20350
|
function errorCode3(error) {
|
|
20272
20351
|
return "code" in error && typeof error.code === "string" ? error.code : void 0;
|
|
20273
20352
|
}
|
|
@@ -20388,7 +20467,7 @@ var AsyncValueQueue2 = class {
|
|
|
20388
20467
|
continue;
|
|
20389
20468
|
}
|
|
20390
20469
|
if (this.closed) return;
|
|
20391
|
-
const next = await new Promise((
|
|
20470
|
+
const next = await new Promise((resolve5) => this.waiters.push(resolve5));
|
|
20392
20471
|
if (next.done) return;
|
|
20393
20472
|
yield next.value;
|
|
20394
20473
|
}
|
|
@@ -20410,7 +20489,13 @@ function normalizeByteResult2(endpoint, result) {
|
|
|
20410
20489
|
|
|
20411
20490
|
// src/typert-gateway-switch.ts
|
|
20412
20491
|
var REMOTE_COMMAND_METHODS = ["execute", "list"];
|
|
20413
|
-
var LOCAL_ONLY_NAMESPACES = /* @__PURE__ */ new Set([
|
|
20492
|
+
var LOCAL_ONLY_NAMESPACES = /* @__PURE__ */ new Set([
|
|
20493
|
+
"account",
|
|
20494
|
+
"dynamicCordisRunner",
|
|
20495
|
+
"remoteAccess",
|
|
20496
|
+
"schedule",
|
|
20497
|
+
"web"
|
|
20498
|
+
]);
|
|
20414
20499
|
var LOCAL_FALLBACK_NAMESPACES = /* @__PURE__ */ new Set(["$events", "settings", "credentials", "dynamicCordisRunner"]);
|
|
20415
20500
|
var PEER_GONE_CODES = /* @__PURE__ */ new Set([
|
|
20416
20501
|
"TRANSPORT_CLOSED",
|
|
@@ -20767,7 +20852,7 @@ import { networkInterfaces } from "node:os";
|
|
|
20767
20852
|
// src/native-rtc-helper.ts
|
|
20768
20853
|
import { spawn, spawnSync } from "node:child_process";
|
|
20769
20854
|
import { existsSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
20770
|
-
import { delimiter, dirname as dirname3, join as
|
|
20855
|
+
import { delimiter, dirname as dirname3, join as join5 } from "node:path";
|
|
20771
20856
|
import { fileURLToPath } from "node:url";
|
|
20772
20857
|
var cachedExternalFactory;
|
|
20773
20858
|
var cachedExternalFactoryResolved = false;
|
|
@@ -20818,21 +20903,21 @@ function nodeBinaryCandidates() {
|
|
|
20818
20903
|
add3(process.env.DSH_REMOTE_NODE);
|
|
20819
20904
|
add3(process.env.NODE);
|
|
20820
20905
|
if (process.versions.electron === void 0) add3(process.execPath);
|
|
20821
|
-
for (const part of (process.env.PATH ?? "").split(delimiter)) add3(
|
|
20906
|
+
for (const part of (process.env.PATH ?? "").split(delimiter)) add3(join5(part, process.platform === "win32" ? "node.exe" : "node"));
|
|
20822
20907
|
add3("/opt/homebrew/bin/node");
|
|
20823
20908
|
add3("/usr/local/bin/node");
|
|
20824
20909
|
add3("/usr/bin/node");
|
|
20825
|
-
add3(
|
|
20826
|
-
add3(
|
|
20827
|
-
add3(
|
|
20910
|
+
add3(join5(process.env.HOME ?? "", ".volta", "bin", process.platform === "win32" ? "node.exe" : "node"));
|
|
20911
|
+
add3(join5(process.env.HOME ?? "", ".asdf", "shims", process.platform === "win32" ? "node.exe" : "node"));
|
|
20912
|
+
add3(join5(process.env.HOME ?? "", ".local", "bin", process.platform === "win32" ? "node.exe" : "node"));
|
|
20828
20913
|
for (const nvmNode of nvmNodeCandidates()) add3(nvmNode);
|
|
20829
20914
|
return candidates;
|
|
20830
20915
|
}
|
|
20831
20916
|
function nvmNodeCandidates() {
|
|
20832
|
-
const root =
|
|
20917
|
+
const root = join5(process.env.HOME ?? "", ".nvm", "versions", "node");
|
|
20833
20918
|
if (root === "" || !existsSync(root)) return [];
|
|
20834
20919
|
try {
|
|
20835
|
-
return readdirSync(root).map((version) =>
|
|
20920
|
+
return readdirSync(root).map((version) => join5(root, version, "bin", process.platform === "win32" ? "node.exe" : "node")).sort((left, right) => right.localeCompare(left, "en", { numeric: true }));
|
|
20836
20921
|
} catch {
|
|
20837
20922
|
return [];
|
|
20838
20923
|
}
|
|
@@ -20955,8 +21040,8 @@ var ExternalNativePeerConnection = class {
|
|
|
20955
21040
|
request(method, payload) {
|
|
20956
21041
|
if (this.closed) return Promise.reject(new Error("native rtc helper is closed"));
|
|
20957
21042
|
const id4 = this.nextRequestId++;
|
|
20958
|
-
const promise = new Promise((
|
|
20959
|
-
this.pending.set(id4, { resolve:
|
|
21043
|
+
const promise = new Promise((resolve5, reject) => {
|
|
21044
|
+
this.pending.set(id4, { resolve: resolve5, reject });
|
|
20960
21045
|
});
|
|
20961
21046
|
this.write({ id: id4, method, payload });
|
|
20962
21047
|
return promise;
|
|
@@ -21750,10 +21835,10 @@ function parseRouteTarget(value) {
|
|
|
21750
21835
|
async function detectRouteHostIpv4(target2, timeoutMs) {
|
|
21751
21836
|
const socket = createSocket("udp4");
|
|
21752
21837
|
try {
|
|
21753
|
-
return await new Promise((
|
|
21838
|
+
return await new Promise((resolve5, reject) => {
|
|
21754
21839
|
const timer = setTimeout(() => {
|
|
21755
21840
|
cleanup();
|
|
21756
|
-
|
|
21841
|
+
resolve5(void 0);
|
|
21757
21842
|
}, timeoutMs);
|
|
21758
21843
|
const cleanup = () => {
|
|
21759
21844
|
clearTimeout(timer);
|
|
@@ -21767,7 +21852,7 @@ async function detectRouteHostIpv4(target2, timeoutMs) {
|
|
|
21767
21852
|
socket.connect(target2.port, target2.host, () => {
|
|
21768
21853
|
cleanup();
|
|
21769
21854
|
const address = socket.address();
|
|
21770
|
-
|
|
21855
|
+
resolve5(typeof address === "string" ? void 0 : address.address);
|
|
21771
21856
|
});
|
|
21772
21857
|
});
|
|
21773
21858
|
} finally {
|
|
@@ -21862,6 +21947,7 @@ var ClientModeRuntime = class {
|
|
|
21862
21947
|
this.proxySwitch = apiProxy === void 0 ? void 0 : new ApiProxySwitch(apiProxy);
|
|
21863
21948
|
this.gatewaySwitch = new TypertGatewaySwitch(typertGateway, { peer: this.peer });
|
|
21864
21949
|
this.gatewaySwitch.setRemoteAvailability(() => this.connected !== void 0);
|
|
21950
|
+
this.installCodexHistoryCacheInspector();
|
|
21865
21951
|
}
|
|
21866
21952
|
preview;
|
|
21867
21953
|
identity;
|
|
@@ -23316,6 +23402,43 @@ var ClientModeRuntime = class {
|
|
|
23316
23402
|
const virtual = this.codexVirtual;
|
|
23317
23403
|
this.codexVirtual = void 0;
|
|
23318
23404
|
await virtual?.close();
|
|
23405
|
+
this.installCodexHistoryCacheInspector();
|
|
23406
|
+
}
|
|
23407
|
+
/**
|
|
23408
|
+
* Keep `__dshCodexHistoryCacheStats()` defined for the whole client lifetime.
|
|
23409
|
+
* The Codex virtual harness has its own inspector while a workspace is open;
|
|
23410
|
+
* this runtime-level one fills the gap before the first open and after close.
|
|
23411
|
+
*/
|
|
23412
|
+
installCodexHistoryCacheInspector() {
|
|
23413
|
+
const globalScope = globalThis;
|
|
23414
|
+
const scope = globalThis;
|
|
23415
|
+
const inspect = () => this.codexVirtual?.historyCacheStats() ?? {
|
|
23416
|
+
ttlMs: 6e4,
|
|
23417
|
+
hits: 0,
|
|
23418
|
+
misses: 0,
|
|
23419
|
+
active: false,
|
|
23420
|
+
entries: []
|
|
23421
|
+
};
|
|
23422
|
+
globalScope.__dshCodexHistoryCacheStats = inspect;
|
|
23423
|
+
if (scope.window !== void 0) scope.window.__dshCodexHistoryCacheStats = inspect;
|
|
23424
|
+
this.writeCodexHistoryCacheStats(inspect());
|
|
23425
|
+
}
|
|
23426
|
+
writeCodexHistoryCacheStats(stats) {
|
|
23427
|
+
if (!codexHistoryStatsFileEnabled() || process.env.VITEST !== void 0) return;
|
|
23428
|
+
try {
|
|
23429
|
+
const home = process.env.DSH_HOME ?? join6(homedir2(), ".dsh");
|
|
23430
|
+
mkdirSync2(home, { recursive: true });
|
|
23431
|
+
writeFileSync2(
|
|
23432
|
+
join6(home, "codex-history-cache-stats.json"),
|
|
23433
|
+
JSON.stringify({
|
|
23434
|
+
...stats,
|
|
23435
|
+
pid: process.pid,
|
|
23436
|
+
source: "client-runtime",
|
|
23437
|
+
writtenAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
23438
|
+
}, null, 2)
|
|
23439
|
+
);
|
|
23440
|
+
} catch {
|
|
23441
|
+
}
|
|
23319
23442
|
}
|
|
23320
23443
|
assertRemoteCompatible(remote) {
|
|
23321
23444
|
this.selectHarnessRemoteTransport(remote);
|
|
@@ -23912,8 +24035,8 @@ function failureCode(error) {
|
|
|
23912
24035
|
return own ?? safeErrorCode(error);
|
|
23913
24036
|
}
|
|
23914
24037
|
function sleep3(ms) {
|
|
23915
|
-
return new Promise((
|
|
23916
|
-
setTimeout(
|
|
24038
|
+
return new Promise((resolve5) => {
|
|
24039
|
+
setTimeout(resolve5, ms);
|
|
23917
24040
|
});
|
|
23918
24041
|
}
|
|
23919
24042
|
function remoteHostFeatures(clientVersion) {
|
|
@@ -23964,7 +24087,7 @@ async function probeRemoteHostFeatures(client, clientVersion) {
|
|
|
23964
24087
|
}
|
|
23965
24088
|
async function waitForCodexFrames(stream, signal) {
|
|
23966
24089
|
if (signal?.aborted) throw new ClientModeError("RPC_ABORTED", "The Codex event poll was cancelled.");
|
|
23967
|
-
await new Promise((
|
|
24090
|
+
await new Promise((resolve5, reject) => {
|
|
23968
24091
|
const previousWake = stream.wake;
|
|
23969
24092
|
const timer = setTimeout(done, 25e3);
|
|
23970
24093
|
const onAbort = () => {
|
|
@@ -23978,7 +24101,7 @@ async function waitForCodexFrames(stream, signal) {
|
|
|
23978
24101
|
}
|
|
23979
24102
|
function done() {
|
|
23980
24103
|
cleanup();
|
|
23981
|
-
|
|
24104
|
+
resolve5();
|
|
23982
24105
|
}
|
|
23983
24106
|
stream.wake = () => {
|
|
23984
24107
|
previousWake();
|
|
@@ -24023,14 +24146,14 @@ function iceServersForAttempt(attempt, iceServers) {
|
|
|
24023
24146
|
// src/client-target-store.ts
|
|
24024
24147
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
24025
24148
|
import { chmod as chmod2, mkdir as mkdir2, readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises";
|
|
24026
|
-
import { join as
|
|
24149
|
+
import { join as join7 } from "node:path";
|
|
24027
24150
|
var FILE_NAME = "client-target.json";
|
|
24028
24151
|
var ClientTargetStore = class {
|
|
24029
24152
|
constructor(directory) {
|
|
24030
24153
|
this.directory = directory;
|
|
24031
24154
|
}
|
|
24032
24155
|
get file() {
|
|
24033
|
-
return
|
|
24156
|
+
return join7(this.directory, FILE_NAME);
|
|
24034
24157
|
}
|
|
24035
24158
|
/** Read the record. A missing, unreadable or malformed file reads as "no record". */
|
|
24036
24159
|
async load() {
|
|
@@ -24106,8 +24229,8 @@ import { execFileSync } from "node:child_process";
|
|
|
24106
24229
|
import { existsSync as existsSync2 } from "node:fs";
|
|
24107
24230
|
import { createHash } from "node:crypto";
|
|
24108
24231
|
import { chmod as chmod3, cp, mkdir as mkdir3, readFile as readFile4, rm as rm3, stat as stat3, writeFile as writeFile3 } from "node:fs/promises";
|
|
24109
|
-
import { homedir } from "node:os";
|
|
24110
|
-
import { dirname as dirname4, join as
|
|
24232
|
+
import { homedir as homedir3 } from "node:os";
|
|
24233
|
+
import { dirname as dirname4, join as join8 } from "node:path";
|
|
24111
24234
|
var identitySchema = external_exports.object({
|
|
24112
24235
|
schemaVersion: external_exports.literal(1),
|
|
24113
24236
|
deviceId: external_exports.string().uuid(),
|
|
@@ -24136,14 +24259,14 @@ var IdentityStore = class {
|
|
|
24136
24259
|
peers = /* @__PURE__ */ new Map();
|
|
24137
24260
|
constructor(options = {}) {
|
|
24138
24261
|
const env = options.env ?? process.env;
|
|
24139
|
-
const dshHome = env.DSH_HOME ||
|
|
24140
|
-
this.directory = options.directory ??
|
|
24262
|
+
const dshHome = env.DSH_HOME || join8(options.homeDirectory ?? homedir3(), ".dsh");
|
|
24263
|
+
this.directory = options.directory ?? join8(dshHome, "remote");
|
|
24141
24264
|
}
|
|
24142
24265
|
async loadOrCreate(deviceName) {
|
|
24143
24266
|
await mkdir3(this.directory, { recursive: true, mode: 448 });
|
|
24144
24267
|
await chmod3(this.directory, 448);
|
|
24145
|
-
const devicePath =
|
|
24146
|
-
const keyPath =
|
|
24268
|
+
const devicePath = join8(this.directory, "device.json");
|
|
24269
|
+
const keyPath = join8(this.directory, "device.key");
|
|
24147
24270
|
const [hasDevice, hasKey] = await Promise.all([exists2(devicePath), exists2(keyPath)]);
|
|
24148
24271
|
if (hasDevice !== hasKey) {
|
|
24149
24272
|
throw new IdentityInvalidError("device identity is incomplete; repair it explicitly before reconnecting");
|
|
@@ -24212,7 +24335,7 @@ var IdentityStore = class {
|
|
|
24212
24335
|
return removed;
|
|
24213
24336
|
}
|
|
24214
24337
|
async loadPeers() {
|
|
24215
|
-
const path =
|
|
24338
|
+
const path = join8(this.directory, "trusted-peers.json");
|
|
24216
24339
|
if (!await exists2(path)) {
|
|
24217
24340
|
await atomicJsonWrite(path, { schemaVersion: 1, peers: [] }, 384);
|
|
24218
24341
|
}
|
|
@@ -24228,7 +24351,7 @@ var IdentityStore = class {
|
|
|
24228
24351
|
this.peers = peers;
|
|
24229
24352
|
}
|
|
24230
24353
|
async savePeers() {
|
|
24231
|
-
await atomicJsonWrite(
|
|
24354
|
+
await atomicJsonWrite(join8(this.directory, "trusted-peers.json"), {
|
|
24232
24355
|
schemaVersion: 1,
|
|
24233
24356
|
peers: [...this.peers.values()]
|
|
24234
24357
|
}, 384);
|
|
@@ -24237,7 +24360,7 @@ var IdentityStore = class {
|
|
|
24237
24360
|
function serverStorageDirectory(root, serverUrl, role) {
|
|
24238
24361
|
const origin = new URL(serverUrl).origin;
|
|
24239
24362
|
const scope = createHash("sha256").update(origin).digest("hex").slice(0, 24);
|
|
24240
|
-
return
|
|
24363
|
+
return join8(root, "servers", scope, role);
|
|
24241
24364
|
}
|
|
24242
24365
|
async function ensureDeviceDirectory(root, serverUrl) {
|
|
24243
24366
|
const directory = serverStorageDirectory(root, serverUrl, "device");
|
|
@@ -24438,10 +24561,11 @@ var ControlStatusStream = class {
|
|
|
24438
24561
|
|
|
24439
24562
|
// src/codex/domain.ts
|
|
24440
24563
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
24564
|
+
import { Buffer as Buffer4 } from "node:buffer";
|
|
24441
24565
|
import { accessSync, constants, existsSync as existsSync3, readFileSync, readdirSync as readdirSync2, statSync as statSync2 } from "node:fs";
|
|
24442
24566
|
import { readdir as readdir2, realpath, stat as stat4 } from "node:fs/promises";
|
|
24443
|
-
import { homedir as
|
|
24444
|
-
import { basename as basename2, isAbsolute as isAbsolute2, join as
|
|
24567
|
+
import { homedir as homedir4 } from "node:os";
|
|
24568
|
+
import { basename as basename2, isAbsolute as isAbsolute2, join as join9, posix, relative, resolve } from "node:path";
|
|
24445
24569
|
|
|
24446
24570
|
// src/codex/app-server.ts
|
|
24447
24571
|
import { spawn as spawn2 } from "node:child_process";
|
|
@@ -24514,15 +24638,15 @@ var CodexAppServerClient = class {
|
|
|
24514
24638
|
const child = this.process;
|
|
24515
24639
|
this.process = void 0;
|
|
24516
24640
|
if (child === void 0 || child.exitCode !== null || child.killed) return;
|
|
24517
|
-
await new Promise((
|
|
24641
|
+
await new Promise((resolve5) => {
|
|
24518
24642
|
const timer = setTimeout(() => {
|
|
24519
24643
|
child.kill("SIGKILL");
|
|
24520
|
-
|
|
24644
|
+
resolve5();
|
|
24521
24645
|
}, 2e3);
|
|
24522
24646
|
timer.unref?.();
|
|
24523
24647
|
child.once("exit", () => {
|
|
24524
24648
|
clearTimeout(timer);
|
|
24525
|
-
|
|
24649
|
+
resolve5();
|
|
24526
24650
|
});
|
|
24527
24651
|
child.kill("SIGTERM");
|
|
24528
24652
|
});
|
|
@@ -24578,13 +24702,13 @@ var CodexAppServerClient = class {
|
|
|
24578
24702
|
}
|
|
24579
24703
|
request(method, params, timeoutMs) {
|
|
24580
24704
|
const id4 = this.nextId++;
|
|
24581
|
-
const result = new Promise((
|
|
24705
|
+
const result = new Promise((resolve5, reject) => {
|
|
24582
24706
|
const timer = setTimeout(() => {
|
|
24583
24707
|
this.pending.delete(id4);
|
|
24584
24708
|
reject(new CodexAppServerError("CODEX_REQUEST_TIMEOUT", "Codex App Server request timed out."));
|
|
24585
24709
|
}, timeoutMs);
|
|
24586
24710
|
timer.unref?.();
|
|
24587
|
-
this.pending.set(id4, { resolve:
|
|
24711
|
+
this.pending.set(id4, { resolve: resolve5, reject, timer });
|
|
24588
24712
|
});
|
|
24589
24713
|
try {
|
|
24590
24714
|
this.write({ id: id4, method, params });
|
|
@@ -24698,20 +24822,149 @@ function isRecord10(value) {
|
|
|
24698
24822
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
24699
24823
|
}
|
|
24700
24824
|
|
|
24701
|
-
// src/codex/
|
|
24702
|
-
|
|
24703
|
-
var
|
|
24704
|
-
|
|
24705
|
-
|
|
24706
|
-
|
|
24707
|
-
var streamCloseSchema = external_exports.object({ streamId: external_exports.string().min(1).max(128) }).strict();
|
|
24708
|
-
var transferOpenSchema = external_exports.object({
|
|
24709
|
-
transferId: external_exports.string().uuid(),
|
|
24710
|
-
totalBytes: external_exports.number().int().positive().max(MAX_CODEX_APP_TRANSFER_BYTES),
|
|
24711
|
-
totalChunks: external_exports.number().int().positive()
|
|
24825
|
+
// src/codex/method-policy.ts
|
|
24826
|
+
var id2 = external_exports.string().min(1).max(256);
|
|
24827
|
+
var cursor = external_exports.string().min(1).max(4096).nullable().optional();
|
|
24828
|
+
var textInput = external_exports.object({
|
|
24829
|
+
type: external_exports.literal("text"),
|
|
24830
|
+
text: external_exports.string().min(1).max(256 * 1024)
|
|
24712
24831
|
}).strict();
|
|
24713
|
-
var
|
|
24714
|
-
|
|
24832
|
+
var imageMediaType = external_exports.enum(["image/png", "image/jpeg", "image/webp", "image/gif"]);
|
|
24833
|
+
var canonicalBase64 = external_exports.string().min(4).max(288 * 1024 * 1024).refine((value) => value.length % 4 === 0 && /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u.test(value), {
|
|
24834
|
+
message: "Image data must use canonical base64."
|
|
24835
|
+
});
|
|
24836
|
+
var imageInput = external_exports.object({
|
|
24837
|
+
type: external_exports.literal("image"),
|
|
24838
|
+
mediaType: imageMediaType,
|
|
24839
|
+
data: canonicalBase64
|
|
24840
|
+
}).strict();
|
|
24841
|
+
var input = external_exports.array(external_exports.union([textInput, imageInput])).min(1).max(16);
|
|
24842
|
+
var permissionPreset = external_exports.enum(["workspace-write", "danger-full-access"]);
|
|
24843
|
+
var projectRoot = external_exports.object({
|
|
24844
|
+
path: external_exports.string().min(1).max(4096)
|
|
24845
|
+
}).strict();
|
|
24846
|
+
var CODEX_HISTORY_MAX_MESSAGES = 200;
|
|
24847
|
+
var schemas = {
|
|
24848
|
+
"account/read": external_exports.object({ refreshToken: external_exports.literal(false).optional() }).strict(),
|
|
24849
|
+
"model/list": external_exports.object({
|
|
24850
|
+
cursor,
|
|
24851
|
+
limit: external_exports.number().int().min(1).max(100).optional(),
|
|
24852
|
+
includeHidden: external_exports.boolean().optional()
|
|
24853
|
+
}).strict(),
|
|
24854
|
+
"project/list": external_exports.object({
|
|
24855
|
+
cursor,
|
|
24856
|
+
limit: external_exports.number().int().min(1).max(100).optional()
|
|
24857
|
+
}).strict(),
|
|
24858
|
+
"project/create": external_exports.object({
|
|
24859
|
+
name: external_exports.string().trim().min(1).max(256),
|
|
24860
|
+
roots: external_exports.array(projectRoot).length(1),
|
|
24861
|
+
idempotencyKey: external_exports.string().min(16).max(256)
|
|
24862
|
+
}).strict(),
|
|
24863
|
+
"thread/list": external_exports.object({
|
|
24864
|
+
cursor,
|
|
24865
|
+
limit: external_exports.number().int().min(1).max(100).optional(),
|
|
24866
|
+
sortKey: external_exports.enum(["created_at", "updated_at", "recency_at"]).optional(),
|
|
24867
|
+
sortDirection: external_exports.enum(["asc", "desc"]).optional(),
|
|
24868
|
+
modelProviders: external_exports.array(external_exports.string().min(1).max(128)).max(32).nullable().optional(),
|
|
24869
|
+
sourceKinds: external_exports.array(external_exports.enum(["cli", "vscode", "exec", "appServer", "unknown"])).max(8).optional(),
|
|
24870
|
+
archived: external_exports.boolean().optional(),
|
|
24871
|
+
isPinned: external_exports.boolean().optional(),
|
|
24872
|
+
cwd: external_exports.union([external_exports.string().min(1).max(4096), external_exports.array(external_exports.string().min(1).max(4096)).min(1).max(32)]).optional(),
|
|
24873
|
+
useStateDbOnly: external_exports.boolean().optional(),
|
|
24874
|
+
searchTerm: external_exports.string().max(1024).optional(),
|
|
24875
|
+
// Upstream `ThreadListParams` declares these two as well. Refusing them turned a
|
|
24876
|
+
// legitimate client query into "The CodeX call parameters are invalid.".
|
|
24877
|
+
originators: external_exports.array(external_exports.string().min(1).max(256)).max(32).nullable().optional(),
|
|
24878
|
+
sectionId: external_exports.string().max(256).nullable().optional()
|
|
24879
|
+
}).strict(),
|
|
24880
|
+
"thread/read": external_exports.object({ threadId: id2, includeTurns: external_exports.boolean().optional() }).strict(),
|
|
24881
|
+
"dsh/sessionHistory": external_exports.object({
|
|
24882
|
+
threadId: id2,
|
|
24883
|
+
beforeSeq: external_exports.number().int().nonnegative().optional(),
|
|
24884
|
+
throughSeq: external_exports.number().int().min(-1).optional(),
|
|
24885
|
+
maxMessages: external_exports.number().int().min(1).max(CODEX_HISTORY_MAX_MESSAGES).optional()
|
|
24886
|
+
}).strict(),
|
|
24887
|
+
"dsh/directoryList": external_exports.object({
|
|
24888
|
+
path: external_exports.string().min(1).max(4096)
|
|
24889
|
+
}).strict(),
|
|
24890
|
+
"thread/start": external_exports.object({
|
|
24891
|
+
cwd: external_exports.string().min(1).max(4096),
|
|
24892
|
+
model: external_exports.string().min(1).max(128).optional(),
|
|
24893
|
+
personality: external_exports.string().min(1).max(64).optional(),
|
|
24894
|
+
permissionPreset: permissionPreset.optional()
|
|
24895
|
+
}).strict(),
|
|
24896
|
+
"thread/resume": external_exports.object({
|
|
24897
|
+
threadId: id2,
|
|
24898
|
+
model: external_exports.string().min(1).max(128).optional(),
|
|
24899
|
+
permissionPreset: permissionPreset.optional()
|
|
24900
|
+
}).strict(),
|
|
24901
|
+
"thread/fork": external_exports.object({
|
|
24902
|
+
threadId: id2,
|
|
24903
|
+
lastTurnId: id2.optional(),
|
|
24904
|
+
permissionPreset: permissionPreset.optional()
|
|
24905
|
+
}).strict(),
|
|
24906
|
+
// Upstream declares a plain string and uses the empty value to clear a name, so the
|
|
24907
|
+
// trim-based minimum rejected a legitimate rename.
|
|
24908
|
+
"thread/name/set": external_exports.object({ threadId: id2, name: external_exports.string().max(256) }).strict(),
|
|
24909
|
+
"thread/archive": external_exports.object({ threadId: id2 }).strict(),
|
|
24910
|
+
"thread/unarchive": external_exports.object({ threadId: id2 }).strict(),
|
|
24911
|
+
"thread/unsubscribe": external_exports.object({ threadId: id2 }).strict(),
|
|
24912
|
+
"turn/start": external_exports.object({
|
|
24913
|
+
threadId: id2,
|
|
24914
|
+
input,
|
|
24915
|
+
model: external_exports.string().min(1).max(128).optional(),
|
|
24916
|
+
effort: external_exports.enum(["none", "minimal", "low", "medium", "high", "xhigh", "max", "ultra"]).optional(),
|
|
24917
|
+
summary: external_exports.enum(["auto", "concise", "detailed", "none"]).optional(),
|
|
24918
|
+
personality: external_exports.string().min(1).max(64).optional(),
|
|
24919
|
+
permissionPreset: permissionPreset.optional()
|
|
24920
|
+
}).strict(),
|
|
24921
|
+
"turn/steer": external_exports.object({
|
|
24922
|
+
threadId: id2,
|
|
24923
|
+
input,
|
|
24924
|
+
expectedTurnId: id2,
|
|
24925
|
+
// Declared by upstream `TurnSteerParams`; see the thread/list note above.
|
|
24926
|
+
clientUserMessageId: external_exports.string().max(256).nullable().optional()
|
|
24927
|
+
}).strict(),
|
|
24928
|
+
"turn/interrupt": external_exports.object({ threadId: id2, turnId: id2 }).strict()
|
|
24929
|
+
};
|
|
24930
|
+
var CODEX_APP_ALLOWLIST = Object.freeze(Object.keys(schemas));
|
|
24931
|
+
function parseCodexCall(method, params) {
|
|
24932
|
+
if (!Object.prototype.hasOwnProperty.call(schemas, method)) {
|
|
24933
|
+
throw new RpcError("METHOD_NOT_ALLOWED", "The requested Codex method is not available over Remote.");
|
|
24934
|
+
}
|
|
24935
|
+
const schema = schemas[method];
|
|
24936
|
+
const parsed = schema.safeParse(params);
|
|
24937
|
+
if (!parsed.success) {
|
|
24938
|
+
const detail = parsed.error.issues.slice(0, 4).map((issue) => {
|
|
24939
|
+
const path = issue.path.length === 0 ? "(root)" : issue.path.join(".");
|
|
24940
|
+
const keys = "keys" in issue && Array.isArray(issue.keys) ? ` [${issue.keys.join(", ")}]` : "";
|
|
24941
|
+
return `${path}${keys}: ${issue.code}`;
|
|
24942
|
+
}).join(", ");
|
|
24943
|
+
throw new RpcError("INVALID_MESSAGE", `The CodeX call parameters are invalid. (${method} \u2192 ${detail})`);
|
|
24944
|
+
}
|
|
24945
|
+
return { method, params: parsed.data };
|
|
24946
|
+
}
|
|
24947
|
+
function isThreadMutation(method) {
|
|
24948
|
+
return method === "turn/start" || method === "turn/steer" || method === "turn/interrupt";
|
|
24949
|
+
}
|
|
24950
|
+
function threadIdFromParams(params) {
|
|
24951
|
+
return typeof params.threadId === "string" ? params.threadId : void 0;
|
|
24952
|
+
}
|
|
24953
|
+
|
|
24954
|
+
// src/codex/peer-bridge.ts
|
|
24955
|
+
import { Buffer as Buffer3 } from "node:buffer";
|
|
24956
|
+
var streamOpenSchema = external_exports.object({
|
|
24957
|
+
streamId: external_exports.string().min(1).max(128),
|
|
24958
|
+
threadId: external_exports.string().min(1).max(256)
|
|
24959
|
+
}).strict();
|
|
24960
|
+
var streamCloseSchema = external_exports.object({ streamId: external_exports.string().min(1).max(128) }).strict();
|
|
24961
|
+
var transferOpenSchema = external_exports.object({
|
|
24962
|
+
transferId: external_exports.string().uuid(),
|
|
24963
|
+
totalBytes: external_exports.number().int().positive().max(MAX_CODEX_APP_TRANSFER_BYTES),
|
|
24964
|
+
totalChunks: external_exports.number().int().positive()
|
|
24965
|
+
}).strict();
|
|
24966
|
+
var transferChunkSchema = external_exports.object({
|
|
24967
|
+
transferId: external_exports.string().uuid(),
|
|
24715
24968
|
index: external_exports.number().int().nonnegative(),
|
|
24716
24969
|
data: external_exports.string().min(1).max(Math.ceil(CODEX_APP_TRANSFER_CHUNK_BYTES / 3) * 4)
|
|
24717
24970
|
}).strict();
|
|
@@ -24988,6 +25241,219 @@ function concatChunks(chunks, totalBytes) {
|
|
|
24988
25241
|
return output;
|
|
24989
25242
|
}
|
|
24990
25243
|
|
|
25244
|
+
// src/codex/history-window.ts
|
|
25245
|
+
var PAGE_LIMIT = 25;
|
|
25246
|
+
var MAX_PAGES = 64;
|
|
25247
|
+
var CACHE_TTL_MS = 5 * 6e4;
|
|
25248
|
+
var CACHE_LIMIT = 16;
|
|
25249
|
+
var CodexHistoryWindows = class {
|
|
25250
|
+
constructor(call, logger) {
|
|
25251
|
+
this.call = call;
|
|
25252
|
+
this.logger = logger;
|
|
25253
|
+
}
|
|
25254
|
+
windows = /* @__PURE__ */ new Map();
|
|
25255
|
+
async window(threadId, metadata, beforeSeq, maxMessages) {
|
|
25256
|
+
const updatedAt = typeof metadata.updatedAt === "number" ? metadata.updatedAt : void 0;
|
|
25257
|
+
const cached = this.windows.get(threadId);
|
|
25258
|
+
const fresh = cached !== void 0 && (updatedAt === void 0 || cached.updatedAt === void 0 || cached.updatedAt === updatedAt) && Date.now() - cached.at < CACHE_TTL_MS;
|
|
25259
|
+
if (fresh && cached !== void 0) {
|
|
25260
|
+
cached.at = Date.now();
|
|
25261
|
+
await this.ensureOlder(threadId, cached, beforeSeq, maxMessages);
|
|
25262
|
+
this.logger?.debug("codex history cache hit", {
|
|
25263
|
+
threadId,
|
|
25264
|
+
turns: cached.loaded.size,
|
|
25265
|
+
records: cached.history.entries.length
|
|
25266
|
+
});
|
|
25267
|
+
return cached;
|
|
25268
|
+
}
|
|
25269
|
+
if (cached !== void 0) {
|
|
25270
|
+
const appended = await this.appendNewTurns(threadId, cached, updatedAt);
|
|
25271
|
+
if (appended !== void 0) {
|
|
25272
|
+
await this.ensureOlder(threadId, appended, beforeSeq, maxMessages);
|
|
25273
|
+
this.remember(threadId, appended);
|
|
25274
|
+
this.logger?.debug("codex history cache appended", {
|
|
25275
|
+
threadId,
|
|
25276
|
+
turns: appended.loaded.size,
|
|
25277
|
+
records: appended.history.entries.length
|
|
25278
|
+
});
|
|
25279
|
+
return appended;
|
|
25280
|
+
}
|
|
25281
|
+
}
|
|
25282
|
+
const built = await this.rebuild(threadId, metadata, cached?.acceptedPageSize);
|
|
25283
|
+
await this.ensureOlder(threadId, built, beforeSeq, maxMessages);
|
|
25284
|
+
this.remember(threadId, built);
|
|
25285
|
+
return built;
|
|
25286
|
+
}
|
|
25287
|
+
async rebuild(threadId, metadata, acceptedPageSize) {
|
|
25288
|
+
const startedAt = performance.now();
|
|
25289
|
+
const index = await this.readTurnIndex(threadId);
|
|
25290
|
+
const window = {
|
|
25291
|
+
metadata,
|
|
25292
|
+
turnNumbers: index.numbers,
|
|
25293
|
+
turnStartSeq: index.turnStartSeq,
|
|
25294
|
+
totalTurns: index.total,
|
|
25295
|
+
totalEvents: index.totalEvents,
|
|
25296
|
+
loaded: /* @__PURE__ */ new Map(),
|
|
25297
|
+
history: projectCodexNativeHistory({ ...metadata, turns: [] }, `codex:${threadId}`, "legacy", {
|
|
25298
|
+
turnNumbers: index.numbers
|
|
25299
|
+
}),
|
|
25300
|
+
...typeof metadata.updatedAt === "number" ? { updatedAt: metadata.updatedAt } : {},
|
|
25301
|
+
...acceptedPageSize === void 0 ? {} : { acceptedPageSize },
|
|
25302
|
+
at: Date.now()
|
|
25303
|
+
};
|
|
25304
|
+
this.logger?.debug("codex history index", {
|
|
25305
|
+
threadId,
|
|
25306
|
+
turns: index.total,
|
|
25307
|
+
pages: index.pages,
|
|
25308
|
+
durationMs: Math.round(performance.now() - startedAt)
|
|
25309
|
+
});
|
|
25310
|
+
await this.loadTail(threadId, window);
|
|
25311
|
+
return window;
|
|
25312
|
+
}
|
|
25313
|
+
async readTurnIndex(threadId) {
|
|
25314
|
+
const numbers = /* @__PURE__ */ new Map();
|
|
25315
|
+
const turnStartSeq = /* @__PURE__ */ new Map();
|
|
25316
|
+
let total = 0;
|
|
25317
|
+
let totalEvents = 0;
|
|
25318
|
+
let pages = 0;
|
|
25319
|
+
let cursor2;
|
|
25320
|
+
for (let page = 0; page < MAX_PAGES; page += 1) {
|
|
25321
|
+
const result = await this.turnPage(threadId, cursor2, "asc", "summary");
|
|
25322
|
+
pages += 1;
|
|
25323
|
+
for (const rawTurn of result.turns) {
|
|
25324
|
+
const turnId = typeof rawTurn.id === "string" ? rawTurn.id : void 0;
|
|
25325
|
+
if (turnId === void 0 || numbers.has(turnId)) continue;
|
|
25326
|
+
total += 1;
|
|
25327
|
+
numbers.set(turnId, total);
|
|
25328
|
+
turnStartSeq.set(turnId, totalEvents);
|
|
25329
|
+
totalEvents += projectCodexNativeHistory({ turns: [rawTurn] }, `codex:${threadId}`).entries.length;
|
|
25330
|
+
}
|
|
25331
|
+
cursor2 = result.nextCursor;
|
|
25332
|
+
if (cursor2 === void 0) break;
|
|
25333
|
+
}
|
|
25334
|
+
return { numbers, turnStartSeq, total, totalEvents, pages };
|
|
25335
|
+
}
|
|
25336
|
+
async loadTail(threadId, window) {
|
|
25337
|
+
let cursor2;
|
|
25338
|
+
let fetched = 0;
|
|
25339
|
+
for (let page = 0; page < MAX_PAGES; page += 1) {
|
|
25340
|
+
const result = await this.turnPage(threadId, cursor2, "desc", "full");
|
|
25341
|
+
fetched += result.turns.length;
|
|
25342
|
+
this.addTurns(window, result.turns);
|
|
25343
|
+
cursor2 = result.nextCursor;
|
|
25344
|
+
this.project(threadId, window);
|
|
25345
|
+
if (surfaceRecords(window.history) >= HISTORY_PAGE_SIZES[0] || cursor2 === void 0) break;
|
|
25346
|
+
}
|
|
25347
|
+
window.olderCursor = cursor2;
|
|
25348
|
+
this.logger?.debug("codex history tail", {
|
|
25349
|
+
threadId,
|
|
25350
|
+
turnsFetched: fetched,
|
|
25351
|
+
loadedTurns: window.loaded.size,
|
|
25352
|
+
records: window.history.entries.length,
|
|
25353
|
+
older: window.olderCursor !== void 0
|
|
25354
|
+
});
|
|
25355
|
+
}
|
|
25356
|
+
async ensureOlder(threadId, window, beforeSeq, maxMessages) {
|
|
25357
|
+
if (beforeSeq === void 0) return;
|
|
25358
|
+
const needed = Math.min(maxMessages ?? HISTORY_PAGE_SIZES[0], HISTORY_PAGE_SIZES[0]);
|
|
25359
|
+
const available = () => window.history.entries.filter((entry) => entry.event.seq < beforeSeq && isSurfaceEvent(entry.event.type) && entry.event.surfaceOp === "append").length;
|
|
25360
|
+
let fetched = 0;
|
|
25361
|
+
for (let page = 0; page < MAX_PAGES; page += 1) {
|
|
25362
|
+
if (available() >= needed) break;
|
|
25363
|
+
if (window.olderCursor === void 0) break;
|
|
25364
|
+
const result = await this.turnPage(threadId, window.olderCursor, "desc", "full");
|
|
25365
|
+
fetched += result.turns.length;
|
|
25366
|
+
this.addTurns(window, result.turns);
|
|
25367
|
+
window.olderCursor = result.nextCursor;
|
|
25368
|
+
this.project(threadId, window);
|
|
25369
|
+
}
|
|
25370
|
+
if (fetched > 0) {
|
|
25371
|
+
this.logger?.debug("codex history older", {
|
|
25372
|
+
threadId,
|
|
25373
|
+
beforeSeq,
|
|
25374
|
+
needed,
|
|
25375
|
+
turnsFetched: fetched,
|
|
25376
|
+
loadedTurns: window.loaded.size
|
|
25377
|
+
});
|
|
25378
|
+
}
|
|
25379
|
+
}
|
|
25380
|
+
async appendNewTurns(threadId, cached, updatedAt) {
|
|
25381
|
+
if (cached.newestTurnId === void 0) return void 0;
|
|
25382
|
+
const newer = [];
|
|
25383
|
+
let cursor2;
|
|
25384
|
+
for (let page = 0; page < MAX_PAGES; page += 1) {
|
|
25385
|
+
const result = await this.turnPage(threadId, cursor2, "desc", "full");
|
|
25386
|
+
for (const rawTurn of result.turns) {
|
|
25387
|
+
if (typeof rawTurn.id === "string" && rawTurn.id === cached.newestTurnId) {
|
|
25388
|
+
if (newer.length === 0) {
|
|
25389
|
+
cached.updatedAt = updatedAt;
|
|
25390
|
+
cached.at = Date.now();
|
|
25391
|
+
return cached;
|
|
25392
|
+
}
|
|
25393
|
+
this.addTurns(cached, [...newer].reverse());
|
|
25394
|
+
this.project(threadId, cached);
|
|
25395
|
+
cached.updatedAt = updatedAt;
|
|
25396
|
+
cached.at = Date.now();
|
|
25397
|
+
return cached;
|
|
25398
|
+
}
|
|
25399
|
+
newer.push(rawTurn);
|
|
25400
|
+
}
|
|
25401
|
+
cursor2 = result.nextCursor;
|
|
25402
|
+
if (cursor2 === void 0) return void 0;
|
|
25403
|
+
}
|
|
25404
|
+
return void 0;
|
|
25405
|
+
}
|
|
25406
|
+
addTurns(window, turns) {
|
|
25407
|
+
for (const turn of turns) {
|
|
25408
|
+
const turnId = typeof turn.id === "string" ? turn.id : void 0;
|
|
25409
|
+
if (turnId === void 0) continue;
|
|
25410
|
+
if (!window.turnNumbers.has(turnId)) {
|
|
25411
|
+
window.totalTurns += 1;
|
|
25412
|
+
window.turnNumbers.set(turnId, window.totalTurns);
|
|
25413
|
+
window.turnStartSeq.set(turnId, window.totalEvents);
|
|
25414
|
+
window.totalEvents += projectCodexNativeHistory({ turns: [turn] }, window.history.header.id).entries.length;
|
|
25415
|
+
}
|
|
25416
|
+
window.loaded.set(turnId, turn);
|
|
25417
|
+
}
|
|
25418
|
+
}
|
|
25419
|
+
project(threadId, window) {
|
|
25420
|
+
const turns = [...window.loaded.entries()].sort((left, right) => (window.turnNumbers.get(left[0]) ?? 0) - (window.turnNumbers.get(right[0]) ?? 0)).map(([, turn]) => turn);
|
|
25421
|
+
const first = [...window.loaded.keys()].sort((left, right) => (window.turnNumbers.get(left) ?? 0) - (window.turnNumbers.get(right) ?? 0))[0];
|
|
25422
|
+
const startSeq = first === void 0 ? 0 : window.turnStartSeq.get(first) ?? 0;
|
|
25423
|
+
const history = projectCodexNativeHistory({ ...window.metadata, turns }, `codex:${threadId}`, "legacy", {
|
|
25424
|
+
seq: startSeq,
|
|
25425
|
+
turnNumbers: window.turnNumbers
|
|
25426
|
+
});
|
|
25427
|
+
history.nextTurn = window.totalTurns + 1;
|
|
25428
|
+
window.history = history;
|
|
25429
|
+
const newest = [...window.turnNumbers.entries()].sort((left, right) => right[1] - left[1])[0]?.[0];
|
|
25430
|
+
if (newest !== void 0) window.newestTurnId = newest;
|
|
25431
|
+
}
|
|
25432
|
+
async turnPage(threadId, cursor2, direction, itemsView) {
|
|
25433
|
+
const result = await this.call("thread/turns/list", {
|
|
25434
|
+
threadId,
|
|
25435
|
+
limit: PAGE_LIMIT,
|
|
25436
|
+
sortDirection: direction,
|
|
25437
|
+
itemsView,
|
|
25438
|
+
...cursor2 === void 0 ? {} : { cursor: cursor2 }
|
|
25439
|
+
});
|
|
25440
|
+
const page = typeof result === "object" && result !== null ? result : {};
|
|
25441
|
+
const turns = Array.isArray(page.data) ? page.data.filter((turn) => typeof turn === "object" && turn !== null && !Array.isArray(turn)) : [];
|
|
25442
|
+
const nextCursor = typeof page.nextCursor === "string" && page.nextCursor.length > 0 ? page.nextCursor : void 0;
|
|
25443
|
+
return { turns, nextCursor };
|
|
25444
|
+
}
|
|
25445
|
+
remember(threadId, window) {
|
|
25446
|
+
if (this.windows.size >= CACHE_LIMIT && !this.windows.has(threadId)) {
|
|
25447
|
+
const oldest = this.windows.keys().next().value;
|
|
25448
|
+
if (oldest !== void 0) this.windows.delete(oldest);
|
|
25449
|
+
}
|
|
25450
|
+
this.windows.set(threadId, window);
|
|
25451
|
+
}
|
|
25452
|
+
};
|
|
25453
|
+
function surfaceRecords(history) {
|
|
25454
|
+
return history.entries.filter((entry) => isSurfaceEvent(entry.event.type) && entry.event.surfaceOp === "append").length;
|
|
25455
|
+
}
|
|
25456
|
+
|
|
24991
25457
|
// src/codex/domain.ts
|
|
24992
25458
|
var APPROVAL_TTL_MS = 5 * 6e4;
|
|
24993
25459
|
var DEFAULT_RESTART_DELAYS_MS = [1e3, 2e3, 4e3, 8e3, 15e3];
|
|
@@ -24996,8 +25462,6 @@ var ACCOUNT_PROBE_TIMEOUT_MS = 6e4;
|
|
|
24996
25462
|
var CODEX_START_BUDGET_MS = 9e4;
|
|
24997
25463
|
var CODEX_PAGE_LIMIT2 = 100;
|
|
24998
25464
|
var MAX_CODEX_PAGES2 = 32;
|
|
24999
|
-
var CODEX_HISTORY_PAGE_LIMIT = 25;
|
|
25000
|
-
var MAX_CODEX_HISTORY_PAGES = 64;
|
|
25001
25465
|
var CODEX_DIRECTORY_ENTRY_LIMIT = 500;
|
|
25002
25466
|
var CodexRemoteDomain = class {
|
|
25003
25467
|
constructor(config, logger, createAppServer = (binary, targetLogger) => new CodexAppServerClient(binary, targetLogger), restartDelaysMs = DEFAULT_RESTART_DELAYS_MS) {
|
|
@@ -25005,6 +25469,7 @@ var CodexRemoteDomain = class {
|
|
|
25005
25469
|
this.createAppServer = createAppServer;
|
|
25006
25470
|
this.restartDelaysMs = restartDelaysMs;
|
|
25007
25471
|
this.config = config;
|
|
25472
|
+
this.historyWindows = new CodexHistoryWindows((method, params) => this.callUpstream(method, params), logger);
|
|
25008
25473
|
}
|
|
25009
25474
|
appServer;
|
|
25010
25475
|
unsubscribeInbound;
|
|
@@ -25014,6 +25479,8 @@ var CodexRemoteDomain = class {
|
|
|
25014
25479
|
turnOwners = /* @__PURE__ */ new Map();
|
|
25015
25480
|
approvals = /* @__PURE__ */ new Map();
|
|
25016
25481
|
permissionPresets = /* @__PURE__ */ new Map();
|
|
25482
|
+
/** Per-Thread turn index plus the tail-first full-item window. */
|
|
25483
|
+
historyWindows;
|
|
25017
25484
|
approvalExpiryTimer;
|
|
25018
25485
|
restartTimer;
|
|
25019
25486
|
restartAttempt = 0;
|
|
@@ -25115,14 +25582,16 @@ var CodexRemoteDomain = class {
|
|
|
25115
25582
|
}
|
|
25116
25583
|
if (call.method === "thread/list") {
|
|
25117
25584
|
const result = sanitizeThreadList(await this.callUpstream(call.method, call.params));
|
|
25585
|
+
const authority = await this.readWorkspaceAuthority(result.data);
|
|
25118
25586
|
const authorized = filterThreadListByWorkspaceAuthority(
|
|
25119
25587
|
result,
|
|
25120
|
-
|
|
25588
|
+
authority,
|
|
25589
|
+
this.config.showUnprojectedSessions !== false
|
|
25121
25590
|
);
|
|
25122
|
-
return {
|
|
25591
|
+
return rebaseThreadListCwds({
|
|
25123
25592
|
...authorized,
|
|
25124
25593
|
data: uniqueCodexThreadRows(authorized.data)
|
|
25125
|
-
};
|
|
25594
|
+
}, authority.roots);
|
|
25126
25595
|
}
|
|
25127
25596
|
if (call.method === "thread/start") {
|
|
25128
25597
|
const cwd = await this.requireCodexWorkspacePath(call.params.cwd);
|
|
@@ -25141,21 +25610,15 @@ var CodexRemoteDomain = class {
|
|
|
25141
25610
|
}
|
|
25142
25611
|
const threadId = threadIdFromParams(call.params);
|
|
25143
25612
|
if (call.method === "dsh/sessionHistory") {
|
|
25144
|
-
const thread = await this.
|
|
25145
|
-
const
|
|
25146
|
-
|
|
25147
|
-
|
|
25148
|
-
|
|
25149
|
-
|
|
25150
|
-
|
|
25151
|
-
|
|
25152
|
-
);
|
|
25153
|
-
const activeTurnId2 = typeof page.activeTurnId === "string" ? page.activeTurnId : this.turnOwners.get(threadId)?.turnId;
|
|
25154
|
-
return {
|
|
25155
|
-
...page,
|
|
25156
|
-
...activeTurnId2 === void 0 ? {} : { activeTurnId: activeTurnId2 },
|
|
25157
|
-
permissionPreset: this.permissionPresets.get(threadId) ?? null
|
|
25158
|
-
};
|
|
25613
|
+
const { thread: metadata } = await this.readKnownThread(threadId);
|
|
25614
|
+
const beforeSeq = optionalInteger2(call.params.beforeSeq);
|
|
25615
|
+
const maxMessages = optionalInteger2(call.params.maxMessages);
|
|
25616
|
+
const window = await this.historyWindows.window(threadId, metadata, beforeSeq, maxMessages);
|
|
25617
|
+
return this.historyPage(window, threadId, {
|
|
25618
|
+
beforeSeq,
|
|
25619
|
+
throughSeq: optionalInteger2(call.params.throughSeq),
|
|
25620
|
+
maxMessages
|
|
25621
|
+
});
|
|
25159
25622
|
}
|
|
25160
25623
|
if (call.method === "dsh/directoryList") {
|
|
25161
25624
|
return this.listCodexDirectory(call.params.path);
|
|
@@ -25658,103 +26121,60 @@ var CodexRemoteDomain = class {
|
|
|
25658
26121
|
return { thread: listed, ...typeof listed.cwd === "string" && listed.cwd.length > 0 ? { cwd: listed.cwd } : {} };
|
|
25659
26122
|
}
|
|
25660
26123
|
const thread = extractThread(result);
|
|
25661
|
-
if (thread === void 0 || thread.id !== threadId || !
|
|
26124
|
+
if (thread === void 0 || thread.id !== threadId || !this.threadAllowed(thread, authority)) {
|
|
25662
26125
|
throw new RpcError("CODEX_THREAD_NOT_ALLOWED", "The Codex thread is not available through this Remote Host.");
|
|
25663
26126
|
}
|
|
25664
26127
|
return { thread, ...typeof thread.cwd === "string" && thread.cwd.length > 0 ? { cwd: thread.cwd } : {} };
|
|
25665
26128
|
}
|
|
25666
|
-
|
|
25667
|
-
const
|
|
25668
|
-
|
|
25669
|
-
|
|
25670
|
-
|
|
25671
|
-
|
|
25672
|
-
|
|
25673
|
-
|
|
25674
|
-
|
|
25675
|
-
|
|
25676
|
-
|
|
25677
|
-
|
|
25678
|
-
|
|
25679
|
-
|
|
25680
|
-
|
|
25681
|
-
|
|
25682
|
-
|
|
25683
|
-
|
|
25684
|
-
|
|
25685
|
-
|
|
25686
|
-
|
|
25687
|
-
} catch (legacyError) {
|
|
25688
|
-
if (!isHistoryReadRecoverable(legacyError)) throw legacyError;
|
|
25689
|
-
this.logHistoryFallback(connectionId, "thread-read-full", legacyError);
|
|
25690
|
-
return { ...metadata, turns: [] };
|
|
25691
|
-
}
|
|
25692
|
-
}
|
|
25693
|
-
async readThreadTurns(connectionId, threadId, itemsView) {
|
|
25694
|
-
const turns = [];
|
|
25695
|
-
let cursor2;
|
|
25696
|
-
for (let page = 0; page < MAX_CODEX_HISTORY_PAGES; page += 1) {
|
|
25697
|
-
const result = await this.callUpstream("thread/turns/list", {
|
|
26129
|
+
historyPage(window, threadId, request) {
|
|
26130
|
+
const requested = request.maxMessages ?? HISTORY_PAGE_SIZES[0];
|
|
26131
|
+
const start = Math.max(1, Math.min(requested, HISTORY_PAGE_SIZES[0], window.acceptedPageSize ?? requested));
|
|
26132
|
+
const activeTurnId2 = this.turnOwners.get(threadId)?.turnId;
|
|
26133
|
+
for (const size of historyPageSizes(start)) {
|
|
26134
|
+
const page = paginateCodexNativeHistory(window.history, {
|
|
26135
|
+
beforeSeq: request.beforeSeq,
|
|
26136
|
+
throughSeq: request.throughSeq,
|
|
26137
|
+
maxMessages: size
|
|
26138
|
+
});
|
|
26139
|
+
const resolvedActiveTurnId = page.activeTurnId ?? activeTurnId2;
|
|
26140
|
+
const response = {
|
|
26141
|
+
...page,
|
|
26142
|
+
cursor: window.history.lastSeq,
|
|
26143
|
+
nextTurn: window.totalTurns + 1,
|
|
26144
|
+
hasMore: page.hasMore || window.olderCursor !== void 0,
|
|
26145
|
+
...resolvedActiveTurnId === void 0 ? {} : { activeTurnId: resolvedActiveTurnId },
|
|
26146
|
+
permissionPreset: this.permissionPresets.get(threadId) ?? null
|
|
26147
|
+
};
|
|
26148
|
+
const bytes = Buffer4.byteLength(JSON.stringify(response), "utf8");
|
|
26149
|
+
this.logger.debug("codex history page", {
|
|
25698
26150
|
threadId,
|
|
25699
|
-
|
|
25700
|
-
|
|
25701
|
-
|
|
25702
|
-
|
|
26151
|
+
requested,
|
|
26152
|
+
size,
|
|
26153
|
+
bytes,
|
|
26154
|
+
budget: HISTORY_BUDGET_BYTES,
|
|
26155
|
+
loadedTurns: window.loaded.size,
|
|
26156
|
+
totalTurns: window.totalTurns
|
|
25703
26157
|
});
|
|
25704
|
-
|
|
25705
|
-
|
|
25706
|
-
|
|
25707
|
-
const turnId = typeof rawTurn.id === "string" ? rawTurn.id : void 0;
|
|
25708
|
-
const items = rawTurn.itemsView === "full" || turnId === void 0 ? array2(rawTurn.items) : await this.readThreadItems(connectionId, threadId, turnId, array2(rawTurn.items));
|
|
25709
|
-
turns.push({ ...rawTurn, items });
|
|
26158
|
+
if (bytes <= HISTORY_BUDGET_BYTES) {
|
|
26159
|
+
window.acceptedPageSize = size;
|
|
26160
|
+
return response;
|
|
25710
26161
|
}
|
|
25711
|
-
cursor2 = typeof pageResult.nextCursor === "string" && pageResult.nextCursor.length > 0 ? pageResult.nextCursor : void 0;
|
|
25712
|
-
if (cursor2 === void 0) break;
|
|
25713
26162
|
}
|
|
25714
|
-
|
|
25715
|
-
}
|
|
25716
|
-
async readThreadItems(connectionId, threadId, turnId, fallbackItems) {
|
|
25717
|
-
const items = [];
|
|
25718
|
-
let cursor2;
|
|
25719
|
-
try {
|
|
25720
|
-
for (let page = 0; page < MAX_CODEX_HISTORY_PAGES; page += 1) {
|
|
25721
|
-
const result = await this.callUpstream("thread/items/list", {
|
|
25722
|
-
threadId,
|
|
25723
|
-
turnId,
|
|
25724
|
-
limit: CODEX_HISTORY_PAGE_LIMIT,
|
|
25725
|
-
sortDirection: "asc",
|
|
25726
|
-
...cursor2 === void 0 ? {} : { cursor: cursor2 }
|
|
25727
|
-
});
|
|
25728
|
-
const pageResult = isRecord12(result) ? result : {};
|
|
25729
|
-
for (const entry of array2(pageResult.data)) {
|
|
25730
|
-
if (isRecord12(entry) && entry.item !== void 0) items.push(entry.item);
|
|
25731
|
-
}
|
|
25732
|
-
cursor2 = typeof pageResult.nextCursor === "string" && pageResult.nextCursor.length > 0 ? pageResult.nextCursor : void 0;
|
|
25733
|
-
if (cursor2 === void 0) break;
|
|
25734
|
-
}
|
|
25735
|
-
} catch (error) {
|
|
25736
|
-
if (!isHistoryReadRecoverable(error)) throw error;
|
|
25737
|
-
this.logHistoryFallback(connectionId, "items", error);
|
|
25738
|
-
return fallbackItems;
|
|
25739
|
-
}
|
|
25740
|
-
return items;
|
|
25741
|
-
}
|
|
25742
|
-
logHistoryFallback(connectionId, stage, error) {
|
|
25743
|
-
this.logger.warn("Codex history read fallback", {
|
|
25744
|
-
connectionId: maskId(connectionId),
|
|
25745
|
-
stage,
|
|
25746
|
-
code: errorCode4(error)
|
|
25747
|
-
});
|
|
26163
|
+
throw new RpcError("RESPONSE_TOO_LARGE", "The Codex history page is too large for the secure Remote channel.");
|
|
25748
26164
|
}
|
|
25749
26165
|
async assertResultThreadAllowed(result) {
|
|
25750
26166
|
const thread = extractThread(result);
|
|
25751
26167
|
if (thread === void 0 || typeof thread.id !== "string") {
|
|
25752
26168
|
throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned an invalid thread.");
|
|
25753
26169
|
}
|
|
25754
|
-
if (!
|
|
26170
|
+
if (!this.threadAllowed(thread, await this.readWorkspaceAuthority())) {
|
|
25755
26171
|
throw new RpcError("CODEX_THREAD_NOT_ALLOWED", "The Codex thread is not available through this Remote Host.");
|
|
25756
26172
|
}
|
|
25757
26173
|
}
|
|
26174
|
+
/** The authority check, widened when unprojected Sessions are exposed. */
|
|
26175
|
+
threadAllowed(thread, authority) {
|
|
26176
|
+
return this.config.showUnprojectedSessions !== false || isThreadAllowedByWorkspaceAuthority(thread, authority);
|
|
26177
|
+
}
|
|
25758
26178
|
claimTurn(threadId, connectionId, method, turnId) {
|
|
25759
26179
|
const owner = this.turnOwners.get(threadId);
|
|
25760
26180
|
const peerDeviceId = this.peerDeviceIds.get(connectionId) ?? connectionId;
|
|
@@ -25895,7 +26315,7 @@ var CodexRemoteDomain = class {
|
|
|
25895
26315
|
}));
|
|
25896
26316
|
const thread = result.data.find((item) => item.id === threadId);
|
|
25897
26317
|
if (thread !== void 0) {
|
|
25898
|
-
return
|
|
26318
|
+
return this.threadAllowed(thread, authority) ? thread : void 0;
|
|
25899
26319
|
}
|
|
25900
26320
|
cursor2 = typeof result.nextCursor === "string" && result.nextCursor.length > 0 ? result.nextCursor : void 0;
|
|
25901
26321
|
if (cursor2 === void 0) break;
|
|
@@ -25906,6 +26326,14 @@ var CodexRemoteDomain = class {
|
|
|
25906
26326
|
if (!isAbsolute2(path)) {
|
|
25907
26327
|
throw new RpcError("CODEX_PATH_NOT_ALLOWED", "The CodeX working directory is not available as a Workspace.");
|
|
25908
26328
|
}
|
|
26329
|
+
if (this.config.showUnprojectedSessions !== false) {
|
|
26330
|
+
try {
|
|
26331
|
+
const candidate2 = await realpath(path);
|
|
26332
|
+
if ((await stat4(candidate2)).isDirectory()) return candidate2;
|
|
26333
|
+
} catch {
|
|
26334
|
+
}
|
|
26335
|
+
throw new RpcError("CODEX_PATH_NOT_ALLOWED", "The CodeX working directory is not available as a Workspace.");
|
|
26336
|
+
}
|
|
25909
26337
|
const paths = await this.listCodexWorkspacePaths();
|
|
25910
26338
|
const lexicalCandidate = resolve(path);
|
|
25911
26339
|
let candidate;
|
|
@@ -26006,7 +26434,7 @@ var CodexRemoteDomain = class {
|
|
|
26006
26434
|
return paths;
|
|
26007
26435
|
}
|
|
26008
26436
|
};
|
|
26009
|
-
function codexBinaryCandidates(configured, hostPlatform = process.platform, userHome =
|
|
26437
|
+
function codexBinaryCandidates(configured, hostPlatform = process.platform, userHome = homedir4(), localAppData = process.env.LOCALAPPDATA) {
|
|
26010
26438
|
if (configured !== "codex") return [configured];
|
|
26011
26439
|
if (hostPlatform === "win32") {
|
|
26012
26440
|
return [.../* @__PURE__ */ new Set([
|
|
@@ -26042,12 +26470,12 @@ function codexBinaryCandidates(configured, hostPlatform = process.platform, user
|
|
|
26042
26470
|
])];
|
|
26043
26471
|
}
|
|
26044
26472
|
function bundledWindowsCodex(userHome) {
|
|
26045
|
-
return bundledWindowsCodexAt(
|
|
26473
|
+
return bundledWindowsCodexAt(join9(userHome, "AppData", "Local"));
|
|
26046
26474
|
}
|
|
26047
26475
|
function bundledWindowsCodexAt(localAppData) {
|
|
26048
|
-
const bin =
|
|
26476
|
+
const bin = join9(localAppData, "OpenAI", "Codex", "bin");
|
|
26049
26477
|
try {
|
|
26050
|
-
const newest = readdirSync2(bin, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) =>
|
|
26478
|
+
const newest = readdirSync2(bin, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join9(bin, entry.name, "codex.exe")).filter((candidate2) => existsSync3(candidate2)).map((candidate2) => ({ candidate: candidate2, modified: statSync2(candidate2).mtimeMs })).sort((left, right) => right.modified - left.modified);
|
|
26051
26479
|
const candidate = newest[0]?.candidate;
|
|
26052
26480
|
return candidate === void 0 ? [] : [candidate];
|
|
26053
26481
|
} catch {
|
|
@@ -26120,13 +26548,30 @@ function sanitizeThreadList(result) {
|
|
|
26120
26548
|
...typeof result.backwardsCursor === "string" && result.backwardsCursor.length > 0 ? { backwardsCursor: result.backwardsCursor } : {}
|
|
26121
26549
|
};
|
|
26122
26550
|
}
|
|
26123
|
-
function filterThreadListByWorkspaceAuthority(result, authority) {
|
|
26551
|
+
function filterThreadListByWorkspaceAuthority(result, authority, includeUnprojected = false) {
|
|
26124
26552
|
if (!isRecord12(result) || !Array.isArray(result.data)) {
|
|
26125
26553
|
throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned an invalid thread list.");
|
|
26126
26554
|
}
|
|
26127
26555
|
return {
|
|
26128
26556
|
...result,
|
|
26129
|
-
data: result.data.map((record7) => isRecord12(record7) ? record7 : void 0).filter((thread) => thread !== void 0 && isThreadAllowedByWorkspaceAuthority(thread, authority))
|
|
26557
|
+
data: result.data.map((record7) => isRecord12(record7) ? record7 : void 0).filter((thread) => thread !== void 0 && (includeUnprojected || isThreadAllowedByWorkspaceAuthority(thread, authority)))
|
|
26558
|
+
};
|
|
26559
|
+
}
|
|
26560
|
+
function rebaseThreadListCwds(result, roots) {
|
|
26561
|
+
if (process.platform !== "win32" || roots.length === 0) return result;
|
|
26562
|
+
const ordered = [...roots].sort((left, right) => right.length - left.length);
|
|
26563
|
+
return {
|
|
26564
|
+
...result,
|
|
26565
|
+
data: result.data.map((thread) => {
|
|
26566
|
+
const cwd = typeof thread.cwd === "string" ? thread.cwd : void 0;
|
|
26567
|
+
if (cwd === void 0) return thread;
|
|
26568
|
+
const root = ordered.find((candidate) => containsCodexPath(candidate, cwd));
|
|
26569
|
+
if (root === void 0) return thread;
|
|
26570
|
+
const normalizedRoot = normalizeCodexPathForCompare(root);
|
|
26571
|
+
const normalizedCwd = normalizeCodexPathForCompare(cwd);
|
|
26572
|
+
const rebased = normalizedCwd === normalizedRoot ? root : normalizedCwd.startsWith(`${normalizedRoot}/`) || normalizedCwd.startsWith(`${normalizedRoot}\\`) ? `${root}${cwd.slice(root.length)}` : cwd;
|
|
26573
|
+
return rebased === cwd ? thread : { ...thread, cwd: rebased };
|
|
26574
|
+
})
|
|
26130
26575
|
};
|
|
26131
26576
|
}
|
|
26132
26577
|
function sanitizeProject(value) {
|
|
@@ -26283,12 +26728,6 @@ function canTryNextBinary(error) {
|
|
|
26283
26728
|
function isRecord12(value) {
|
|
26284
26729
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
26285
26730
|
}
|
|
26286
|
-
function array2(value) {
|
|
26287
|
-
return Array.isArray(value) ? value : [];
|
|
26288
|
-
}
|
|
26289
|
-
function maskId(value) {
|
|
26290
|
-
return value.length <= 12 ? value : `${value.slice(0, 8)}\u2026${value.slice(-4)}`;
|
|
26291
|
-
}
|
|
26292
26731
|
function isActiveWriterMessage(message) {
|
|
26293
26732
|
return message.toLowerCase().includes("active writer");
|
|
26294
26733
|
}
|
|
@@ -27402,12 +27841,12 @@ function isOversizedListing(value) {
|
|
|
27402
27841
|
|
|
27403
27842
|
// src/remote-directory-browser.ts
|
|
27404
27843
|
import { readdir as readdir3, stat as stat5 } from "node:fs/promises";
|
|
27405
|
-
import { homedir as
|
|
27844
|
+
import { homedir as homedir5, platform as platform2 } from "node:os";
|
|
27406
27845
|
import { basename as basename3, dirname as dirname5, isAbsolute as isAbsolute3, parse, resolve as resolve2 } from "node:path";
|
|
27407
27846
|
var MAX_ENTRIES = 500;
|
|
27408
27847
|
async function listRemoteDirectory(path, signal) {
|
|
27409
27848
|
signal?.throwIfAborted();
|
|
27410
|
-
const home = resolve2(
|
|
27849
|
+
const home = resolve2(homedir5());
|
|
27411
27850
|
const target2 = path === void 0 || path.trim() === "" ? home : resolve2(path);
|
|
27412
27851
|
if (!isAbsolute3(target2)) throw new Error("The remote directory path must be absolute.");
|
|
27413
27852
|
const rows = await readdir3(target2, { withFileTypes: true });
|
|
@@ -28233,7 +28672,7 @@ function frameSessionId(frame) {
|
|
|
28233
28672
|
return typeof payload.sessionId === "string" && payload.sessionId.length > 0 ? payload.sessionId : void 0;
|
|
28234
28673
|
}
|
|
28235
28674
|
function withTimeout(promise, ms, message) {
|
|
28236
|
-
return new Promise((
|
|
28675
|
+
return new Promise((resolve5, reject) => {
|
|
28237
28676
|
const timer = setTimeout(() => {
|
|
28238
28677
|
reject(new RpcError("TIMEOUT", message, void 0, true));
|
|
28239
28678
|
}, ms);
|
|
@@ -28241,7 +28680,7 @@ function withTimeout(promise, ms, message) {
|
|
|
28241
28680
|
promise.then(
|
|
28242
28681
|
(value) => {
|
|
28243
28682
|
clearTimeout(timer);
|
|
28244
|
-
|
|
28683
|
+
resolve5(value);
|
|
28245
28684
|
},
|
|
28246
28685
|
(error) => {
|
|
28247
28686
|
clearTimeout(timer);
|
|
@@ -28271,7 +28710,8 @@ function concatChunks2(chunks, totalBytes) {
|
|
|
28271
28710
|
}
|
|
28272
28711
|
|
|
28273
28712
|
// src/harness-remote-bridge.ts
|
|
28274
|
-
import { Buffer as
|
|
28713
|
+
import { Buffer as Buffer5 } from "node:buffer";
|
|
28714
|
+
import { isAbsolute as isAbsolute4, join as join10, relative as relative2, resolve as resolve3, sep } from "node:path";
|
|
28275
28715
|
var endpointSchema = external_exports.string().min(1).max(128).regex(/^(?:\$events(?:\/result)?|[A-Za-z0-9_$.-]+\/[A-Za-z0-9_$.-]+)$/);
|
|
28276
28716
|
var callSchema3 = external_exports.object({ endpoint: endpointSchema, payload: external_exports.unknown() }).strict();
|
|
28277
28717
|
var streamOpenSchema3 = external_exports.object({
|
|
@@ -28320,9 +28760,8 @@ var MAX_ACTIVE_STREAMS2 = MAX_ALPHA_STREAMS_PER_CONNECTION;
|
|
|
28320
28760
|
var MAX_ACTIVE_TRANSFERS2 = MAX_ACTIVE_TRANSFERS_PER_DIRECTION;
|
|
28321
28761
|
var INLINE_TRANSFER_RESPONSE_BYTES3 = 2 * 1024 * 1024;
|
|
28322
28762
|
var STREAM_FRAME_ENVELOPE_BYTES = 4 * 1024;
|
|
28323
|
-
var
|
|
28324
|
-
var
|
|
28325
|
-
var MAX_HISTORY_PAGE_MESSAGES = 500;
|
|
28763
|
+
var RESPONSE_ENVELOPE_BYTES2 = 8 * 1024;
|
|
28764
|
+
var WORKSPACE_FILE_ENDPOINTS = /* @__PURE__ */ new Set(["workspaceFiles/stat", "workspaceFiles/read", "workspaceFiles/readBytes", "workspaceFiles/list"]);
|
|
28326
28765
|
var REMOTE_HISTORY_CALL_ENDPOINTS = /* @__PURE__ */ new Set(["session/page", "session/history"]);
|
|
28327
28766
|
var REMOTE_HISTORY_STREAM_ENDPOINTS = /* @__PURE__ */ new Set(["session/follow"]);
|
|
28328
28767
|
var MAX_CACHED_HISTORY_PAGES = 64;
|
|
@@ -28345,6 +28784,9 @@ var HARNESS_REMOTE_ALLOWLIST = [
|
|
|
28345
28784
|
"goals/edit",
|
|
28346
28785
|
"goals/pause",
|
|
28347
28786
|
"goals/resume",
|
|
28787
|
+
"job/follow",
|
|
28788
|
+
"job/kill",
|
|
28789
|
+
"job/list",
|
|
28348
28790
|
"llm/discoverModels",
|
|
28349
28791
|
"llm/listConfigurableProviders",
|
|
28350
28792
|
"llm/listProviders",
|
|
@@ -28403,19 +28845,22 @@ var HARNESS_REMOTE_ALLOWLIST = [
|
|
|
28403
28845
|
];
|
|
28404
28846
|
var allowedEndpoints = new Set(HARNESS_REMOTE_ALLOWLIST);
|
|
28405
28847
|
var HarnessRemoteBridge = class {
|
|
28406
|
-
constructor(gateway, publish, logger, harnessVersion, terminal = new TerminalPolicy(() => false, "", /* @__PURE__ */ new Map()), codexWorkspace) {
|
|
28848
|
+
constructor(gateway, publish, logger, harnessVersion, terminal = new TerminalPolicy(() => false, "", /* @__PURE__ */ new Map()), codexWorkspace, sessionHeaderCwd) {
|
|
28407
28849
|
this.gateway = gateway;
|
|
28408
28850
|
this.publish = publish;
|
|
28409
28851
|
this.logger = logger;
|
|
28410
28852
|
this.harnessVersion = harnessVersion;
|
|
28411
28853
|
this.terminal = terminal;
|
|
28412
28854
|
this.codexWorkspace = codexWorkspace;
|
|
28855
|
+
this.sessionHeaderCwd = sessionHeaderCwd;
|
|
28413
28856
|
}
|
|
28414
28857
|
streams = /* @__PURE__ */ new Map();
|
|
28415
28858
|
incomingTransfers = /* @__PURE__ */ new Map();
|
|
28416
28859
|
outgoingTransfers = /* @__PURE__ */ new Map();
|
|
28417
28860
|
/** Last page size that fit one secure message, per session, so probing is paid once. */
|
|
28418
28861
|
historyPageSizes = /* @__PURE__ */ new Map();
|
|
28862
|
+
/** Last working directory read per Session, so a file open does not re-project on every chunk. */
|
|
28863
|
+
sessionWorkingDirectories = /* @__PURE__ */ new Map();
|
|
28419
28864
|
/**
|
|
28420
28865
|
* Answer one remote Gateway call.
|
|
28421
28866
|
*
|
|
@@ -28456,8 +28901,12 @@ var HarnessRemoteBridge = class {
|
|
|
28456
28901
|
const signal = AbortSignal.timeout(6e4);
|
|
28457
28902
|
try {
|
|
28458
28903
|
const nativePayload = normalizeWorkspaceRequestPayload(params.endpoint, params.payload, this.harnessVersion);
|
|
28459
|
-
const nativeResult = REMOTE_HISTORY_CALL_ENDPOINTS.has(params.endpoint) ? await this.dispatchHistoryPage(params.endpoint, nativePayload, signal) : params.endpoint === "commands/execute" ? await dispatchCommandForHost(this.gateway, params.payload, signal, this.harnessVersion) : await this.
|
|
28904
|
+
const nativeResult = REMOTE_HISTORY_CALL_ENDPOINTS.has(params.endpoint) ? await this.dispatchHistoryPage(params.endpoint, nativePayload, signal) : params.endpoint === "commands/execute" ? await dispatchCommandForHost(this.gateway, params.payload, signal, this.harnessVersion) : await this.dispatchWorkspaceFile(params.endpoint, nativePayload, signal);
|
|
28460
28905
|
const result = params.endpoint === "directoryPicker/list" && needsDirectoryFallback(nativeResult) ? await this.directoryList(params.payload, signal) : nativeResult;
|
|
28906
|
+
if (params.endpoint === "workspace/create" && isInvalidWorkspacePath(result)) {
|
|
28907
|
+
const recovered = await this.recoverWorkspaceCreate(params.payload, signal);
|
|
28908
|
+
if (recovered !== void 0) return recovered;
|
|
28909
|
+
}
|
|
28461
28910
|
this.logger?.debug("harness remote call ok", {
|
|
28462
28911
|
endpoint: params.endpoint,
|
|
28463
28912
|
durationMs: Math.round(performance.now() - startedAt)
|
|
@@ -28465,6 +28914,10 @@ var HarnessRemoteBridge = class {
|
|
|
28465
28914
|
return reservation === void 0 ? result : this.terminal.result(params.endpoint, params.payload, result, reservation);
|
|
28466
28915
|
} catch (error) {
|
|
28467
28916
|
if (reservation !== void 0) this.terminal.result(params.endpoint, params.payload, { ok: false, error: { code: "FAILED", message: "", details: {} } }, reservation);
|
|
28917
|
+
if (params.endpoint === "workspace/create" && isInvalidWorkspacePath(error)) {
|
|
28918
|
+
const recovered = await this.recoverWorkspaceCreate(params.payload, signal);
|
|
28919
|
+
if (recovered !== void 0) return recovered;
|
|
28920
|
+
}
|
|
28468
28921
|
if (params.endpoint === "directoryPicker/list") {
|
|
28469
28922
|
const result = await this.directoryList(params.payload, signal);
|
|
28470
28923
|
this.logger?.debug("harness remote call ok", {
|
|
@@ -28490,6 +28943,43 @@ var HarnessRemoteBridge = class {
|
|
|
28490
28943
|
const params = directoryListSchema2.parse(args);
|
|
28491
28944
|
return { ok: true, value: await listRemoteDirectory(params.path, signal) };
|
|
28492
28945
|
}
|
|
28946
|
+
/**
|
|
28947
|
+
* Resolve an existing Workspace when `workspace/create` cannot realpath a
|
|
28948
|
+
* deleted directory.
|
|
28949
|
+
*
|
|
28950
|
+
* DSH keeps a registered Workspace open after its directory disappears, but
|
|
28951
|
+
* `workspace/create` canonicalizes the path first and fails with ENOENT. The
|
|
28952
|
+
* Client can still open the Workspace it sees in the Host's list; resolve that
|
|
28953
|
+
* list by lexical path and return the existing row instead of failing.
|
|
28954
|
+
* @param payload - workspace/create request payload.
|
|
28955
|
+
* @param signal - call lifetime.
|
|
28956
|
+
* @returns an existing-workspace result, or undefined when none matches.
|
|
28957
|
+
*/
|
|
28958
|
+
async recoverWorkspaceCreate(payload, signal) {
|
|
28959
|
+
const path = requestArgs(payload).path;
|
|
28960
|
+
if (typeof path !== "string" || path === "") return void 0;
|
|
28961
|
+
let source;
|
|
28962
|
+
try {
|
|
28963
|
+
source = await this.gateway.open("workspace/follow", { args: {} }, signal);
|
|
28964
|
+
} catch {
|
|
28965
|
+
return void 0;
|
|
28966
|
+
}
|
|
28967
|
+
const iterator = source[Symbol.asyncIterator]();
|
|
28968
|
+
let first;
|
|
28969
|
+
try {
|
|
28970
|
+
first = await iterator.next();
|
|
28971
|
+
} catch {
|
|
28972
|
+
return void 0;
|
|
28973
|
+
} finally {
|
|
28974
|
+
await iterator.return?.().catch(() => void 0);
|
|
28975
|
+
}
|
|
28976
|
+
const frame = isRecord14(first.value) ? first.value : {};
|
|
28977
|
+
const baseline = isRecord14(frame.value) ? frame.value : frame;
|
|
28978
|
+
const items = Array.isArray(baseline.items) ? baseline.items : [];
|
|
28979
|
+
const target2 = workspacePathIdentity(path);
|
|
28980
|
+
const workspace = items.find((item) => isRecord14(item) && typeof item.path === "string" && workspacePathIdentity(item.path) === target2);
|
|
28981
|
+
return workspace === void 0 ? void 0 : { ok: true, value: { workspace, created: false } };
|
|
28982
|
+
}
|
|
28493
28983
|
openTransfer(input2) {
|
|
28494
28984
|
this.pruneTransfers();
|
|
28495
28985
|
const params = transferOpenSchema3.parse(input2);
|
|
@@ -28583,7 +29073,7 @@ var HarnessRemoteBridge = class {
|
|
|
28583
29073
|
return {
|
|
28584
29074
|
transferId: params.transferId,
|
|
28585
29075
|
index: params.index,
|
|
28586
|
-
data:
|
|
29076
|
+
data: Buffer5.from(transfer.bytes.subarray(start, end)).toString("base64")
|
|
28587
29077
|
};
|
|
28588
29078
|
}
|
|
28589
29079
|
closeTransfer(input2) {
|
|
@@ -28703,7 +29193,7 @@ var HarnessRemoteBridge = class {
|
|
|
28703
29193
|
hasMore: value?.hasMore,
|
|
28704
29194
|
bytes
|
|
28705
29195
|
});
|
|
28706
|
-
if (bytes <= MAX_SECURE_MESSAGE_BYTES -
|
|
29196
|
+
if (bytes <= MAX_SECURE_MESSAGE_BYTES - RESPONSE_ENVELOPE_BYTES2) {
|
|
28707
29197
|
this.rememberHistoryPageSize(key, size);
|
|
28708
29198
|
return wire;
|
|
28709
29199
|
}
|
|
@@ -28722,7 +29212,7 @@ var HarnessRemoteBridge = class {
|
|
|
28722
29212
|
const key = historyPageKey(payload);
|
|
28723
29213
|
const requested = requestedMaxMessages(payload) ?? HISTORY_PAGE_SIZES[0];
|
|
28724
29214
|
const remembered = this.historyPageSizes.get(key);
|
|
28725
|
-
const start = Math.max(1, Math.min(requested,
|
|
29215
|
+
const start = Math.max(1, Math.min(requested, HISTORY_PAGE_SIZES[0], remembered ?? requested));
|
|
28726
29216
|
this.logger?.debug("history window", { key: key.slice(0, 120), requested, remembered, start });
|
|
28727
29217
|
return { key, sizes: historyPageSizes(start) };
|
|
28728
29218
|
}
|
|
@@ -28733,6 +29223,68 @@ var HarnessRemoteBridge = class {
|
|
|
28733
29223
|
}
|
|
28734
29224
|
this.historyPageSizes.set(key, size);
|
|
28735
29225
|
}
|
|
29226
|
+
/**
|
|
29227
|
+
* Dispatch a workspace-file call, re-rooting a path written against the Session's current
|
|
29228
|
+
* directory onto the header root the Host resolves against.
|
|
29229
|
+
*
|
|
29230
|
+
* DSH names the workspace root from the Session header, so a Session that changed its working
|
|
29231
|
+
* directory mid-run resolves relative links (and deliverables built from the root) against the
|
|
29232
|
+
* stale header. The live `workingDirectory` projection is the correction; when the re-rooted
|
|
29233
|
+
* path misses, the original path is tried so a file created before the change still opens.
|
|
29234
|
+
*/
|
|
29235
|
+
async dispatchWorkspaceFile(endpoint, payload, signal) {
|
|
29236
|
+
if (!WORKSPACE_FILE_ENDPOINTS.has(endpoint) || this.sessionHeaderCwd === void 0) {
|
|
29237
|
+
return this.gateway.dispatch(endpoint, payload, signal);
|
|
29238
|
+
}
|
|
29239
|
+
const location = workspaceFileLocation(payload);
|
|
29240
|
+
if (location === void 0) return this.gateway.dispatch(endpoint, payload, signal);
|
|
29241
|
+
const headerCwd = this.sessionHeaderCwd(location.sessionId);
|
|
29242
|
+
if (headerCwd === void 0) return this.gateway.dispatch(endpoint, payload, signal);
|
|
29243
|
+
const liveCwd = await this.sessionWorkingDirectory(location.sessionId, signal);
|
|
29244
|
+
if (liveCwd === void 0) return this.gateway.dispatch(endpoint, payload, signal);
|
|
29245
|
+
const nextPath = rerootWorkspacePath(headerCwd, liveCwd, location.path);
|
|
29246
|
+
if (nextPath === void 0 || nextPath === location.path) return this.gateway.dispatch(endpoint, payload, signal);
|
|
29247
|
+
const rewritten = replaceWorkspaceFilePath(payload, nextPath);
|
|
29248
|
+
if (rewritten === payload) return this.gateway.dispatch(endpoint, payload, signal);
|
|
29249
|
+
const rerooted = await this.gateway.dispatch(endpoint, rewritten, signal);
|
|
29250
|
+
if (!workspaceFileMiss(endpoint, rerooted)) {
|
|
29251
|
+
this.logger?.debug("workspace file path re-rooted", {
|
|
29252
|
+
endpoint,
|
|
29253
|
+
headerCwd,
|
|
29254
|
+
liveCwd,
|
|
29255
|
+
path: location.path,
|
|
29256
|
+
nextPath
|
|
29257
|
+
});
|
|
29258
|
+
return rerooted;
|
|
29259
|
+
}
|
|
29260
|
+
this.logger?.debug("workspace file path re-root missed; using the original path", {
|
|
29261
|
+
endpoint,
|
|
29262
|
+
path: location.path,
|
|
29263
|
+
nextPath
|
|
29264
|
+
});
|
|
29265
|
+
return this.gateway.dispatch(endpoint, payload, signal);
|
|
29266
|
+
}
|
|
29267
|
+
/** Read the Session's live working directory from its projections, cached briefly. */
|
|
29268
|
+
async sessionWorkingDirectory(sessionId, signal) {
|
|
29269
|
+
const cached = this.sessionWorkingDirectories.get(sessionId);
|
|
29270
|
+
if (cached !== void 0 && Date.now() - cached.at < 5e3) return cached.cwd;
|
|
29271
|
+
let cwd;
|
|
29272
|
+
try {
|
|
29273
|
+
const result = await this.gateway.dispatch("session/projections", { args: { request: { sessionId } } }, signal);
|
|
29274
|
+
const value = result.ok ? result.value : void 0;
|
|
29275
|
+
const values = isRecord14(value) && isRecord14(value.values) ? value.values : void 0;
|
|
29276
|
+
const raw = values?.workingDirectory;
|
|
29277
|
+
if (typeof raw === "string" && raw.length > 0) cwd = raw;
|
|
29278
|
+
} catch {
|
|
29279
|
+
cwd = void 0;
|
|
29280
|
+
}
|
|
29281
|
+
if (this.sessionWorkingDirectories.size >= MAX_CACHED_HISTORY_PAGES && !this.sessionWorkingDirectories.has(sessionId)) {
|
|
29282
|
+
const oldest = this.sessionWorkingDirectories.keys().next().value;
|
|
29283
|
+
if (oldest !== void 0) this.sessionWorkingDirectories.delete(oldest);
|
|
29284
|
+
}
|
|
29285
|
+
this.sessionWorkingDirectories.set(sessionId, { cwd, at: Date.now() });
|
|
29286
|
+
return cwd;
|
|
29287
|
+
}
|
|
28736
29288
|
closeStream(input2) {
|
|
28737
29289
|
const params = streamCloseSchema3.parse(input2);
|
|
28738
29290
|
const active = this.streams.get(params.streamId);
|
|
@@ -28821,11 +29373,6 @@ function windowedRequest(request, maxMessages) {
|
|
|
28821
29373
|
}
|
|
28822
29374
|
return next;
|
|
28823
29375
|
}
|
|
28824
|
-
function historyPageSizes(start) {
|
|
28825
|
-
const sizes = [start];
|
|
28826
|
-
for (const size of HISTORY_PAGE_SIZES) if (size < start) sizes.push(size);
|
|
28827
|
-
return sizes;
|
|
28828
|
-
}
|
|
28829
29376
|
function historyRequestOf(payload) {
|
|
28830
29377
|
let node = payload;
|
|
28831
29378
|
for (let depth = 0; depth < 4; depth += 1) {
|
|
@@ -28850,6 +29397,64 @@ function requestedMaxMessages(payload) {
|
|
|
28850
29397
|
const value = historyRequestOf(payload)?.maxMessages;
|
|
28851
29398
|
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : void 0;
|
|
28852
29399
|
}
|
|
29400
|
+
function workspaceFileLocation(payload) {
|
|
29401
|
+
if (!isRecord14(payload)) return void 0;
|
|
29402
|
+
if (Array.isArray(payload.args)) {
|
|
29403
|
+
if (payload.args.length >= 2 && typeof payload.args[0] === "string" && typeof payload.args[1] === "string") {
|
|
29404
|
+
return { sessionId: payload.args[0], path: payload.args[1] };
|
|
29405
|
+
}
|
|
29406
|
+
const first = payload.args[0];
|
|
29407
|
+
if (isRecord14(first)) return workspaceFileLocation(first);
|
|
29408
|
+
return void 0;
|
|
29409
|
+
}
|
|
29410
|
+
const candidate = isRecord14(payload.args) ? payload.args : isRecord14(payload.request) ? payload.request : payload;
|
|
29411
|
+
const sessionId = [candidate.workspaceFileScopeId, candidate.workspaceFileScope, candidate.sessionId].find((value) => typeof value === "string" && value.length > 0);
|
|
29412
|
+
const path = candidate.path;
|
|
29413
|
+
if (typeof sessionId !== "string" || typeof path !== "string" || path.length === 0) return void 0;
|
|
29414
|
+
return { sessionId, path };
|
|
29415
|
+
}
|
|
29416
|
+
function replaceWorkspaceFilePath(payload, nextPath) {
|
|
29417
|
+
if (!isRecord14(payload)) return payload;
|
|
29418
|
+
if (Array.isArray(payload.args)) {
|
|
29419
|
+
if (payload.args.length >= 2 && typeof payload.args[0] === "string" && typeof payload.args[1] === "string") {
|
|
29420
|
+
const args = [...payload.args];
|
|
29421
|
+
args[1] = nextPath;
|
|
29422
|
+
return { ...payload, args };
|
|
29423
|
+
}
|
|
29424
|
+
const first = payload.args[0];
|
|
29425
|
+
if (!isRecord14(first)) return payload;
|
|
29426
|
+
return { ...payload, args: [{ ...first, path: nextPath }, ...payload.args.slice(1)] };
|
|
29427
|
+
}
|
|
29428
|
+
if (isRecord14(payload.args)) return { ...payload, args: { ...payload.args, path: nextPath } };
|
|
29429
|
+
if (isRecord14(payload.request)) return { ...payload, request: { ...payload.request, path: nextPath } };
|
|
29430
|
+
return { ...payload, path: nextPath };
|
|
29431
|
+
}
|
|
29432
|
+
function workspaceFileMiss(endpoint, result) {
|
|
29433
|
+
if (!result.ok) return true;
|
|
29434
|
+
if (endpoint !== "workspaceFiles/stat") return false;
|
|
29435
|
+
const value = result.value;
|
|
29436
|
+
return isRecord14(value) && value.exists === false;
|
|
29437
|
+
}
|
|
29438
|
+
function rerootWorkspacePath(headerCwd, liveCwd, target2) {
|
|
29439
|
+
if (samePath(headerCwd, liveCwd)) return void 0;
|
|
29440
|
+
if (isAbsolute4(target2)) {
|
|
29441
|
+
const suffix = relative2(headerCwd, target2);
|
|
29442
|
+
if (suffix === "" || suffix === "." || isAbsolute4(suffix) || suffix.startsWith(`..${sep}`) || suffix === "..") return void 0;
|
|
29443
|
+
return join10(liveCwd, suffix);
|
|
29444
|
+
}
|
|
29445
|
+
const prefix = relative2(headerCwd, liveCwd);
|
|
29446
|
+
if (prefix !== "" && prefix !== "." && !isAbsolute4(prefix) && !prefix.startsWith(`..${sep}`) && prefix !== "..") {
|
|
29447
|
+
return join10(prefix, target2);
|
|
29448
|
+
}
|
|
29449
|
+
return join10(liveCwd, target2);
|
|
29450
|
+
}
|
|
29451
|
+
function samePath(left, right) {
|
|
29452
|
+
const normalize = (value) => {
|
|
29453
|
+
const trimmed = value.replace(/[\\/]+$/u, "");
|
|
29454
|
+
return process.platform === "win32" ? trimmed.toLowerCase() : trimmed;
|
|
29455
|
+
};
|
|
29456
|
+
return normalize(left) === normalize(right);
|
|
29457
|
+
}
|
|
28853
29458
|
function historyPageKey(payload) {
|
|
28854
29459
|
const request = historyRequestOf(payload);
|
|
28855
29460
|
if (request === void 0) return "default";
|
|
@@ -28862,10 +29467,10 @@ function historyPageKey(payload) {
|
|
|
28862
29467
|
}
|
|
28863
29468
|
}
|
|
28864
29469
|
function responseBytes(result) {
|
|
28865
|
-
return
|
|
29470
|
+
return Buffer5.byteLength(JSON.stringify(result), "utf8");
|
|
28866
29471
|
}
|
|
28867
29472
|
function streamFrameBytes(frame) {
|
|
28868
|
-
return
|
|
29473
|
+
return Buffer5.byteLength(JSON.stringify(frame), "utf8");
|
|
28869
29474
|
}
|
|
28870
29475
|
function remainingIterable(iterator) {
|
|
28871
29476
|
return { [Symbol.asyncIterator]: () => iterator };
|
|
@@ -28925,7 +29530,7 @@ async function dispatchCommandForHost(gateway, payload, signal, harnessVersion)
|
|
|
28925
29530
|
);
|
|
28926
29531
|
}
|
|
28927
29532
|
function decodeCanonicalBase643(value) {
|
|
28928
|
-
const bytes =
|
|
29533
|
+
const bytes = Buffer5.from(value, "base64");
|
|
28929
29534
|
if (bytes.toString("base64") !== value) throw new RpcError("INVALID_MESSAGE", "The Harness Remote transfer chunk is invalid.");
|
|
28930
29535
|
return bytes;
|
|
28931
29536
|
}
|
|
@@ -28955,6 +29560,17 @@ function record6(value) {
|
|
|
28955
29560
|
function isRecord14(value) {
|
|
28956
29561
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
28957
29562
|
}
|
|
29563
|
+
function workspacePathIdentity(path) {
|
|
29564
|
+
const normalized = resolve3(path).replace(/[\\/]+$/, "");
|
|
29565
|
+
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
29566
|
+
}
|
|
29567
|
+
function isInvalidWorkspacePath(input2) {
|
|
29568
|
+
const error = isRecord14(input2) && input2.ok === false && isRecord14(input2.error) ? input2.error : input2;
|
|
29569
|
+
if (!isRecord14(error)) return false;
|
|
29570
|
+
const code = typeof error.code === "string" ? error.code : "";
|
|
29571
|
+
const message = typeof error.message === "string" ? error.message : "";
|
|
29572
|
+
return code === "workspace/invalid-path" || message.includes("cannot create a Workspace at") && message.includes("ENOENT");
|
|
29573
|
+
}
|
|
28958
29574
|
|
|
28959
29575
|
// src/acp.ts
|
|
28960
29576
|
import { spawn as spawn3 } from "node:child_process";
|
|
@@ -29044,7 +29660,7 @@ var StdioAcpAdapter = class {
|
|
|
29044
29660
|
const c = this.ensure();
|
|
29045
29661
|
const id4 = this.nextId++;
|
|
29046
29662
|
c.stdin.write(JSON.stringify({ jsonrpc: "2.0", id: id4, method, params }) + "\n");
|
|
29047
|
-
return new Promise((
|
|
29663
|
+
return new Promise((resolve5, reject) => {
|
|
29048
29664
|
let buf = "";
|
|
29049
29665
|
const onData = async (d) => {
|
|
29050
29666
|
buf += d;
|
|
@@ -29055,7 +29671,7 @@ var StdioAcpAdapter = class {
|
|
|
29055
29671
|
const m = JSON.parse(l);
|
|
29056
29672
|
if (m.id === id4) {
|
|
29057
29673
|
c.stdout.off("data", onData);
|
|
29058
|
-
m.error ? reject(new Error(m.error.message ?? "ACP error")) :
|
|
29674
|
+
m.error ? reject(new Error(m.error.message ?? "ACP error")) : resolve5(m.result);
|
|
29059
29675
|
} else if (m.method && onNotification) await onNotification(m.params);
|
|
29060
29676
|
} catch {
|
|
29061
29677
|
}
|
|
@@ -29073,7 +29689,7 @@ import { execFileSync as execFileSync2 } from "node:child_process";
|
|
|
29073
29689
|
// src/codex-workspace-bridge.ts
|
|
29074
29690
|
import { spawn as spawn4 } from "node:child_process";
|
|
29075
29691
|
import { realpath as realpath2, lstat, readdir as readdir4, readFile as readFile5, stat as stat6, watch } from "node:fs/promises";
|
|
29076
|
-
import { isAbsolute as
|
|
29692
|
+
import { isAbsolute as isAbsolute5, join as join11, relative as relative3, resolve as resolve4 } from "node:path";
|
|
29077
29693
|
var MAX_READ_BYTES = 4 * 1024 * 1024;
|
|
29078
29694
|
var MAX_INPUT_BYTES = 64 * 1024;
|
|
29079
29695
|
var MAX_COLS = 240;
|
|
@@ -29104,7 +29720,7 @@ var AsyncQueue = class {
|
|
|
29104
29720
|
const value = this.values.shift();
|
|
29105
29721
|
if (value !== void 0) return Promise.resolve({ done: false, value });
|
|
29106
29722
|
if (this.ended) return Promise.resolve({ done: true, value: void 0 });
|
|
29107
|
-
return new Promise((
|
|
29723
|
+
return new Promise((resolve5) => this.waiters.push(resolve5));
|
|
29108
29724
|
}
|
|
29109
29725
|
[Symbol.asyncIterator]() {
|
|
29110
29726
|
return this;
|
|
@@ -29160,7 +29776,7 @@ var CodexWorkspaceBridge = class {
|
|
|
29160
29776
|
const entries = await readdir4(target2, { withFileTypes: true });
|
|
29161
29777
|
const result = [];
|
|
29162
29778
|
for (const entry of entries.slice(0, 500)) {
|
|
29163
|
-
const item =
|
|
29779
|
+
const item = join11(target2, entry.name);
|
|
29164
29780
|
const info2 = await lstat(item);
|
|
29165
29781
|
if (info2.isSymbolicLink()) continue;
|
|
29166
29782
|
result.push({ name: entry.name, type: info2.isDirectory() ? "directory" : info2.isFile() ? "file" : "other", ...info2.isFile() ? { size: info2.size } : {} });
|
|
@@ -29382,15 +29998,15 @@ var CodexWorkspaceBridge = class {
|
|
|
29382
29998
|
}
|
|
29383
29999
|
}
|
|
29384
30000
|
async safePath(root, path, directory) {
|
|
29385
|
-
const candidate =
|
|
29386
|
-
const rel =
|
|
29387
|
-
if (rel.startsWith("..") ||
|
|
30001
|
+
const candidate = isAbsolute5(path) ? resolve4(path) : resolve4(root, path);
|
|
30002
|
+
const rel = relative3(root, candidate);
|
|
30003
|
+
if (rel.startsWith("..") || isAbsolute5(rel)) throw new RpcError("CODEX_WORKSPACE_PATH_DENIED", "The requested workspace path is outside the CodeX workspace.");
|
|
29388
30004
|
try {
|
|
29389
30005
|
const info = await lstat(candidate);
|
|
29390
30006
|
if (info.isSymbolicLink()) throw new Error();
|
|
29391
30007
|
const canonical = await realpath2(candidate);
|
|
29392
|
-
const canonicalRel =
|
|
29393
|
-
if (canonicalRel.startsWith("..") ||
|
|
30008
|
+
const canonicalRel = relative3(root, canonical);
|
|
30009
|
+
if (canonicalRel.startsWith("..") || isAbsolute5(canonicalRel)) throw new Error();
|
|
29394
30010
|
if (directory && !info.isDirectory()) throw new Error();
|
|
29395
30011
|
return canonical;
|
|
29396
30012
|
} catch {
|
|
@@ -29433,14 +30049,14 @@ function pipeTerminalSpawner() {
|
|
|
29433
30049
|
child.stdout.on("data", (data2) => queue.push(Buffer.from(data2).toString("utf8")));
|
|
29434
30050
|
child.stderr.on("data", (data2) => queue.push(Buffer.from(data2).toString("utf8")));
|
|
29435
30051
|
child.on("error", () => queue.end());
|
|
29436
|
-
const completed = new Promise((
|
|
30052
|
+
const completed = new Promise((resolve5) => {
|
|
29437
30053
|
child.on("error", () => {
|
|
29438
30054
|
queue.end();
|
|
29439
|
-
|
|
30055
|
+
resolve5({ exitCode: null });
|
|
29440
30056
|
});
|
|
29441
30057
|
child.on("exit", (code) => {
|
|
29442
30058
|
queue.end();
|
|
29443
|
-
|
|
30059
|
+
resolve5({ exitCode: code });
|
|
29444
30060
|
});
|
|
29445
30061
|
});
|
|
29446
30062
|
return {
|
|
@@ -29531,7 +30147,7 @@ function readLimit(args) {
|
|
|
29531
30147
|
|
|
29532
30148
|
// src/service.ts
|
|
29533
30149
|
var HostPluginRuntime = class {
|
|
29534
|
-
constructor(config, identities, apiProxy, logger, localGateway, fileViewerHost, terminalSpawner) {
|
|
30150
|
+
constructor(config, identities, apiProxy, logger, localGateway, fileViewerHost, terminalSpawner, sessionSource) {
|
|
29535
30151
|
this.config = config;
|
|
29536
30152
|
this.identities = identities;
|
|
29537
30153
|
this.apiProxy = apiProxy;
|
|
@@ -29539,6 +30155,7 @@ var HostPluginRuntime = class {
|
|
|
29539
30155
|
this.localGateway = localGateway;
|
|
29540
30156
|
this.fileViewerHost = fileViewerHost;
|
|
29541
30157
|
this.terminalSpawner = terminalSpawner;
|
|
30158
|
+
this.sessionSource = sessionSource;
|
|
29542
30159
|
this.terminalEnabled = config.terminal.enabled;
|
|
29543
30160
|
this.loopbackPorts = [...config.loopback.ports];
|
|
29544
30161
|
this.paused = config.hostControl?.paused === true;
|
|
@@ -29563,7 +30180,8 @@ var HostPluginRuntime = class {
|
|
|
29563
30180
|
() => this.terminalEnabled,
|
|
29564
30181
|
this.codexWorkspaceState,
|
|
29565
30182
|
this.terminalSpawner
|
|
29566
|
-
)
|
|
30183
|
+
),
|
|
30184
|
+
this.sessionSource === void 0 ? void 0 : (sessionId) => this.sessionSource?.()?.get?.(sessionId)?.header?.cwd
|
|
29567
30185
|
) : void 0;
|
|
29568
30186
|
const fileViewer = new RemoteFileViewerBridge(
|
|
29569
30187
|
() => this.fileViewerHost?.(),
|
|
@@ -29985,7 +30603,7 @@ function isPlainRecord(value) {
|
|
|
29985
30603
|
// src/cli.ts
|
|
29986
30604
|
import { stat as stat7 } from "node:fs/promises";
|
|
29987
30605
|
import { hostname as hostname3 } from "node:os";
|
|
29988
|
-
import { join as
|
|
30606
|
+
import { join as join12 } from "node:path";
|
|
29989
30607
|
var QR_POLL_INTERVAL_MS = 2e3;
|
|
29990
30608
|
var TERMINAL_QR_MARGIN = 4;
|
|
29991
30609
|
async function runCli(args = process.argv.slice(2), dependencies = {}) {
|
|
@@ -30073,7 +30691,7 @@ async function status(args, runtime) {
|
|
|
30073
30691
|
`Server: ${serverUrl}`,
|
|
30074
30692
|
"Host control: enabled (dsh-TUI default)"
|
|
30075
30693
|
];
|
|
30076
|
-
if (!await exists3(
|
|
30694
|
+
if (!await exists3(join12(directory, "device.json"))) {
|
|
30077
30695
|
lines.push("Device: not initialized", "Authorization: logged out", "Credential: unavailable");
|
|
30078
30696
|
write(runtime.stdout, `${lines.join("\n")}
|
|
30079
30697
|
|
|
@@ -30117,7 +30735,7 @@ async function logout(args, runtime) {
|
|
|
30117
30735
|
const serverUrl = selectedServer();
|
|
30118
30736
|
const root = new IdentityStore({ env: runtime.env }).directory;
|
|
30119
30737
|
const directory = serverStorageDirectory(root, serverUrl, "host");
|
|
30120
|
-
if (!await exists3(
|
|
30738
|
+
if (!await exists3(join12(directory, "device.json"))) {
|
|
30121
30739
|
await new ServerCredentialStore(directory).clear();
|
|
30122
30740
|
write(runtime.stdout, "This Host is already logged out.\n");
|
|
30123
30741
|
return 0;
|
|
@@ -30148,7 +30766,7 @@ function resolveDependencies(input2) {
|
|
|
30148
30766
|
stdout: input2.stdout ?? process.stdout,
|
|
30149
30767
|
stderr: input2.stderr ?? process.stderr,
|
|
30150
30768
|
now: input2.now ?? Date.now,
|
|
30151
|
-
wait: input2.wait ?? ((milliseconds) => new Promise((
|
|
30769
|
+
wait: input2.wait ?? ((milliseconds) => new Promise((resolve5) => setTimeout(resolve5, milliseconds))),
|
|
30152
30770
|
renderQr: input2.renderQr ?? renderTerminalQr,
|
|
30153
30771
|
createIdentityStore: input2.createIdentityStore ?? ((options) => new IdentityStore(options)),
|
|
30154
30772
|
createHostApi: input2.createHostApi ?? ((serverUrl, store) => new HostServerApi(serverUrl, store))
|
|
@@ -30597,7 +31215,7 @@ function errorCode6(error) {
|
|
|
30597
31215
|
return "CONNECTION_FAILED";
|
|
30598
31216
|
}
|
|
30599
31217
|
function wait(milliseconds) {
|
|
30600
|
-
return new Promise((
|
|
31218
|
+
return new Promise((resolve5) => setTimeout(resolve5, milliseconds));
|
|
30601
31219
|
}
|
|
30602
31220
|
|
|
30603
31221
|
// src/remote-file-content-provider.ts
|
|
@@ -30782,6 +31400,7 @@ async function activate(ctx, readConfig, entryId, tuiBinding) {
|
|
|
30782
31400
|
const nativeTypertGateway = ctx.get("typertGateway");
|
|
30783
31401
|
const localTypertGateway = new TypertGatewaySwitch(nativeTypertGateway, { peer: peerFor }).local();
|
|
30784
31402
|
const subprocess = ctx.get("subprocess", false);
|
|
31403
|
+
const sessionSource = () => ctx.get("sessions", false);
|
|
30785
31404
|
const deepseekAccount = ctx.get("deepseekAccount", false);
|
|
30786
31405
|
const deepseekSession = deepseekAccount === void 0 ? void 0 : {
|
|
30787
31406
|
read: async () => {
|
|
@@ -30813,7 +31432,8 @@ async function activate(ctx, readConfig, entryId, tuiBinding) {
|
|
|
30813
31432
|
logger,
|
|
30814
31433
|
localTypertGateway,
|
|
30815
31434
|
() => ctx.get("fileViewerHost"),
|
|
30816
|
-
subprocess === void 0 ? void 0 : subprocessTerminalSpawner(subprocess)
|
|
31435
|
+
subprocess === void 0 ? void 0 : subprocessTerminalSpawner(subprocess),
|
|
31436
|
+
sessionSource
|
|
30817
31437
|
);
|
|
30818
31438
|
let clientRuntime;
|
|
30819
31439
|
const hostControl = runtime;
|