@blue-soda/dsh-remote 0.5.7 → 0.5.8
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 +14 -3
- package/dist/index.js +909 -353
- 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((resolve4) => setTimeout(resolve4, 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();
|
|
@@ -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;
|
|
@@ -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.8";
|
|
13525
13547
|
|
|
13526
13548
|
// src/server-api.ts
|
|
13527
13549
|
var ENABLED_QR_PROVIDERS = ["github"];
|
|
@@ -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
|
+
scope.console?.info?.("[dsh-remote] codex history cache inspector ready; call __dshCodexHistoryCacheStats()");
|
|
19199
|
+
}
|
|
19200
|
+
publishHistoryCacheStats() {
|
|
19201
|
+
const stats = this.historyCacheStats();
|
|
19202
|
+
this.writeHistoryCacheStatsFile(stats);
|
|
19203
|
+
const scope = globalThis;
|
|
19204
|
+
const root = scope.document?.documentElement;
|
|
19205
|
+
if (root?.setAttribute === void 0) return;
|
|
19206
|
+
try {
|
|
19207
|
+
root.setAttribute("data-dsh-codex-history-cache-stats", JSON.stringify(stats));
|
|
19208
|
+
} catch {
|
|
19209
|
+
}
|
|
19210
|
+
}
|
|
19211
|
+
/**
|
|
19212
|
+
* The Codex virtual harness runs in the DSH client's Node process, not in the
|
|
19213
|
+
* browser page, so DevTools cannot see its globals. Mirror the same metadata to
|
|
19214
|
+
* the client home for inspection without any message content.
|
|
19215
|
+
*/
|
|
19216
|
+
writeHistoryCacheStatsFile(stats) {
|
|
19217
|
+
if (!codexHistoryStatsFileEnabled() || process.env.VITEST !== void 0) return;
|
|
19218
|
+
try {
|
|
19219
|
+
const home = process.env.DSH_HOME ?? join4(homedir(), ".dsh");
|
|
19220
|
+
mkdirSync(home, { recursive: true });
|
|
19221
|
+
writeFileSync(
|
|
19222
|
+
join4(home, "codex-history-cache-stats.json"),
|
|
19223
|
+
JSON.stringify({
|
|
19224
|
+
...stats,
|
|
19225
|
+
pid: process.pid,
|
|
19226
|
+
source: "codex-harness",
|
|
19227
|
+
writtenAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
19228
|
+
}, null, 2)
|
|
19229
|
+
);
|
|
19230
|
+
} catch {
|
|
19231
|
+
}
|
|
19232
|
+
}
|
|
19233
|
+
logHistoryCache(event, threadId, page, records, ageMs) {
|
|
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
|
}
|
|
@@ -20767,7 +20846,7 @@ import { networkInterfaces } from "node:os";
|
|
|
20767
20846
|
// src/native-rtc-helper.ts
|
|
20768
20847
|
import { spawn, spawnSync } from "node:child_process";
|
|
20769
20848
|
import { existsSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
20770
|
-
import { delimiter, dirname as dirname3, join as
|
|
20849
|
+
import { delimiter, dirname as dirname3, join as join5 } from "node:path";
|
|
20771
20850
|
import { fileURLToPath } from "node:url";
|
|
20772
20851
|
var cachedExternalFactory;
|
|
20773
20852
|
var cachedExternalFactoryResolved = false;
|
|
@@ -20818,21 +20897,21 @@ function nodeBinaryCandidates() {
|
|
|
20818
20897
|
add3(process.env.DSH_REMOTE_NODE);
|
|
20819
20898
|
add3(process.env.NODE);
|
|
20820
20899
|
if (process.versions.electron === void 0) add3(process.execPath);
|
|
20821
|
-
for (const part of (process.env.PATH ?? "").split(delimiter)) add3(
|
|
20900
|
+
for (const part of (process.env.PATH ?? "").split(delimiter)) add3(join5(part, process.platform === "win32" ? "node.exe" : "node"));
|
|
20822
20901
|
add3("/opt/homebrew/bin/node");
|
|
20823
20902
|
add3("/usr/local/bin/node");
|
|
20824
20903
|
add3("/usr/bin/node");
|
|
20825
|
-
add3(
|
|
20826
|
-
add3(
|
|
20827
|
-
add3(
|
|
20904
|
+
add3(join5(process.env.HOME ?? "", ".volta", "bin", process.platform === "win32" ? "node.exe" : "node"));
|
|
20905
|
+
add3(join5(process.env.HOME ?? "", ".asdf", "shims", process.platform === "win32" ? "node.exe" : "node"));
|
|
20906
|
+
add3(join5(process.env.HOME ?? "", ".local", "bin", process.platform === "win32" ? "node.exe" : "node"));
|
|
20828
20907
|
for (const nvmNode of nvmNodeCandidates()) add3(nvmNode);
|
|
20829
20908
|
return candidates;
|
|
20830
20909
|
}
|
|
20831
20910
|
function nvmNodeCandidates() {
|
|
20832
|
-
const root =
|
|
20911
|
+
const root = join5(process.env.HOME ?? "", ".nvm", "versions", "node");
|
|
20833
20912
|
if (root === "" || !existsSync(root)) return [];
|
|
20834
20913
|
try {
|
|
20835
|
-
return readdirSync(root).map((version) =>
|
|
20914
|
+
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
20915
|
} catch {
|
|
20837
20916
|
return [];
|
|
20838
20917
|
}
|
|
@@ -21862,6 +21941,7 @@ var ClientModeRuntime = class {
|
|
|
21862
21941
|
this.proxySwitch = apiProxy === void 0 ? void 0 : new ApiProxySwitch(apiProxy);
|
|
21863
21942
|
this.gatewaySwitch = new TypertGatewaySwitch(typertGateway, { peer: this.peer });
|
|
21864
21943
|
this.gatewaySwitch.setRemoteAvailability(() => this.connected !== void 0);
|
|
21944
|
+
this.installCodexHistoryCacheInspector();
|
|
21865
21945
|
}
|
|
21866
21946
|
preview;
|
|
21867
21947
|
identity;
|
|
@@ -23316,6 +23396,44 @@ var ClientModeRuntime = class {
|
|
|
23316
23396
|
const virtual = this.codexVirtual;
|
|
23317
23397
|
this.codexVirtual = void 0;
|
|
23318
23398
|
await virtual?.close();
|
|
23399
|
+
this.installCodexHistoryCacheInspector();
|
|
23400
|
+
}
|
|
23401
|
+
/**
|
|
23402
|
+
* Keep `__dshCodexHistoryCacheStats()` defined for the whole client lifetime.
|
|
23403
|
+
* The Codex virtual harness has its own inspector while a workspace is open;
|
|
23404
|
+
* this runtime-level one fills the gap before the first open and after close.
|
|
23405
|
+
*/
|
|
23406
|
+
installCodexHistoryCacheInspector() {
|
|
23407
|
+
const globalScope = globalThis;
|
|
23408
|
+
const scope = globalThis;
|
|
23409
|
+
const inspect = () => this.codexVirtual?.historyCacheStats() ?? {
|
|
23410
|
+
ttlMs: 6e4,
|
|
23411
|
+
hits: 0,
|
|
23412
|
+
misses: 0,
|
|
23413
|
+
active: false,
|
|
23414
|
+
entries: []
|
|
23415
|
+
};
|
|
23416
|
+
globalScope.__dshCodexHistoryCacheStats = inspect;
|
|
23417
|
+
if (scope.window !== void 0) scope.window.__dshCodexHistoryCacheStats = inspect;
|
|
23418
|
+
this.writeCodexHistoryCacheStats(inspect());
|
|
23419
|
+
scope.console?.info?.("[dsh-remote] codex history cache inspector ready (client runtime)");
|
|
23420
|
+
}
|
|
23421
|
+
writeCodexHistoryCacheStats(stats) {
|
|
23422
|
+
if (!codexHistoryStatsFileEnabled() || process.env.VITEST !== void 0) return;
|
|
23423
|
+
try {
|
|
23424
|
+
const home = process.env.DSH_HOME ?? join6(homedir2(), ".dsh");
|
|
23425
|
+
mkdirSync2(home, { recursive: true });
|
|
23426
|
+
writeFileSync2(
|
|
23427
|
+
join6(home, "codex-history-cache-stats.json"),
|
|
23428
|
+
JSON.stringify({
|
|
23429
|
+
...stats,
|
|
23430
|
+
pid: process.pid,
|
|
23431
|
+
source: "client-runtime",
|
|
23432
|
+
writtenAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
23433
|
+
}, null, 2)
|
|
23434
|
+
);
|
|
23435
|
+
} catch {
|
|
23436
|
+
}
|
|
23319
23437
|
}
|
|
23320
23438
|
assertRemoteCompatible(remote) {
|
|
23321
23439
|
this.selectHarnessRemoteTransport(remote);
|
|
@@ -24023,14 +24141,14 @@ function iceServersForAttempt(attempt, iceServers) {
|
|
|
24023
24141
|
// src/client-target-store.ts
|
|
24024
24142
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
24025
24143
|
import { chmod as chmod2, mkdir as mkdir2, readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises";
|
|
24026
|
-
import { join as
|
|
24144
|
+
import { join as join7 } from "node:path";
|
|
24027
24145
|
var FILE_NAME = "client-target.json";
|
|
24028
24146
|
var ClientTargetStore = class {
|
|
24029
24147
|
constructor(directory) {
|
|
24030
24148
|
this.directory = directory;
|
|
24031
24149
|
}
|
|
24032
24150
|
get file() {
|
|
24033
|
-
return
|
|
24151
|
+
return join7(this.directory, FILE_NAME);
|
|
24034
24152
|
}
|
|
24035
24153
|
/** Read the record. A missing, unreadable or malformed file reads as "no record". */
|
|
24036
24154
|
async load() {
|
|
@@ -24106,8 +24224,8 @@ import { execFileSync } from "node:child_process";
|
|
|
24106
24224
|
import { existsSync as existsSync2 } from "node:fs";
|
|
24107
24225
|
import { createHash } from "node:crypto";
|
|
24108
24226
|
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
|
|
24227
|
+
import { homedir as homedir3 } from "node:os";
|
|
24228
|
+
import { dirname as dirname4, join as join8 } from "node:path";
|
|
24111
24229
|
var identitySchema = external_exports.object({
|
|
24112
24230
|
schemaVersion: external_exports.literal(1),
|
|
24113
24231
|
deviceId: external_exports.string().uuid(),
|
|
@@ -24136,14 +24254,14 @@ var IdentityStore = class {
|
|
|
24136
24254
|
peers = /* @__PURE__ */ new Map();
|
|
24137
24255
|
constructor(options = {}) {
|
|
24138
24256
|
const env = options.env ?? process.env;
|
|
24139
|
-
const dshHome = env.DSH_HOME ||
|
|
24140
|
-
this.directory = options.directory ??
|
|
24257
|
+
const dshHome = env.DSH_HOME || join8(options.homeDirectory ?? homedir3(), ".dsh");
|
|
24258
|
+
this.directory = options.directory ?? join8(dshHome, "remote");
|
|
24141
24259
|
}
|
|
24142
24260
|
async loadOrCreate(deviceName) {
|
|
24143
24261
|
await mkdir3(this.directory, { recursive: true, mode: 448 });
|
|
24144
24262
|
await chmod3(this.directory, 448);
|
|
24145
|
-
const devicePath =
|
|
24146
|
-
const keyPath =
|
|
24263
|
+
const devicePath = join8(this.directory, "device.json");
|
|
24264
|
+
const keyPath = join8(this.directory, "device.key");
|
|
24147
24265
|
const [hasDevice, hasKey] = await Promise.all([exists2(devicePath), exists2(keyPath)]);
|
|
24148
24266
|
if (hasDevice !== hasKey) {
|
|
24149
24267
|
throw new IdentityInvalidError("device identity is incomplete; repair it explicitly before reconnecting");
|
|
@@ -24212,7 +24330,7 @@ var IdentityStore = class {
|
|
|
24212
24330
|
return removed;
|
|
24213
24331
|
}
|
|
24214
24332
|
async loadPeers() {
|
|
24215
|
-
const path =
|
|
24333
|
+
const path = join8(this.directory, "trusted-peers.json");
|
|
24216
24334
|
if (!await exists2(path)) {
|
|
24217
24335
|
await atomicJsonWrite(path, { schemaVersion: 1, peers: [] }, 384);
|
|
24218
24336
|
}
|
|
@@ -24228,7 +24346,7 @@ var IdentityStore = class {
|
|
|
24228
24346
|
this.peers = peers;
|
|
24229
24347
|
}
|
|
24230
24348
|
async savePeers() {
|
|
24231
|
-
await atomicJsonWrite(
|
|
24349
|
+
await atomicJsonWrite(join8(this.directory, "trusted-peers.json"), {
|
|
24232
24350
|
schemaVersion: 1,
|
|
24233
24351
|
peers: [...this.peers.values()]
|
|
24234
24352
|
}, 384);
|
|
@@ -24237,7 +24355,7 @@ var IdentityStore = class {
|
|
|
24237
24355
|
function serverStorageDirectory(root, serverUrl, role) {
|
|
24238
24356
|
const origin = new URL(serverUrl).origin;
|
|
24239
24357
|
const scope = createHash("sha256").update(origin).digest("hex").slice(0, 24);
|
|
24240
|
-
return
|
|
24358
|
+
return join8(root, "servers", scope, role);
|
|
24241
24359
|
}
|
|
24242
24360
|
async function ensureDeviceDirectory(root, serverUrl) {
|
|
24243
24361
|
const directory = serverStorageDirectory(root, serverUrl, "device");
|
|
@@ -24438,10 +24556,11 @@ var ControlStatusStream = class {
|
|
|
24438
24556
|
|
|
24439
24557
|
// src/codex/domain.ts
|
|
24440
24558
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
24559
|
+
import { Buffer as Buffer4 } from "node:buffer";
|
|
24441
24560
|
import { accessSync, constants, existsSync as existsSync3, readFileSync, readdirSync as readdirSync2, statSync as statSync2 } from "node:fs";
|
|
24442
24561
|
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
|
|
24562
|
+
import { homedir as homedir4 } from "node:os";
|
|
24563
|
+
import { basename as basename2, isAbsolute as isAbsolute2, join as join9, posix, relative, resolve } from "node:path";
|
|
24445
24564
|
|
|
24446
24565
|
// src/codex/app-server.ts
|
|
24447
24566
|
import { spawn as spawn2 } from "node:child_process";
|
|
@@ -24698,6 +24817,135 @@ function isRecord10(value) {
|
|
|
24698
24817
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
24699
24818
|
}
|
|
24700
24819
|
|
|
24820
|
+
// src/codex/method-policy.ts
|
|
24821
|
+
var id2 = external_exports.string().min(1).max(256);
|
|
24822
|
+
var cursor = external_exports.string().min(1).max(4096).nullable().optional();
|
|
24823
|
+
var textInput = external_exports.object({
|
|
24824
|
+
type: external_exports.literal("text"),
|
|
24825
|
+
text: external_exports.string().min(1).max(256 * 1024)
|
|
24826
|
+
}).strict();
|
|
24827
|
+
var imageMediaType = external_exports.enum(["image/png", "image/jpeg", "image/webp", "image/gif"]);
|
|
24828
|
+
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), {
|
|
24829
|
+
message: "Image data must use canonical base64."
|
|
24830
|
+
});
|
|
24831
|
+
var imageInput = external_exports.object({
|
|
24832
|
+
type: external_exports.literal("image"),
|
|
24833
|
+
mediaType: imageMediaType,
|
|
24834
|
+
data: canonicalBase64
|
|
24835
|
+
}).strict();
|
|
24836
|
+
var input = external_exports.array(external_exports.union([textInput, imageInput])).min(1).max(16);
|
|
24837
|
+
var permissionPreset = external_exports.enum(["workspace-write", "danger-full-access"]);
|
|
24838
|
+
var projectRoot = external_exports.object({
|
|
24839
|
+
path: external_exports.string().min(1).max(4096)
|
|
24840
|
+
}).strict();
|
|
24841
|
+
var CODEX_HISTORY_MAX_MESSAGES = 200;
|
|
24842
|
+
var schemas = {
|
|
24843
|
+
"account/read": external_exports.object({ refreshToken: external_exports.literal(false).optional() }).strict(),
|
|
24844
|
+
"model/list": external_exports.object({
|
|
24845
|
+
cursor,
|
|
24846
|
+
limit: external_exports.number().int().min(1).max(100).optional(),
|
|
24847
|
+
includeHidden: external_exports.boolean().optional()
|
|
24848
|
+
}).strict(),
|
|
24849
|
+
"project/list": external_exports.object({
|
|
24850
|
+
cursor,
|
|
24851
|
+
limit: external_exports.number().int().min(1).max(100).optional()
|
|
24852
|
+
}).strict(),
|
|
24853
|
+
"project/create": external_exports.object({
|
|
24854
|
+
name: external_exports.string().trim().min(1).max(256),
|
|
24855
|
+
roots: external_exports.array(projectRoot).length(1),
|
|
24856
|
+
idempotencyKey: external_exports.string().min(16).max(256)
|
|
24857
|
+
}).strict(),
|
|
24858
|
+
"thread/list": external_exports.object({
|
|
24859
|
+
cursor,
|
|
24860
|
+
limit: external_exports.number().int().min(1).max(100).optional(),
|
|
24861
|
+
sortKey: external_exports.enum(["created_at", "updated_at", "recency_at"]).optional(),
|
|
24862
|
+
sortDirection: external_exports.enum(["asc", "desc"]).optional(),
|
|
24863
|
+
modelProviders: external_exports.array(external_exports.string().min(1).max(128)).max(32).nullable().optional(),
|
|
24864
|
+
sourceKinds: external_exports.array(external_exports.enum(["cli", "vscode", "exec", "appServer", "unknown"])).max(8).optional(),
|
|
24865
|
+
archived: external_exports.boolean().optional(),
|
|
24866
|
+
isPinned: external_exports.boolean().optional(),
|
|
24867
|
+
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(),
|
|
24868
|
+
useStateDbOnly: external_exports.boolean().optional(),
|
|
24869
|
+
searchTerm: external_exports.string().max(1024).optional(),
|
|
24870
|
+
// Upstream `ThreadListParams` declares these two as well. Refusing them turned a
|
|
24871
|
+
// legitimate client query into "The CodeX call parameters are invalid.".
|
|
24872
|
+
originators: external_exports.array(external_exports.string().min(1).max(256)).max(32).nullable().optional(),
|
|
24873
|
+
sectionId: external_exports.string().max(256).nullable().optional()
|
|
24874
|
+
}).strict(),
|
|
24875
|
+
"thread/read": external_exports.object({ threadId: id2, includeTurns: external_exports.boolean().optional() }).strict(),
|
|
24876
|
+
"dsh/sessionHistory": external_exports.object({
|
|
24877
|
+
threadId: id2,
|
|
24878
|
+
beforeSeq: external_exports.number().int().nonnegative().optional(),
|
|
24879
|
+
throughSeq: external_exports.number().int().min(-1).optional(),
|
|
24880
|
+
maxMessages: external_exports.number().int().min(1).max(CODEX_HISTORY_MAX_MESSAGES).optional()
|
|
24881
|
+
}).strict(),
|
|
24882
|
+
"dsh/directoryList": external_exports.object({
|
|
24883
|
+
path: external_exports.string().min(1).max(4096)
|
|
24884
|
+
}).strict(),
|
|
24885
|
+
"thread/start": external_exports.object({
|
|
24886
|
+
cwd: external_exports.string().min(1).max(4096),
|
|
24887
|
+
model: external_exports.string().min(1).max(128).optional(),
|
|
24888
|
+
personality: external_exports.string().min(1).max(64).optional(),
|
|
24889
|
+
permissionPreset: permissionPreset.optional()
|
|
24890
|
+
}).strict(),
|
|
24891
|
+
"thread/resume": external_exports.object({
|
|
24892
|
+
threadId: id2,
|
|
24893
|
+
model: external_exports.string().min(1).max(128).optional(),
|
|
24894
|
+
permissionPreset: permissionPreset.optional()
|
|
24895
|
+
}).strict(),
|
|
24896
|
+
"thread/fork": external_exports.object({
|
|
24897
|
+
threadId: id2,
|
|
24898
|
+
lastTurnId: id2.optional(),
|
|
24899
|
+
permissionPreset: permissionPreset.optional()
|
|
24900
|
+
}).strict(),
|
|
24901
|
+
// Upstream declares a plain string and uses the empty value to clear a name, so the
|
|
24902
|
+
// trim-based minimum rejected a legitimate rename.
|
|
24903
|
+
"thread/name/set": external_exports.object({ threadId: id2, name: external_exports.string().max(256) }).strict(),
|
|
24904
|
+
"thread/archive": external_exports.object({ threadId: id2 }).strict(),
|
|
24905
|
+
"thread/unarchive": external_exports.object({ threadId: id2 }).strict(),
|
|
24906
|
+
"thread/unsubscribe": external_exports.object({ threadId: id2 }).strict(),
|
|
24907
|
+
"turn/start": external_exports.object({
|
|
24908
|
+
threadId: id2,
|
|
24909
|
+
input,
|
|
24910
|
+
model: external_exports.string().min(1).max(128).optional(),
|
|
24911
|
+
effort: external_exports.enum(["none", "minimal", "low", "medium", "high", "xhigh", "max", "ultra"]).optional(),
|
|
24912
|
+
summary: external_exports.enum(["auto", "concise", "detailed", "none"]).optional(),
|
|
24913
|
+
personality: external_exports.string().min(1).max(64).optional(),
|
|
24914
|
+
permissionPreset: permissionPreset.optional()
|
|
24915
|
+
}).strict(),
|
|
24916
|
+
"turn/steer": external_exports.object({
|
|
24917
|
+
threadId: id2,
|
|
24918
|
+
input,
|
|
24919
|
+
expectedTurnId: id2,
|
|
24920
|
+
// Declared by upstream `TurnSteerParams`; see the thread/list note above.
|
|
24921
|
+
clientUserMessageId: external_exports.string().max(256).nullable().optional()
|
|
24922
|
+
}).strict(),
|
|
24923
|
+
"turn/interrupt": external_exports.object({ threadId: id2, turnId: id2 }).strict()
|
|
24924
|
+
};
|
|
24925
|
+
var CODEX_APP_ALLOWLIST = Object.freeze(Object.keys(schemas));
|
|
24926
|
+
function parseCodexCall(method, params) {
|
|
24927
|
+
if (!Object.prototype.hasOwnProperty.call(schemas, method)) {
|
|
24928
|
+
throw new RpcError("METHOD_NOT_ALLOWED", "The requested Codex method is not available over Remote.");
|
|
24929
|
+
}
|
|
24930
|
+
const schema = schemas[method];
|
|
24931
|
+
const parsed = schema.safeParse(params);
|
|
24932
|
+
if (!parsed.success) {
|
|
24933
|
+
const detail = parsed.error.issues.slice(0, 4).map((issue) => {
|
|
24934
|
+
const path = issue.path.length === 0 ? "(root)" : issue.path.join(".");
|
|
24935
|
+
const keys = "keys" in issue && Array.isArray(issue.keys) ? ` [${issue.keys.join(", ")}]` : "";
|
|
24936
|
+
return `${path}${keys}: ${issue.code}`;
|
|
24937
|
+
}).join(", ");
|
|
24938
|
+
throw new RpcError("INVALID_MESSAGE", `The CodeX call parameters are invalid. (${method} \u2192 ${detail})`);
|
|
24939
|
+
}
|
|
24940
|
+
return { method, params: parsed.data };
|
|
24941
|
+
}
|
|
24942
|
+
function isThreadMutation(method) {
|
|
24943
|
+
return method === "turn/start" || method === "turn/steer" || method === "turn/interrupt";
|
|
24944
|
+
}
|
|
24945
|
+
function threadIdFromParams(params) {
|
|
24946
|
+
return typeof params.threadId === "string" ? params.threadId : void 0;
|
|
24947
|
+
}
|
|
24948
|
+
|
|
24701
24949
|
// src/codex/peer-bridge.ts
|
|
24702
24950
|
import { Buffer as Buffer3 } from "node:buffer";
|
|
24703
24951
|
var streamOpenSchema = external_exports.object({
|
|
@@ -24988,6 +25236,219 @@ function concatChunks(chunks, totalBytes) {
|
|
|
24988
25236
|
return output;
|
|
24989
25237
|
}
|
|
24990
25238
|
|
|
25239
|
+
// src/codex/history-window.ts
|
|
25240
|
+
var PAGE_LIMIT = 25;
|
|
25241
|
+
var MAX_PAGES = 64;
|
|
25242
|
+
var CACHE_TTL_MS = 5 * 6e4;
|
|
25243
|
+
var CACHE_LIMIT = 16;
|
|
25244
|
+
var CodexHistoryWindows = class {
|
|
25245
|
+
constructor(call, logger) {
|
|
25246
|
+
this.call = call;
|
|
25247
|
+
this.logger = logger;
|
|
25248
|
+
}
|
|
25249
|
+
windows = /* @__PURE__ */ new Map();
|
|
25250
|
+
async window(threadId, metadata, beforeSeq, maxMessages) {
|
|
25251
|
+
const updatedAt = typeof metadata.updatedAt === "number" ? metadata.updatedAt : void 0;
|
|
25252
|
+
const cached = this.windows.get(threadId);
|
|
25253
|
+
const fresh = cached !== void 0 && (updatedAt === void 0 || cached.updatedAt === void 0 || cached.updatedAt === updatedAt) && Date.now() - cached.at < CACHE_TTL_MS;
|
|
25254
|
+
if (fresh && cached !== void 0) {
|
|
25255
|
+
cached.at = Date.now();
|
|
25256
|
+
await this.ensureOlder(threadId, cached, beforeSeq, maxMessages);
|
|
25257
|
+
this.logger?.debug("codex history cache hit", {
|
|
25258
|
+
threadId,
|
|
25259
|
+
turns: cached.loaded.size,
|
|
25260
|
+
records: cached.history.entries.length
|
|
25261
|
+
});
|
|
25262
|
+
return cached;
|
|
25263
|
+
}
|
|
25264
|
+
if (cached !== void 0) {
|
|
25265
|
+
const appended = await this.appendNewTurns(threadId, cached, updatedAt);
|
|
25266
|
+
if (appended !== void 0) {
|
|
25267
|
+
await this.ensureOlder(threadId, appended, beforeSeq, maxMessages);
|
|
25268
|
+
this.remember(threadId, appended);
|
|
25269
|
+
this.logger?.debug("codex history cache appended", {
|
|
25270
|
+
threadId,
|
|
25271
|
+
turns: appended.loaded.size,
|
|
25272
|
+
records: appended.history.entries.length
|
|
25273
|
+
});
|
|
25274
|
+
return appended;
|
|
25275
|
+
}
|
|
25276
|
+
}
|
|
25277
|
+
const built = await this.rebuild(threadId, metadata, cached?.acceptedPageSize);
|
|
25278
|
+
await this.ensureOlder(threadId, built, beforeSeq, maxMessages);
|
|
25279
|
+
this.remember(threadId, built);
|
|
25280
|
+
return built;
|
|
25281
|
+
}
|
|
25282
|
+
async rebuild(threadId, metadata, acceptedPageSize) {
|
|
25283
|
+
const startedAt = performance.now();
|
|
25284
|
+
const index = await this.readTurnIndex(threadId);
|
|
25285
|
+
const window = {
|
|
25286
|
+
metadata,
|
|
25287
|
+
turnNumbers: index.numbers,
|
|
25288
|
+
turnStartSeq: index.turnStartSeq,
|
|
25289
|
+
totalTurns: index.total,
|
|
25290
|
+
totalEvents: index.totalEvents,
|
|
25291
|
+
loaded: /* @__PURE__ */ new Map(),
|
|
25292
|
+
history: projectCodexNativeHistory({ ...metadata, turns: [] }, `codex:${threadId}`, "legacy", {
|
|
25293
|
+
turnNumbers: index.numbers
|
|
25294
|
+
}),
|
|
25295
|
+
...typeof metadata.updatedAt === "number" ? { updatedAt: metadata.updatedAt } : {},
|
|
25296
|
+
...acceptedPageSize === void 0 ? {} : { acceptedPageSize },
|
|
25297
|
+
at: Date.now()
|
|
25298
|
+
};
|
|
25299
|
+
this.logger?.debug("codex history index", {
|
|
25300
|
+
threadId,
|
|
25301
|
+
turns: index.total,
|
|
25302
|
+
pages: index.pages,
|
|
25303
|
+
durationMs: Math.round(performance.now() - startedAt)
|
|
25304
|
+
});
|
|
25305
|
+
await this.loadTail(threadId, window);
|
|
25306
|
+
return window;
|
|
25307
|
+
}
|
|
25308
|
+
async readTurnIndex(threadId) {
|
|
25309
|
+
const numbers = /* @__PURE__ */ new Map();
|
|
25310
|
+
const turnStartSeq = /* @__PURE__ */ new Map();
|
|
25311
|
+
let total = 0;
|
|
25312
|
+
let totalEvents = 0;
|
|
25313
|
+
let pages = 0;
|
|
25314
|
+
let cursor2;
|
|
25315
|
+
for (let page = 0; page < MAX_PAGES; page += 1) {
|
|
25316
|
+
const result = await this.turnPage(threadId, cursor2, "asc", "summary");
|
|
25317
|
+
pages += 1;
|
|
25318
|
+
for (const rawTurn of result.turns) {
|
|
25319
|
+
const turnId = typeof rawTurn.id === "string" ? rawTurn.id : void 0;
|
|
25320
|
+
if (turnId === void 0 || numbers.has(turnId)) continue;
|
|
25321
|
+
total += 1;
|
|
25322
|
+
numbers.set(turnId, total);
|
|
25323
|
+
turnStartSeq.set(turnId, totalEvents);
|
|
25324
|
+
totalEvents += projectCodexNativeHistory({ turns: [rawTurn] }, `codex:${threadId}`).entries.length;
|
|
25325
|
+
}
|
|
25326
|
+
cursor2 = result.nextCursor;
|
|
25327
|
+
if (cursor2 === void 0) break;
|
|
25328
|
+
}
|
|
25329
|
+
return { numbers, turnStartSeq, total, totalEvents, pages };
|
|
25330
|
+
}
|
|
25331
|
+
async loadTail(threadId, window) {
|
|
25332
|
+
let cursor2;
|
|
25333
|
+
let fetched = 0;
|
|
25334
|
+
for (let page = 0; page < MAX_PAGES; page += 1) {
|
|
25335
|
+
const result = await this.turnPage(threadId, cursor2, "desc", "full");
|
|
25336
|
+
fetched += result.turns.length;
|
|
25337
|
+
this.addTurns(window, result.turns);
|
|
25338
|
+
cursor2 = result.nextCursor;
|
|
25339
|
+
this.project(threadId, window);
|
|
25340
|
+
if (surfaceRecords(window.history) >= HISTORY_PAGE_SIZES[0] || cursor2 === void 0) break;
|
|
25341
|
+
}
|
|
25342
|
+
window.olderCursor = cursor2;
|
|
25343
|
+
this.logger?.debug("codex history tail", {
|
|
25344
|
+
threadId,
|
|
25345
|
+
turnsFetched: fetched,
|
|
25346
|
+
loadedTurns: window.loaded.size,
|
|
25347
|
+
records: window.history.entries.length,
|
|
25348
|
+
older: window.olderCursor !== void 0
|
|
25349
|
+
});
|
|
25350
|
+
}
|
|
25351
|
+
async ensureOlder(threadId, window, beforeSeq, maxMessages) {
|
|
25352
|
+
if (beforeSeq === void 0) return;
|
|
25353
|
+
const needed = Math.min(maxMessages ?? HISTORY_PAGE_SIZES[0], HISTORY_PAGE_SIZES[0]);
|
|
25354
|
+
const available = () => window.history.entries.filter((entry) => entry.event.seq < beforeSeq && isSurfaceEvent(entry.event.type) && entry.event.surfaceOp === "append").length;
|
|
25355
|
+
let fetched = 0;
|
|
25356
|
+
for (let page = 0; page < MAX_PAGES; page += 1) {
|
|
25357
|
+
if (available() >= needed) break;
|
|
25358
|
+
if (window.olderCursor === void 0) break;
|
|
25359
|
+
const result = await this.turnPage(threadId, window.olderCursor, "desc", "full");
|
|
25360
|
+
fetched += result.turns.length;
|
|
25361
|
+
this.addTurns(window, result.turns);
|
|
25362
|
+
window.olderCursor = result.nextCursor;
|
|
25363
|
+
this.project(threadId, window);
|
|
25364
|
+
}
|
|
25365
|
+
if (fetched > 0) {
|
|
25366
|
+
this.logger?.debug("codex history older", {
|
|
25367
|
+
threadId,
|
|
25368
|
+
beforeSeq,
|
|
25369
|
+
needed,
|
|
25370
|
+
turnsFetched: fetched,
|
|
25371
|
+
loadedTurns: window.loaded.size
|
|
25372
|
+
});
|
|
25373
|
+
}
|
|
25374
|
+
}
|
|
25375
|
+
async appendNewTurns(threadId, cached, updatedAt) {
|
|
25376
|
+
if (cached.newestTurnId === void 0) return void 0;
|
|
25377
|
+
const newer = [];
|
|
25378
|
+
let cursor2;
|
|
25379
|
+
for (let page = 0; page < MAX_PAGES; page += 1) {
|
|
25380
|
+
const result = await this.turnPage(threadId, cursor2, "desc", "full");
|
|
25381
|
+
for (const rawTurn of result.turns) {
|
|
25382
|
+
if (typeof rawTurn.id === "string" && rawTurn.id === cached.newestTurnId) {
|
|
25383
|
+
if (newer.length === 0) {
|
|
25384
|
+
cached.updatedAt = updatedAt;
|
|
25385
|
+
cached.at = Date.now();
|
|
25386
|
+
return cached;
|
|
25387
|
+
}
|
|
25388
|
+
this.addTurns(cached, [...newer].reverse());
|
|
25389
|
+
this.project(threadId, cached);
|
|
25390
|
+
cached.updatedAt = updatedAt;
|
|
25391
|
+
cached.at = Date.now();
|
|
25392
|
+
return cached;
|
|
25393
|
+
}
|
|
25394
|
+
newer.push(rawTurn);
|
|
25395
|
+
}
|
|
25396
|
+
cursor2 = result.nextCursor;
|
|
25397
|
+
if (cursor2 === void 0) return void 0;
|
|
25398
|
+
}
|
|
25399
|
+
return void 0;
|
|
25400
|
+
}
|
|
25401
|
+
addTurns(window, turns) {
|
|
25402
|
+
for (const turn of turns) {
|
|
25403
|
+
const turnId = typeof turn.id === "string" ? turn.id : void 0;
|
|
25404
|
+
if (turnId === void 0) continue;
|
|
25405
|
+
if (!window.turnNumbers.has(turnId)) {
|
|
25406
|
+
window.totalTurns += 1;
|
|
25407
|
+
window.turnNumbers.set(turnId, window.totalTurns);
|
|
25408
|
+
window.turnStartSeq.set(turnId, window.totalEvents);
|
|
25409
|
+
window.totalEvents += projectCodexNativeHistory({ turns: [turn] }, window.history.header.id).entries.length;
|
|
25410
|
+
}
|
|
25411
|
+
window.loaded.set(turnId, turn);
|
|
25412
|
+
}
|
|
25413
|
+
}
|
|
25414
|
+
project(threadId, window) {
|
|
25415
|
+
const turns = [...window.loaded.entries()].sort((left, right) => (window.turnNumbers.get(left[0]) ?? 0) - (window.turnNumbers.get(right[0]) ?? 0)).map(([, turn]) => turn);
|
|
25416
|
+
const first = [...window.loaded.keys()].sort((left, right) => (window.turnNumbers.get(left) ?? 0) - (window.turnNumbers.get(right) ?? 0))[0];
|
|
25417
|
+
const startSeq = first === void 0 ? 0 : window.turnStartSeq.get(first) ?? 0;
|
|
25418
|
+
const history = projectCodexNativeHistory({ ...window.metadata, turns }, `codex:${threadId}`, "legacy", {
|
|
25419
|
+
seq: startSeq,
|
|
25420
|
+
turnNumbers: window.turnNumbers
|
|
25421
|
+
});
|
|
25422
|
+
history.nextTurn = window.totalTurns + 1;
|
|
25423
|
+
window.history = history;
|
|
25424
|
+
const newest = [...window.turnNumbers.entries()].sort((left, right) => right[1] - left[1])[0]?.[0];
|
|
25425
|
+
if (newest !== void 0) window.newestTurnId = newest;
|
|
25426
|
+
}
|
|
25427
|
+
async turnPage(threadId, cursor2, direction, itemsView) {
|
|
25428
|
+
const result = await this.call("thread/turns/list", {
|
|
25429
|
+
threadId,
|
|
25430
|
+
limit: PAGE_LIMIT,
|
|
25431
|
+
sortDirection: direction,
|
|
25432
|
+
itemsView,
|
|
25433
|
+
...cursor2 === void 0 ? {} : { cursor: cursor2 }
|
|
25434
|
+
});
|
|
25435
|
+
const page = typeof result === "object" && result !== null ? result : {};
|
|
25436
|
+
const turns = Array.isArray(page.data) ? page.data.filter((turn) => typeof turn === "object" && turn !== null && !Array.isArray(turn)) : [];
|
|
25437
|
+
const nextCursor = typeof page.nextCursor === "string" && page.nextCursor.length > 0 ? page.nextCursor : void 0;
|
|
25438
|
+
return { turns, nextCursor };
|
|
25439
|
+
}
|
|
25440
|
+
remember(threadId, window) {
|
|
25441
|
+
if (this.windows.size >= CACHE_LIMIT && !this.windows.has(threadId)) {
|
|
25442
|
+
const oldest = this.windows.keys().next().value;
|
|
25443
|
+
if (oldest !== void 0) this.windows.delete(oldest);
|
|
25444
|
+
}
|
|
25445
|
+
this.windows.set(threadId, window);
|
|
25446
|
+
}
|
|
25447
|
+
};
|
|
25448
|
+
function surfaceRecords(history) {
|
|
25449
|
+
return history.entries.filter((entry) => isSurfaceEvent(entry.event.type) && entry.event.surfaceOp === "append").length;
|
|
25450
|
+
}
|
|
25451
|
+
|
|
24991
25452
|
// src/codex/domain.ts
|
|
24992
25453
|
var APPROVAL_TTL_MS = 5 * 6e4;
|
|
24993
25454
|
var DEFAULT_RESTART_DELAYS_MS = [1e3, 2e3, 4e3, 8e3, 15e3];
|
|
@@ -24996,8 +25457,6 @@ var ACCOUNT_PROBE_TIMEOUT_MS = 6e4;
|
|
|
24996
25457
|
var CODEX_START_BUDGET_MS = 9e4;
|
|
24997
25458
|
var CODEX_PAGE_LIMIT2 = 100;
|
|
24998
25459
|
var MAX_CODEX_PAGES2 = 32;
|
|
24999
|
-
var CODEX_HISTORY_PAGE_LIMIT = 25;
|
|
25000
|
-
var MAX_CODEX_HISTORY_PAGES = 64;
|
|
25001
25460
|
var CODEX_DIRECTORY_ENTRY_LIMIT = 500;
|
|
25002
25461
|
var CodexRemoteDomain = class {
|
|
25003
25462
|
constructor(config, logger, createAppServer = (binary, targetLogger) => new CodexAppServerClient(binary, targetLogger), restartDelaysMs = DEFAULT_RESTART_DELAYS_MS) {
|
|
@@ -25005,6 +25464,7 @@ var CodexRemoteDomain = class {
|
|
|
25005
25464
|
this.createAppServer = createAppServer;
|
|
25006
25465
|
this.restartDelaysMs = restartDelaysMs;
|
|
25007
25466
|
this.config = config;
|
|
25467
|
+
this.historyWindows = new CodexHistoryWindows((method, params) => this.callUpstream(method, params), logger);
|
|
25008
25468
|
}
|
|
25009
25469
|
appServer;
|
|
25010
25470
|
unsubscribeInbound;
|
|
@@ -25014,6 +25474,8 @@ var CodexRemoteDomain = class {
|
|
|
25014
25474
|
turnOwners = /* @__PURE__ */ new Map();
|
|
25015
25475
|
approvals = /* @__PURE__ */ new Map();
|
|
25016
25476
|
permissionPresets = /* @__PURE__ */ new Map();
|
|
25477
|
+
/** Per-Thread turn index plus the tail-first full-item window. */
|
|
25478
|
+
historyWindows;
|
|
25017
25479
|
approvalExpiryTimer;
|
|
25018
25480
|
restartTimer;
|
|
25019
25481
|
restartAttempt = 0;
|
|
@@ -25115,14 +25577,16 @@ var CodexRemoteDomain = class {
|
|
|
25115
25577
|
}
|
|
25116
25578
|
if (call.method === "thread/list") {
|
|
25117
25579
|
const result = sanitizeThreadList(await this.callUpstream(call.method, call.params));
|
|
25580
|
+
const authority = await this.readWorkspaceAuthority(result.data);
|
|
25118
25581
|
const authorized = filterThreadListByWorkspaceAuthority(
|
|
25119
25582
|
result,
|
|
25120
|
-
|
|
25583
|
+
authority,
|
|
25584
|
+
this.config.showUnprojectedSessions !== false
|
|
25121
25585
|
);
|
|
25122
|
-
return {
|
|
25586
|
+
return rebaseThreadListCwds({
|
|
25123
25587
|
...authorized,
|
|
25124
25588
|
data: uniqueCodexThreadRows(authorized.data)
|
|
25125
|
-
};
|
|
25589
|
+
}, authority.roots);
|
|
25126
25590
|
}
|
|
25127
25591
|
if (call.method === "thread/start") {
|
|
25128
25592
|
const cwd = await this.requireCodexWorkspacePath(call.params.cwd);
|
|
@@ -25141,21 +25605,15 @@ var CodexRemoteDomain = class {
|
|
|
25141
25605
|
}
|
|
25142
25606
|
const threadId = threadIdFromParams(call.params);
|
|
25143
25607
|
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
|
-
};
|
|
25608
|
+
const { thread: metadata } = await this.readKnownThread(threadId);
|
|
25609
|
+
const beforeSeq = optionalInteger2(call.params.beforeSeq);
|
|
25610
|
+
const maxMessages = optionalInteger2(call.params.maxMessages);
|
|
25611
|
+
const window = await this.historyWindows.window(threadId, metadata, beforeSeq, maxMessages);
|
|
25612
|
+
return this.historyPage(window, threadId, {
|
|
25613
|
+
beforeSeq,
|
|
25614
|
+
throughSeq: optionalInteger2(call.params.throughSeq),
|
|
25615
|
+
maxMessages
|
|
25616
|
+
});
|
|
25159
25617
|
}
|
|
25160
25618
|
if (call.method === "dsh/directoryList") {
|
|
25161
25619
|
return this.listCodexDirectory(call.params.path);
|
|
@@ -25658,103 +26116,60 @@ var CodexRemoteDomain = class {
|
|
|
25658
26116
|
return { thread: listed, ...typeof listed.cwd === "string" && listed.cwd.length > 0 ? { cwd: listed.cwd } : {} };
|
|
25659
26117
|
}
|
|
25660
26118
|
const thread = extractThread(result);
|
|
25661
|
-
if (thread === void 0 || thread.id !== threadId || !
|
|
26119
|
+
if (thread === void 0 || thread.id !== threadId || !this.threadAllowed(thread, authority)) {
|
|
25662
26120
|
throw new RpcError("CODEX_THREAD_NOT_ALLOWED", "The Codex thread is not available through this Remote Host.");
|
|
25663
26121
|
}
|
|
25664
26122
|
return { thread, ...typeof thread.cwd === "string" && thread.cwd.length > 0 ? { cwd: thread.cwd } : {} };
|
|
25665
26123
|
}
|
|
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", {
|
|
26124
|
+
historyPage(window, threadId, request) {
|
|
26125
|
+
const requested = request.maxMessages ?? HISTORY_PAGE_SIZES[0];
|
|
26126
|
+
const start = Math.max(1, Math.min(requested, HISTORY_PAGE_SIZES[0], window.acceptedPageSize ?? requested));
|
|
26127
|
+
const activeTurnId2 = this.turnOwners.get(threadId)?.turnId;
|
|
26128
|
+
for (const size of historyPageSizes(start)) {
|
|
26129
|
+
const page = paginateCodexNativeHistory(window.history, {
|
|
26130
|
+
beforeSeq: request.beforeSeq,
|
|
26131
|
+
throughSeq: request.throughSeq,
|
|
26132
|
+
maxMessages: size
|
|
26133
|
+
});
|
|
26134
|
+
const resolvedActiveTurnId = page.activeTurnId ?? activeTurnId2;
|
|
26135
|
+
const response = {
|
|
26136
|
+
...page,
|
|
26137
|
+
cursor: window.history.lastSeq,
|
|
26138
|
+
nextTurn: window.totalTurns + 1,
|
|
26139
|
+
hasMore: page.hasMore || window.olderCursor !== void 0,
|
|
26140
|
+
...resolvedActiveTurnId === void 0 ? {} : { activeTurnId: resolvedActiveTurnId },
|
|
26141
|
+
permissionPreset: this.permissionPresets.get(threadId) ?? null
|
|
26142
|
+
};
|
|
26143
|
+
const bytes = Buffer4.byteLength(JSON.stringify(response), "utf8");
|
|
26144
|
+
this.logger.debug("codex history page", {
|
|
25698
26145
|
threadId,
|
|
25699
|
-
|
|
25700
|
-
|
|
25701
|
-
|
|
25702
|
-
|
|
26146
|
+
requested,
|
|
26147
|
+
size,
|
|
26148
|
+
bytes,
|
|
26149
|
+
budget: HISTORY_BUDGET_BYTES,
|
|
26150
|
+
loadedTurns: window.loaded.size,
|
|
26151
|
+
totalTurns: window.totalTurns
|
|
25703
26152
|
});
|
|
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 });
|
|
25710
|
-
}
|
|
25711
|
-
cursor2 = typeof pageResult.nextCursor === "string" && pageResult.nextCursor.length > 0 ? pageResult.nextCursor : void 0;
|
|
25712
|
-
if (cursor2 === void 0) break;
|
|
25713
|
-
}
|
|
25714
|
-
return turns;
|
|
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;
|
|
26153
|
+
if (bytes <= HISTORY_BUDGET_BYTES) {
|
|
26154
|
+
window.acceptedPageSize = size;
|
|
26155
|
+
return response;
|
|
25734
26156
|
}
|
|
25735
|
-
} catch (error) {
|
|
25736
|
-
if (!isHistoryReadRecoverable(error)) throw error;
|
|
25737
|
-
this.logHistoryFallback(connectionId, "items", error);
|
|
25738
|
-
return fallbackItems;
|
|
25739
26157
|
}
|
|
25740
|
-
|
|
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
|
-
});
|
|
26158
|
+
throw new RpcError("RESPONSE_TOO_LARGE", "The Codex history page is too large for the secure Remote channel.");
|
|
25748
26159
|
}
|
|
25749
26160
|
async assertResultThreadAllowed(result) {
|
|
25750
26161
|
const thread = extractThread(result);
|
|
25751
26162
|
if (thread === void 0 || typeof thread.id !== "string") {
|
|
25752
26163
|
throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned an invalid thread.");
|
|
25753
26164
|
}
|
|
25754
|
-
if (!
|
|
26165
|
+
if (!this.threadAllowed(thread, await this.readWorkspaceAuthority())) {
|
|
25755
26166
|
throw new RpcError("CODEX_THREAD_NOT_ALLOWED", "The Codex thread is not available through this Remote Host.");
|
|
25756
26167
|
}
|
|
25757
26168
|
}
|
|
26169
|
+
/** The authority check, widened when unprojected Sessions are exposed. */
|
|
26170
|
+
threadAllowed(thread, authority) {
|
|
26171
|
+
return this.config.showUnprojectedSessions !== false || isThreadAllowedByWorkspaceAuthority(thread, authority);
|
|
26172
|
+
}
|
|
25758
26173
|
claimTurn(threadId, connectionId, method, turnId) {
|
|
25759
26174
|
const owner = this.turnOwners.get(threadId);
|
|
25760
26175
|
const peerDeviceId = this.peerDeviceIds.get(connectionId) ?? connectionId;
|
|
@@ -25895,7 +26310,7 @@ var CodexRemoteDomain = class {
|
|
|
25895
26310
|
}));
|
|
25896
26311
|
const thread = result.data.find((item) => item.id === threadId);
|
|
25897
26312
|
if (thread !== void 0) {
|
|
25898
|
-
return
|
|
26313
|
+
return this.threadAllowed(thread, authority) ? thread : void 0;
|
|
25899
26314
|
}
|
|
25900
26315
|
cursor2 = typeof result.nextCursor === "string" && result.nextCursor.length > 0 ? result.nextCursor : void 0;
|
|
25901
26316
|
if (cursor2 === void 0) break;
|
|
@@ -25906,6 +26321,14 @@ var CodexRemoteDomain = class {
|
|
|
25906
26321
|
if (!isAbsolute2(path)) {
|
|
25907
26322
|
throw new RpcError("CODEX_PATH_NOT_ALLOWED", "The CodeX working directory is not available as a Workspace.");
|
|
25908
26323
|
}
|
|
26324
|
+
if (this.config.showUnprojectedSessions !== false) {
|
|
26325
|
+
try {
|
|
26326
|
+
const candidate2 = await realpath(path);
|
|
26327
|
+
if ((await stat4(candidate2)).isDirectory()) return candidate2;
|
|
26328
|
+
} catch {
|
|
26329
|
+
}
|
|
26330
|
+
throw new RpcError("CODEX_PATH_NOT_ALLOWED", "The CodeX working directory is not available as a Workspace.");
|
|
26331
|
+
}
|
|
25909
26332
|
const paths = await this.listCodexWorkspacePaths();
|
|
25910
26333
|
const lexicalCandidate = resolve(path);
|
|
25911
26334
|
let candidate;
|
|
@@ -26006,7 +26429,7 @@ var CodexRemoteDomain = class {
|
|
|
26006
26429
|
return paths;
|
|
26007
26430
|
}
|
|
26008
26431
|
};
|
|
26009
|
-
function codexBinaryCandidates(configured, hostPlatform = process.platform, userHome =
|
|
26432
|
+
function codexBinaryCandidates(configured, hostPlatform = process.platform, userHome = homedir4(), localAppData = process.env.LOCALAPPDATA) {
|
|
26010
26433
|
if (configured !== "codex") return [configured];
|
|
26011
26434
|
if (hostPlatform === "win32") {
|
|
26012
26435
|
return [.../* @__PURE__ */ new Set([
|
|
@@ -26042,12 +26465,12 @@ function codexBinaryCandidates(configured, hostPlatform = process.platform, user
|
|
|
26042
26465
|
])];
|
|
26043
26466
|
}
|
|
26044
26467
|
function bundledWindowsCodex(userHome) {
|
|
26045
|
-
return bundledWindowsCodexAt(
|
|
26468
|
+
return bundledWindowsCodexAt(join9(userHome, "AppData", "Local"));
|
|
26046
26469
|
}
|
|
26047
26470
|
function bundledWindowsCodexAt(localAppData) {
|
|
26048
|
-
const bin =
|
|
26471
|
+
const bin = join9(localAppData, "OpenAI", "Codex", "bin");
|
|
26049
26472
|
try {
|
|
26050
|
-
const newest = readdirSync2(bin, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) =>
|
|
26473
|
+
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
26474
|
const candidate = newest[0]?.candidate;
|
|
26052
26475
|
return candidate === void 0 ? [] : [candidate];
|
|
26053
26476
|
} catch {
|
|
@@ -26120,13 +26543,30 @@ function sanitizeThreadList(result) {
|
|
|
26120
26543
|
...typeof result.backwardsCursor === "string" && result.backwardsCursor.length > 0 ? { backwardsCursor: result.backwardsCursor } : {}
|
|
26121
26544
|
};
|
|
26122
26545
|
}
|
|
26123
|
-
function filterThreadListByWorkspaceAuthority(result, authority) {
|
|
26546
|
+
function filterThreadListByWorkspaceAuthority(result, authority, includeUnprojected = false) {
|
|
26124
26547
|
if (!isRecord12(result) || !Array.isArray(result.data)) {
|
|
26125
26548
|
throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned an invalid thread list.");
|
|
26126
26549
|
}
|
|
26127
26550
|
return {
|
|
26128
26551
|
...result,
|
|
26129
|
-
data: result.data.map((record7) => isRecord12(record7) ? record7 : void 0).filter((thread) => thread !== void 0 && isThreadAllowedByWorkspaceAuthority(thread, authority))
|
|
26552
|
+
data: result.data.map((record7) => isRecord12(record7) ? record7 : void 0).filter((thread) => thread !== void 0 && (includeUnprojected || isThreadAllowedByWorkspaceAuthority(thread, authority)))
|
|
26553
|
+
};
|
|
26554
|
+
}
|
|
26555
|
+
function rebaseThreadListCwds(result, roots) {
|
|
26556
|
+
if (process.platform !== "win32" || roots.length === 0) return result;
|
|
26557
|
+
const ordered = [...roots].sort((left, right) => right.length - left.length);
|
|
26558
|
+
return {
|
|
26559
|
+
...result,
|
|
26560
|
+
data: result.data.map((thread) => {
|
|
26561
|
+
const cwd = typeof thread.cwd === "string" ? thread.cwd : void 0;
|
|
26562
|
+
if (cwd === void 0) return thread;
|
|
26563
|
+
const root = ordered.find((candidate) => containsCodexPath(candidate, cwd));
|
|
26564
|
+
if (root === void 0) return thread;
|
|
26565
|
+
const normalizedRoot = normalizeCodexPathForCompare(root);
|
|
26566
|
+
const normalizedCwd = normalizeCodexPathForCompare(cwd);
|
|
26567
|
+
const rebased = normalizedCwd === normalizedRoot ? root : normalizedCwd.startsWith(`${normalizedRoot}/`) || normalizedCwd.startsWith(`${normalizedRoot}\\`) ? `${root}${cwd.slice(root.length)}` : cwd;
|
|
26568
|
+
return rebased === cwd ? thread : { ...thread, cwd: rebased };
|
|
26569
|
+
})
|
|
26130
26570
|
};
|
|
26131
26571
|
}
|
|
26132
26572
|
function sanitizeProject(value) {
|
|
@@ -26283,12 +26723,6 @@ function canTryNextBinary(error) {
|
|
|
26283
26723
|
function isRecord12(value) {
|
|
26284
26724
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
26285
26725
|
}
|
|
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
26726
|
function isActiveWriterMessage(message) {
|
|
26293
26727
|
return message.toLowerCase().includes("active writer");
|
|
26294
26728
|
}
|
|
@@ -27402,12 +27836,12 @@ function isOversizedListing(value) {
|
|
|
27402
27836
|
|
|
27403
27837
|
// src/remote-directory-browser.ts
|
|
27404
27838
|
import { readdir as readdir3, stat as stat5 } from "node:fs/promises";
|
|
27405
|
-
import { homedir as
|
|
27839
|
+
import { homedir as homedir5, platform as platform2 } from "node:os";
|
|
27406
27840
|
import { basename as basename3, dirname as dirname5, isAbsolute as isAbsolute3, parse, resolve as resolve2 } from "node:path";
|
|
27407
27841
|
var MAX_ENTRIES = 500;
|
|
27408
27842
|
async function listRemoteDirectory(path, signal) {
|
|
27409
27843
|
signal?.throwIfAborted();
|
|
27410
|
-
const home = resolve2(
|
|
27844
|
+
const home = resolve2(homedir5());
|
|
27411
27845
|
const target2 = path === void 0 || path.trim() === "" ? home : resolve2(path);
|
|
27412
27846
|
if (!isAbsolute3(target2)) throw new Error("The remote directory path must be absolute.");
|
|
27413
27847
|
const rows = await readdir3(target2, { withFileTypes: true });
|
|
@@ -28271,7 +28705,8 @@ function concatChunks2(chunks, totalBytes) {
|
|
|
28271
28705
|
}
|
|
28272
28706
|
|
|
28273
28707
|
// src/harness-remote-bridge.ts
|
|
28274
|
-
import { Buffer as
|
|
28708
|
+
import { Buffer as Buffer5 } from "node:buffer";
|
|
28709
|
+
import { isAbsolute as isAbsolute4, join as join10, relative as relative2, sep } from "node:path";
|
|
28275
28710
|
var endpointSchema = external_exports.string().min(1).max(128).regex(/^(?:\$events(?:\/result)?|[A-Za-z0-9_$.-]+\/[A-Za-z0-9_$.-]+)$/);
|
|
28276
28711
|
var callSchema3 = external_exports.object({ endpoint: endpointSchema, payload: external_exports.unknown() }).strict();
|
|
28277
28712
|
var streamOpenSchema3 = external_exports.object({
|
|
@@ -28320,9 +28755,8 @@ var MAX_ACTIVE_STREAMS2 = MAX_ALPHA_STREAMS_PER_CONNECTION;
|
|
|
28320
28755
|
var MAX_ACTIVE_TRANSFERS2 = MAX_ACTIVE_TRANSFERS_PER_DIRECTION;
|
|
28321
28756
|
var INLINE_TRANSFER_RESPONSE_BYTES3 = 2 * 1024 * 1024;
|
|
28322
28757
|
var STREAM_FRAME_ENVELOPE_BYTES = 4 * 1024;
|
|
28323
|
-
var
|
|
28324
|
-
var
|
|
28325
|
-
var MAX_HISTORY_PAGE_MESSAGES = 500;
|
|
28758
|
+
var RESPONSE_ENVELOPE_BYTES2 = 8 * 1024;
|
|
28759
|
+
var WORKSPACE_FILE_ENDPOINTS = /* @__PURE__ */ new Set(["workspaceFiles/stat", "workspaceFiles/read", "workspaceFiles/readBytes", "workspaceFiles/list"]);
|
|
28326
28760
|
var REMOTE_HISTORY_CALL_ENDPOINTS = /* @__PURE__ */ new Set(["session/page", "session/history"]);
|
|
28327
28761
|
var REMOTE_HISTORY_STREAM_ENDPOINTS = /* @__PURE__ */ new Set(["session/follow"]);
|
|
28328
28762
|
var MAX_CACHED_HISTORY_PAGES = 64;
|
|
@@ -28403,19 +28837,22 @@ var HARNESS_REMOTE_ALLOWLIST = [
|
|
|
28403
28837
|
];
|
|
28404
28838
|
var allowedEndpoints = new Set(HARNESS_REMOTE_ALLOWLIST);
|
|
28405
28839
|
var HarnessRemoteBridge = class {
|
|
28406
|
-
constructor(gateway, publish, logger, harnessVersion, terminal = new TerminalPolicy(() => false, "", /* @__PURE__ */ new Map()), codexWorkspace) {
|
|
28840
|
+
constructor(gateway, publish, logger, harnessVersion, terminal = new TerminalPolicy(() => false, "", /* @__PURE__ */ new Map()), codexWorkspace, sessionHeaderCwd) {
|
|
28407
28841
|
this.gateway = gateway;
|
|
28408
28842
|
this.publish = publish;
|
|
28409
28843
|
this.logger = logger;
|
|
28410
28844
|
this.harnessVersion = harnessVersion;
|
|
28411
28845
|
this.terminal = terminal;
|
|
28412
28846
|
this.codexWorkspace = codexWorkspace;
|
|
28847
|
+
this.sessionHeaderCwd = sessionHeaderCwd;
|
|
28413
28848
|
}
|
|
28414
28849
|
streams = /* @__PURE__ */ new Map();
|
|
28415
28850
|
incomingTransfers = /* @__PURE__ */ new Map();
|
|
28416
28851
|
outgoingTransfers = /* @__PURE__ */ new Map();
|
|
28417
28852
|
/** Last page size that fit one secure message, per session, so probing is paid once. */
|
|
28418
28853
|
historyPageSizes = /* @__PURE__ */ new Map();
|
|
28854
|
+
/** Last working directory read per Session, so a file open does not re-project on every chunk. */
|
|
28855
|
+
sessionWorkingDirectories = /* @__PURE__ */ new Map();
|
|
28419
28856
|
/**
|
|
28420
28857
|
* Answer one remote Gateway call.
|
|
28421
28858
|
*
|
|
@@ -28456,7 +28893,7 @@ var HarnessRemoteBridge = class {
|
|
|
28456
28893
|
const signal = AbortSignal.timeout(6e4);
|
|
28457
28894
|
try {
|
|
28458
28895
|
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.
|
|
28896
|
+
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
28897
|
const result = params.endpoint === "directoryPicker/list" && needsDirectoryFallback(nativeResult) ? await this.directoryList(params.payload, signal) : nativeResult;
|
|
28461
28898
|
this.logger?.debug("harness remote call ok", {
|
|
28462
28899
|
endpoint: params.endpoint,
|
|
@@ -28583,7 +29020,7 @@ var HarnessRemoteBridge = class {
|
|
|
28583
29020
|
return {
|
|
28584
29021
|
transferId: params.transferId,
|
|
28585
29022
|
index: params.index,
|
|
28586
|
-
data:
|
|
29023
|
+
data: Buffer5.from(transfer.bytes.subarray(start, end)).toString("base64")
|
|
28587
29024
|
};
|
|
28588
29025
|
}
|
|
28589
29026
|
closeTransfer(input2) {
|
|
@@ -28703,7 +29140,7 @@ var HarnessRemoteBridge = class {
|
|
|
28703
29140
|
hasMore: value?.hasMore,
|
|
28704
29141
|
bytes
|
|
28705
29142
|
});
|
|
28706
|
-
if (bytes <= MAX_SECURE_MESSAGE_BYTES -
|
|
29143
|
+
if (bytes <= MAX_SECURE_MESSAGE_BYTES - RESPONSE_ENVELOPE_BYTES2) {
|
|
28707
29144
|
this.rememberHistoryPageSize(key, size);
|
|
28708
29145
|
return wire;
|
|
28709
29146
|
}
|
|
@@ -28722,7 +29159,7 @@ var HarnessRemoteBridge = class {
|
|
|
28722
29159
|
const key = historyPageKey(payload);
|
|
28723
29160
|
const requested = requestedMaxMessages(payload) ?? HISTORY_PAGE_SIZES[0];
|
|
28724
29161
|
const remembered = this.historyPageSizes.get(key);
|
|
28725
|
-
const start = Math.max(1, Math.min(requested,
|
|
29162
|
+
const start = Math.max(1, Math.min(requested, HISTORY_PAGE_SIZES[0], remembered ?? requested));
|
|
28726
29163
|
this.logger?.debug("history window", { key: key.slice(0, 120), requested, remembered, start });
|
|
28727
29164
|
return { key, sizes: historyPageSizes(start) };
|
|
28728
29165
|
}
|
|
@@ -28733,6 +29170,68 @@ var HarnessRemoteBridge = class {
|
|
|
28733
29170
|
}
|
|
28734
29171
|
this.historyPageSizes.set(key, size);
|
|
28735
29172
|
}
|
|
29173
|
+
/**
|
|
29174
|
+
* Dispatch a workspace-file call, re-rooting a path written against the Session's current
|
|
29175
|
+
* directory onto the header root the Host resolves against.
|
|
29176
|
+
*
|
|
29177
|
+
* DSH names the workspace root from the Session header, so a Session that changed its working
|
|
29178
|
+
* directory mid-run resolves relative links (and deliverables built from the root) against the
|
|
29179
|
+
* stale header. The live `workingDirectory` projection is the correction; when the re-rooted
|
|
29180
|
+
* path misses, the original path is tried so a file created before the change still opens.
|
|
29181
|
+
*/
|
|
29182
|
+
async dispatchWorkspaceFile(endpoint, payload, signal) {
|
|
29183
|
+
if (!WORKSPACE_FILE_ENDPOINTS.has(endpoint) || this.sessionHeaderCwd === void 0) {
|
|
29184
|
+
return this.gateway.dispatch(endpoint, payload, signal);
|
|
29185
|
+
}
|
|
29186
|
+
const location = workspaceFileLocation(payload);
|
|
29187
|
+
if (location === void 0) return this.gateway.dispatch(endpoint, payload, signal);
|
|
29188
|
+
const headerCwd = this.sessionHeaderCwd(location.sessionId);
|
|
29189
|
+
if (headerCwd === void 0) return this.gateway.dispatch(endpoint, payload, signal);
|
|
29190
|
+
const liveCwd = await this.sessionWorkingDirectory(location.sessionId, signal);
|
|
29191
|
+
if (liveCwd === void 0) return this.gateway.dispatch(endpoint, payload, signal);
|
|
29192
|
+
const nextPath = rerootWorkspacePath(headerCwd, liveCwd, location.path);
|
|
29193
|
+
if (nextPath === void 0 || nextPath === location.path) return this.gateway.dispatch(endpoint, payload, signal);
|
|
29194
|
+
const rewritten = replaceWorkspaceFilePath(payload, nextPath);
|
|
29195
|
+
if (rewritten === payload) return this.gateway.dispatch(endpoint, payload, signal);
|
|
29196
|
+
const rerooted = await this.gateway.dispatch(endpoint, rewritten, signal);
|
|
29197
|
+
if (!workspaceFileMiss(endpoint, rerooted)) {
|
|
29198
|
+
this.logger?.debug("workspace file path re-rooted", {
|
|
29199
|
+
endpoint,
|
|
29200
|
+
headerCwd,
|
|
29201
|
+
liveCwd,
|
|
29202
|
+
path: location.path,
|
|
29203
|
+
nextPath
|
|
29204
|
+
});
|
|
29205
|
+
return rerooted;
|
|
29206
|
+
}
|
|
29207
|
+
this.logger?.debug("workspace file path re-root missed; using the original path", {
|
|
29208
|
+
endpoint,
|
|
29209
|
+
path: location.path,
|
|
29210
|
+
nextPath
|
|
29211
|
+
});
|
|
29212
|
+
return this.gateway.dispatch(endpoint, payload, signal);
|
|
29213
|
+
}
|
|
29214
|
+
/** Read the Session's live working directory from its projections, cached briefly. */
|
|
29215
|
+
async sessionWorkingDirectory(sessionId, signal) {
|
|
29216
|
+
const cached = this.sessionWorkingDirectories.get(sessionId);
|
|
29217
|
+
if (cached !== void 0 && Date.now() - cached.at < 5e3) return cached.cwd;
|
|
29218
|
+
let cwd;
|
|
29219
|
+
try {
|
|
29220
|
+
const result = await this.gateway.dispatch("session/projections", { args: { request: { sessionId } } }, signal);
|
|
29221
|
+
const value = result.ok ? result.value : void 0;
|
|
29222
|
+
const values = isRecord14(value) && isRecord14(value.values) ? value.values : void 0;
|
|
29223
|
+
const raw = values?.workingDirectory;
|
|
29224
|
+
if (typeof raw === "string" && raw.length > 0) cwd = raw;
|
|
29225
|
+
} catch {
|
|
29226
|
+
cwd = void 0;
|
|
29227
|
+
}
|
|
29228
|
+
if (this.sessionWorkingDirectories.size >= MAX_CACHED_HISTORY_PAGES && !this.sessionWorkingDirectories.has(sessionId)) {
|
|
29229
|
+
const oldest = this.sessionWorkingDirectories.keys().next().value;
|
|
29230
|
+
if (oldest !== void 0) this.sessionWorkingDirectories.delete(oldest);
|
|
29231
|
+
}
|
|
29232
|
+
this.sessionWorkingDirectories.set(sessionId, { cwd, at: Date.now() });
|
|
29233
|
+
return cwd;
|
|
29234
|
+
}
|
|
28736
29235
|
closeStream(input2) {
|
|
28737
29236
|
const params = streamCloseSchema3.parse(input2);
|
|
28738
29237
|
const active = this.streams.get(params.streamId);
|
|
@@ -28821,11 +29320,6 @@ function windowedRequest(request, maxMessages) {
|
|
|
28821
29320
|
}
|
|
28822
29321
|
return next;
|
|
28823
29322
|
}
|
|
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
29323
|
function historyRequestOf(payload) {
|
|
28830
29324
|
let node = payload;
|
|
28831
29325
|
for (let depth = 0; depth < 4; depth += 1) {
|
|
@@ -28850,6 +29344,64 @@ function requestedMaxMessages(payload) {
|
|
|
28850
29344
|
const value = historyRequestOf(payload)?.maxMessages;
|
|
28851
29345
|
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : void 0;
|
|
28852
29346
|
}
|
|
29347
|
+
function workspaceFileLocation(payload) {
|
|
29348
|
+
if (!isRecord14(payload)) return void 0;
|
|
29349
|
+
if (Array.isArray(payload.args)) {
|
|
29350
|
+
if (payload.args.length >= 2 && typeof payload.args[0] === "string" && typeof payload.args[1] === "string") {
|
|
29351
|
+
return { sessionId: payload.args[0], path: payload.args[1] };
|
|
29352
|
+
}
|
|
29353
|
+
const first = payload.args[0];
|
|
29354
|
+
if (isRecord14(first)) return workspaceFileLocation(first);
|
|
29355
|
+
return void 0;
|
|
29356
|
+
}
|
|
29357
|
+
const candidate = isRecord14(payload.args) ? payload.args : isRecord14(payload.request) ? payload.request : payload;
|
|
29358
|
+
const sessionId = [candidate.workspaceFileScopeId, candidate.workspaceFileScope, candidate.sessionId].find((value) => typeof value === "string" && value.length > 0);
|
|
29359
|
+
const path = candidate.path;
|
|
29360
|
+
if (typeof sessionId !== "string" || typeof path !== "string" || path.length === 0) return void 0;
|
|
29361
|
+
return { sessionId, path };
|
|
29362
|
+
}
|
|
29363
|
+
function replaceWorkspaceFilePath(payload, nextPath) {
|
|
29364
|
+
if (!isRecord14(payload)) return payload;
|
|
29365
|
+
if (Array.isArray(payload.args)) {
|
|
29366
|
+
if (payload.args.length >= 2 && typeof payload.args[0] === "string" && typeof payload.args[1] === "string") {
|
|
29367
|
+
const args = [...payload.args];
|
|
29368
|
+
args[1] = nextPath;
|
|
29369
|
+
return { ...payload, args };
|
|
29370
|
+
}
|
|
29371
|
+
const first = payload.args[0];
|
|
29372
|
+
if (!isRecord14(first)) return payload;
|
|
29373
|
+
return { ...payload, args: [{ ...first, path: nextPath }, ...payload.args.slice(1)] };
|
|
29374
|
+
}
|
|
29375
|
+
if (isRecord14(payload.args)) return { ...payload, args: { ...payload.args, path: nextPath } };
|
|
29376
|
+
if (isRecord14(payload.request)) return { ...payload, request: { ...payload.request, path: nextPath } };
|
|
29377
|
+
return { ...payload, path: nextPath };
|
|
29378
|
+
}
|
|
29379
|
+
function workspaceFileMiss(endpoint, result) {
|
|
29380
|
+
if (!result.ok) return true;
|
|
29381
|
+
if (endpoint !== "workspaceFiles/stat") return false;
|
|
29382
|
+
const value = result.value;
|
|
29383
|
+
return isRecord14(value) && value.exists === false;
|
|
29384
|
+
}
|
|
29385
|
+
function rerootWorkspacePath(headerCwd, liveCwd, target2) {
|
|
29386
|
+
if (samePath(headerCwd, liveCwd)) return void 0;
|
|
29387
|
+
if (isAbsolute4(target2)) {
|
|
29388
|
+
const suffix = relative2(headerCwd, target2);
|
|
29389
|
+
if (suffix === "" || suffix === "." || isAbsolute4(suffix) || suffix.startsWith(`..${sep}`) || suffix === "..") return void 0;
|
|
29390
|
+
return join10(liveCwd, suffix);
|
|
29391
|
+
}
|
|
29392
|
+
const prefix = relative2(headerCwd, liveCwd);
|
|
29393
|
+
if (prefix !== "" && prefix !== "." && !isAbsolute4(prefix) && !prefix.startsWith(`..${sep}`) && prefix !== "..") {
|
|
29394
|
+
return join10(prefix, target2);
|
|
29395
|
+
}
|
|
29396
|
+
return join10(liveCwd, target2);
|
|
29397
|
+
}
|
|
29398
|
+
function samePath(left, right) {
|
|
29399
|
+
const normalize = (value) => {
|
|
29400
|
+
const trimmed = value.replace(/[\\/]+$/u, "");
|
|
29401
|
+
return process.platform === "win32" ? trimmed.toLowerCase() : trimmed;
|
|
29402
|
+
};
|
|
29403
|
+
return normalize(left) === normalize(right);
|
|
29404
|
+
}
|
|
28853
29405
|
function historyPageKey(payload) {
|
|
28854
29406
|
const request = historyRequestOf(payload);
|
|
28855
29407
|
if (request === void 0) return "default";
|
|
@@ -28862,10 +29414,10 @@ function historyPageKey(payload) {
|
|
|
28862
29414
|
}
|
|
28863
29415
|
}
|
|
28864
29416
|
function responseBytes(result) {
|
|
28865
|
-
return
|
|
29417
|
+
return Buffer5.byteLength(JSON.stringify(result), "utf8");
|
|
28866
29418
|
}
|
|
28867
29419
|
function streamFrameBytes(frame) {
|
|
28868
|
-
return
|
|
29420
|
+
return Buffer5.byteLength(JSON.stringify(frame), "utf8");
|
|
28869
29421
|
}
|
|
28870
29422
|
function remainingIterable(iterator) {
|
|
28871
29423
|
return { [Symbol.asyncIterator]: () => iterator };
|
|
@@ -28925,7 +29477,7 @@ async function dispatchCommandForHost(gateway, payload, signal, harnessVersion)
|
|
|
28925
29477
|
);
|
|
28926
29478
|
}
|
|
28927
29479
|
function decodeCanonicalBase643(value) {
|
|
28928
|
-
const bytes =
|
|
29480
|
+
const bytes = Buffer5.from(value, "base64");
|
|
28929
29481
|
if (bytes.toString("base64") !== value) throw new RpcError("INVALID_MESSAGE", "The Harness Remote transfer chunk is invalid.");
|
|
28930
29482
|
return bytes;
|
|
28931
29483
|
}
|
|
@@ -29073,7 +29625,7 @@ import { execFileSync as execFileSync2 } from "node:child_process";
|
|
|
29073
29625
|
// src/codex-workspace-bridge.ts
|
|
29074
29626
|
import { spawn as spawn4 } from "node:child_process";
|
|
29075
29627
|
import { realpath as realpath2, lstat, readdir as readdir4, readFile as readFile5, stat as stat6, watch } from "node:fs/promises";
|
|
29076
|
-
import { isAbsolute as
|
|
29628
|
+
import { isAbsolute as isAbsolute5, join as join11, relative as relative3, resolve as resolve3 } from "node:path";
|
|
29077
29629
|
var MAX_READ_BYTES = 4 * 1024 * 1024;
|
|
29078
29630
|
var MAX_INPUT_BYTES = 64 * 1024;
|
|
29079
29631
|
var MAX_COLS = 240;
|
|
@@ -29160,7 +29712,7 @@ var CodexWorkspaceBridge = class {
|
|
|
29160
29712
|
const entries = await readdir4(target2, { withFileTypes: true });
|
|
29161
29713
|
const result = [];
|
|
29162
29714
|
for (const entry of entries.slice(0, 500)) {
|
|
29163
|
-
const item =
|
|
29715
|
+
const item = join11(target2, entry.name);
|
|
29164
29716
|
const info2 = await lstat(item);
|
|
29165
29717
|
if (info2.isSymbolicLink()) continue;
|
|
29166
29718
|
result.push({ name: entry.name, type: info2.isDirectory() ? "directory" : info2.isFile() ? "file" : "other", ...info2.isFile() ? { size: info2.size } : {} });
|
|
@@ -29382,15 +29934,15 @@ var CodexWorkspaceBridge = class {
|
|
|
29382
29934
|
}
|
|
29383
29935
|
}
|
|
29384
29936
|
async safePath(root, path, directory) {
|
|
29385
|
-
const candidate =
|
|
29386
|
-
const rel =
|
|
29387
|
-
if (rel.startsWith("..") ||
|
|
29937
|
+
const candidate = isAbsolute5(path) ? resolve3(path) : resolve3(root, path);
|
|
29938
|
+
const rel = relative3(root, candidate);
|
|
29939
|
+
if (rel.startsWith("..") || isAbsolute5(rel)) throw new RpcError("CODEX_WORKSPACE_PATH_DENIED", "The requested workspace path is outside the CodeX workspace.");
|
|
29388
29940
|
try {
|
|
29389
29941
|
const info = await lstat(candidate);
|
|
29390
29942
|
if (info.isSymbolicLink()) throw new Error();
|
|
29391
29943
|
const canonical = await realpath2(candidate);
|
|
29392
|
-
const canonicalRel =
|
|
29393
|
-
if (canonicalRel.startsWith("..") ||
|
|
29944
|
+
const canonicalRel = relative3(root, canonical);
|
|
29945
|
+
if (canonicalRel.startsWith("..") || isAbsolute5(canonicalRel)) throw new Error();
|
|
29394
29946
|
if (directory && !info.isDirectory()) throw new Error();
|
|
29395
29947
|
return canonical;
|
|
29396
29948
|
} catch {
|
|
@@ -29531,7 +30083,7 @@ function readLimit(args) {
|
|
|
29531
30083
|
|
|
29532
30084
|
// src/service.ts
|
|
29533
30085
|
var HostPluginRuntime = class {
|
|
29534
|
-
constructor(config, identities, apiProxy, logger, localGateway, fileViewerHost, terminalSpawner) {
|
|
30086
|
+
constructor(config, identities, apiProxy, logger, localGateway, fileViewerHost, terminalSpawner, sessionSource) {
|
|
29535
30087
|
this.config = config;
|
|
29536
30088
|
this.identities = identities;
|
|
29537
30089
|
this.apiProxy = apiProxy;
|
|
@@ -29539,6 +30091,7 @@ var HostPluginRuntime = class {
|
|
|
29539
30091
|
this.localGateway = localGateway;
|
|
29540
30092
|
this.fileViewerHost = fileViewerHost;
|
|
29541
30093
|
this.terminalSpawner = terminalSpawner;
|
|
30094
|
+
this.sessionSource = sessionSource;
|
|
29542
30095
|
this.terminalEnabled = config.terminal.enabled;
|
|
29543
30096
|
this.loopbackPorts = [...config.loopback.ports];
|
|
29544
30097
|
this.paused = config.hostControl?.paused === true;
|
|
@@ -29563,7 +30116,8 @@ var HostPluginRuntime = class {
|
|
|
29563
30116
|
() => this.terminalEnabled,
|
|
29564
30117
|
this.codexWorkspaceState,
|
|
29565
30118
|
this.terminalSpawner
|
|
29566
|
-
)
|
|
30119
|
+
),
|
|
30120
|
+
this.sessionSource === void 0 ? void 0 : (sessionId) => this.sessionSource?.()?.get?.(sessionId)?.header?.cwd
|
|
29567
30121
|
) : void 0;
|
|
29568
30122
|
const fileViewer = new RemoteFileViewerBridge(
|
|
29569
30123
|
() => this.fileViewerHost?.(),
|
|
@@ -29985,7 +30539,7 @@ function isPlainRecord(value) {
|
|
|
29985
30539
|
// src/cli.ts
|
|
29986
30540
|
import { stat as stat7 } from "node:fs/promises";
|
|
29987
30541
|
import { hostname as hostname3 } from "node:os";
|
|
29988
|
-
import { join as
|
|
30542
|
+
import { join as join12 } from "node:path";
|
|
29989
30543
|
var QR_POLL_INTERVAL_MS = 2e3;
|
|
29990
30544
|
var TERMINAL_QR_MARGIN = 4;
|
|
29991
30545
|
async function runCli(args = process.argv.slice(2), dependencies = {}) {
|
|
@@ -30073,7 +30627,7 @@ async function status(args, runtime) {
|
|
|
30073
30627
|
`Server: ${serverUrl}`,
|
|
30074
30628
|
"Host control: enabled (dsh-TUI default)"
|
|
30075
30629
|
];
|
|
30076
|
-
if (!await exists3(
|
|
30630
|
+
if (!await exists3(join12(directory, "device.json"))) {
|
|
30077
30631
|
lines.push("Device: not initialized", "Authorization: logged out", "Credential: unavailable");
|
|
30078
30632
|
write(runtime.stdout, `${lines.join("\n")}
|
|
30079
30633
|
|
|
@@ -30117,7 +30671,7 @@ async function logout(args, runtime) {
|
|
|
30117
30671
|
const serverUrl = selectedServer();
|
|
30118
30672
|
const root = new IdentityStore({ env: runtime.env }).directory;
|
|
30119
30673
|
const directory = serverStorageDirectory(root, serverUrl, "host");
|
|
30120
|
-
if (!await exists3(
|
|
30674
|
+
if (!await exists3(join12(directory, "device.json"))) {
|
|
30121
30675
|
await new ServerCredentialStore(directory).clear();
|
|
30122
30676
|
write(runtime.stdout, "This Host is already logged out.\n");
|
|
30123
30677
|
return 0;
|
|
@@ -30782,6 +31336,7 @@ async function activate(ctx, readConfig, entryId, tuiBinding) {
|
|
|
30782
31336
|
const nativeTypertGateway = ctx.get("typertGateway");
|
|
30783
31337
|
const localTypertGateway = new TypertGatewaySwitch(nativeTypertGateway, { peer: peerFor }).local();
|
|
30784
31338
|
const subprocess = ctx.get("subprocess", false);
|
|
31339
|
+
const sessionSource = () => ctx.get("sessions", false);
|
|
30785
31340
|
const deepseekAccount = ctx.get("deepseekAccount", false);
|
|
30786
31341
|
const deepseekSession = deepseekAccount === void 0 ? void 0 : {
|
|
30787
31342
|
read: async () => {
|
|
@@ -30813,7 +31368,8 @@ async function activate(ctx, readConfig, entryId, tuiBinding) {
|
|
|
30813
31368
|
logger,
|
|
30814
31369
|
localTypertGateway,
|
|
30815
31370
|
() => ctx.get("fileViewerHost"),
|
|
30816
|
-
subprocess === void 0 ? void 0 : subprocessTerminalSpawner(subprocess)
|
|
31371
|
+
subprocess === void 0 ? void 0 : subprocessTerminalSpawner(subprocess),
|
|
31372
|
+
sessionSource
|
|
30817
31373
|
);
|
|
30818
31374
|
let clientRuntime;
|
|
30819
31375
|
const hostControl = runtime;
|