ds-harness-remote 0.4.4 → 0.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -14529,9 +14529,15 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14529
14529
  case "session/selectModel":
14530
14530
  return business(await this.selectModel(requestArg(args), signal));
14531
14531
  case "session/canOpenWorkspacePath":
14532
- return business(false);
14532
+ return business(this.selectedWorkspaceId !== void 0);
14533
14533
  case "session/openWorkspacePath":
14534
14534
  return business(failure("bad-request", "Opening Host paths is unavailable in CodeX mode."));
14535
+ case "host/describe":
14536
+ return business(success(await this.describeHost(signal)));
14537
+ case "host/listDirectory":
14538
+ return business(await this.listDirectory(requestArg(args), signal));
14539
+ case "directoryPicker/list":
14540
+ return business(await this.listDirectory(requestArg(args), signal));
14535
14541
  case "skills/list":
14536
14542
  return business(success({ items: [] }));
14537
14543
  case "commands/list":
@@ -15223,6 +15229,46 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15223
15229
  const workspace = catalog.workspaces.find((item) => sessionId !== void 0 && item.sessionIds.includes(sessionId));
15224
15230
  return workspace === void 0 ? failure("workspace-not-found", "The CodeX virtual Workspace was not found.") : success({ workspace: nativeWorkspace(workspace) });
15225
15231
  }
15232
+ selectedWorkspace(catalog) {
15233
+ return this.selectedWorkspaceId === void 0 ? void 0 : catalog.workspaces.find((item) => item.workspaceId === this.selectedWorkspaceId);
15234
+ }
15235
+ async describeHost(signal) {
15236
+ const catalog = await this.currentCatalog(signal);
15237
+ const workspace = this.selectedWorkspace(catalog);
15238
+ const models = await this.models(signal).catch(() => void 0);
15239
+ return {
15240
+ version: "CodeX Remote",
15241
+ cwd: workspace?.path ?? "",
15242
+ home: workspace?.path ?? "",
15243
+ provider: "CodeX",
15244
+ ...models === void 0 ? {} : { model: models.default.model },
15245
+ attachedSessions: catalog.sessions.length,
15246
+ canOpenPath: workspace !== void 0
15247
+ };
15248
+ }
15249
+ async listDirectory(request, signal) {
15250
+ const catalog = await this.currentCatalog(signal);
15251
+ const workspace = this.selectedWorkspace(catalog);
15252
+ if (workspace === void 0) return failure("workspace-not-found", "The CodeX virtual Workspace was not found.");
15253
+ const path = string(request.path) ?? workspace.path;
15254
+ if (!containsPath(workspace.path, path)) {
15255
+ return failure("workspace-not-found", "The selected directory is outside the current CodeX virtual Workspace.");
15256
+ }
15257
+ const listing = record(await this.client.request("dsh/directoryList", { path }, signal));
15258
+ if (!isCodexDirectoryListing(listing)) {
15259
+ return failure("internal", "CodeX Remote returned an invalid directory listing.");
15260
+ }
15261
+ const listedPaths = [
15262
+ listing.path,
15263
+ listing.home,
15264
+ ...listing.crumbs.map((item) => item.path),
15265
+ ...listing.entries.map((item) => item.path)
15266
+ ];
15267
+ if (listedPaths.some((path2) => !containsPath(workspace.path, path2))) {
15268
+ return failure("internal", "CodeX Remote returned a directory outside the current Workspace.");
15269
+ }
15270
+ return success(listing);
15271
+ }
15226
15272
  async archiveSession(request, signal) {
15227
15273
  const sessionId = requiredString(request.sessionId, "sessionId");
15228
15274
  await this.client.request("thread/archive", { threadId: nativeThreadId(sessionId) }, signal);
@@ -15233,8 +15279,12 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15233
15279
  async createSession(request, signal) {
15234
15280
  const catalog = await this.currentCatalog(signal);
15235
15281
  const workspaceId = string(request.workspaceId) ?? this.selectedWorkspaceId;
15236
- const cwd = string(request.cwd) ?? (workspaceId === void 0 ? void 0 : catalog.workspaces.find((item) => item.workspaceId === workspaceId)?.path);
15282
+ const workspace = workspaceId === void 0 ? void 0 : catalog.workspaces.find((item) => item.workspaceId === workspaceId);
15283
+ const cwd = string(request.cwd) ?? workspace?.path;
15237
15284
  if (cwd === void 0) return failure("workspace-not-found", "The CodeX virtual Workspace was not found.");
15285
+ if (workspace !== void 0 && !containsPath(workspace.path, cwd)) {
15286
+ return failure("workspace-not-found", "The selected directory is outside the current CodeX virtual Workspace.");
15287
+ }
15238
15288
  const directory = await this.models(signal);
15239
15289
  const selection = directory.default;
15240
15290
  const requestedPreset = CODEX_DEFAULT_PERMISSION;
@@ -15540,7 +15590,10 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15540
15590
  archiveSession: call("workspace.archiveSession")
15541
15591
  },
15542
15592
  subagents: {},
15543
- host: {},
15593
+ host: {
15594
+ describe: call("host.describe"),
15595
+ listDirectory: call("host.listDirectory")
15596
+ },
15544
15597
  skills: { list: call("skills.list") },
15545
15598
  agentPresets: {},
15546
15599
  goals: {},
@@ -16530,6 +16583,14 @@ function business(value) {
16530
16583
  }
16531
16584
  return ok(value);
16532
16585
  }
16586
+ function isCodexDirectoryListing(value) {
16587
+ const listing = record(value);
16588
+ return typeof listing.path === "string" && typeof listing.home === "string" && Array.isArray(listing.crumbs) && listing.crumbs.every(isCodexDirectoryEntry) && Array.isArray(listing.entries) && listing.entries.every(isCodexDirectoryEntry) && typeof listing.truncated === "boolean";
16589
+ }
16590
+ function isCodexDirectoryEntry(value) {
16591
+ const entry = record(value);
16592
+ return typeof entry.name === "string" && typeof entry.path === "string" && typeof entry.hidden === "boolean";
16593
+ }
16533
16594
  function fail(code, message, details = {}) {
16534
16595
  return { ok: false, error: { code, message, details } };
16535
16596
  }
@@ -16764,7 +16825,7 @@ function normalizeServerUrl(value) {
16764
16825
  }
16765
16826
 
16766
16827
  // src/version.ts
16767
- var PLUGIN_VERSION = "0.4.4";
16828
+ var PLUGIN_VERSION = "0.4.6";
16768
16829
 
16769
16830
  // src/server-api.ts
16770
16831
  var TERMINAL_CONTROL_CHARACTERS = /[\u0000-\u001f\u007f-\u009f]/u;
@@ -19370,24 +19431,24 @@ var IdentityStore = class {
19370
19431
  }
19371
19432
  if (!hasDevice) {
19372
19433
  const keys = generateKeyPair();
19373
- const record4 = { schemaVersion: 1, deviceId: uuidV7(), name: deviceName, publicKey: keys.publicKey };
19374
- await atomicJsonWrite(devicePath, record4, 384);
19434
+ const record5 = { schemaVersion: 1, deviceId: uuidV7(), name: deviceName, publicKey: keys.publicKey };
19435
+ await atomicJsonWrite(devicePath, record5, 384);
19375
19436
  await atomicTextWrite(keyPath, `${keys.privateKey}
19376
19437
  `, 384);
19377
19438
  }
19378
19439
  await assertPrivateMode(keyPath);
19379
19440
  try {
19380
- let record4 = identitySchema.parse(JSON.parse(await readFile(devicePath, "utf8")));
19441
+ let record5 = identitySchema.parse(JSON.parse(await readFile(devicePath, "utf8")));
19381
19442
  const privateKey = (await readFile(keyPath, "utf8")).trim();
19382
19443
  const regenerated = generateKeyPair(fromBase64Url2(privateKey));
19383
- if (regenerated.publicKey !== record4.publicKey) {
19444
+ if (regenerated.publicKey !== record5.publicKey) {
19384
19445
  throw new IdentityInvalidError("device public and private keys do not match");
19385
19446
  }
19386
- if (record4.name !== deviceName) {
19387
- record4 = { ...record4, name: deviceName };
19388
- await atomicJsonWrite(devicePath, record4, 384);
19447
+ if (record5.name !== deviceName) {
19448
+ record5 = { ...record5, name: deviceName };
19449
+ await atomicJsonWrite(devicePath, record5, 384);
19389
19450
  }
19390
- this.identity = { ...record4, privateKey, fingerprint: fingerprint(record4.publicKey) };
19451
+ this.identity = { ...record5, privateKey, fingerprint: fingerprint(record5.publicKey) };
19391
19452
  await this.loadPeers();
19392
19453
  return this.identity;
19393
19454
  } catch (error) {
@@ -19529,10 +19590,10 @@ var ServerCredentialStore = class {
19529
19590
  return parsed.serverUrl === serverUrl && parsed.deviceId === deviceId ? parsed : void 0;
19530
19591
  }
19531
19592
  async save(credentials) {
19532
- const record4 = credentialSchema.parse({ schemaVersion: 1, ...credentials });
19533
- await atomicWrite(this.path, `${JSON.stringify(record4, null, 2)}
19593
+ const record5 = credentialSchema.parse({ schemaVersion: 1, ...credentials });
19594
+ await atomicWrite(this.path, `${JSON.stringify(record5, null, 2)}
19534
19595
  `);
19535
- return record4;
19596
+ return record5;
19536
19597
  }
19537
19598
  async clear() {
19538
19599
  await rm2(this.path, { force: true });
@@ -21334,6 +21395,9 @@ var commandExecuteSchema = external_exports.object({
21334
21395
  var commandListSchema = external_exports.object({
21335
21396
  agentId: external_exports.string().min(1).max(128)
21336
21397
  }).strict();
21398
+ var directoryListSchema = external_exports.object({
21399
+ path: external_exports.string().min(1).max(4096).optional()
21400
+ }).strict();
21337
21401
  var credentialRefSchema = external_exports.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/, "Invalid credential reference name").max(128);
21338
21402
  var CONFIG_PLANE_SETTINGS_BYTES = 64 * 1024;
21339
21403
  var CONFIG_PLANE_MAX_OPS = 64;
@@ -21463,10 +21527,18 @@ var HarnessApiBridge = class {
21463
21527
  answer;
21464
21528
  async call(input2) {
21465
21529
  const params = callSchema2.parse(input2);
21530
+ const signal = AbortSignal.timeout(NATIVE_CALL_TIMEOUT_MS);
21466
21531
  const method = this.methods.get(params.method);
21467
- if (method === void 0) throw deniedMethod(params.method);
21532
+ if (method === void 0) {
21533
+ if (params.method === "host.listDirectory") {
21534
+ return this.callDirectoryFallback(params, signal);
21535
+ }
21536
+ if (params.method === "host.describe") {
21537
+ return this.describeFallback(params.rpcId);
21538
+ }
21539
+ throw deniedMethod(params.method);
21540
+ }
21468
21541
  const startedAt = performance.now();
21469
- const signal = AbortSignal.timeout(NATIVE_CALL_TIMEOUT_MS);
21470
21542
  const request = { rpcId: params.rpcId, payload: params.payload };
21471
21543
  try {
21472
21544
  const callWithTimeout = (overridePayload) => withTimeout(
@@ -21481,11 +21553,11 @@ var HarnessApiBridge = class {
21481
21553
  response = await callWithTimeout(request.payload);
21482
21554
  }
21483
21555
  if (params.method === "host.listDirectory" && needsRemoteDirectoryFallback(response)) {
21484
- const payload = typeof params.payload === "object" && params.payload !== null ? params.payload : {};
21485
- const value = await listRemoteDirectory(typeof payload.path === "string" ? payload.path : void 0, signal);
21486
- response = { rpcId: params.rpcId, result: { ok: true, value } };
21556
+ response = await this.callDirectoryFallback(params, signal);
21557
+ }
21558
+ if (params.method === "host.describe") {
21559
+ response = normalizeHostDescribe(response, this.harnessVersion, true);
21487
21560
  }
21488
- if (params.method === "host.describe") response = normalizeHostDescribeVersion(response, this.harnessVersion);
21489
21561
  this.logger?.debug("harness api call ok", {
21490
21562
  method: params.method,
21491
21563
  durationMs: Math.round(performance.now() - startedAt)
@@ -21502,6 +21574,26 @@ var HarnessApiBridge = class {
21502
21574
  throw error;
21503
21575
  }
21504
21576
  }
21577
+ async callDirectoryFallback(params, signal) {
21578
+ const payload = directoryListSchema.parse(params.payload);
21579
+ const value = await listRemoteDirectory(payload.path, signal);
21580
+ return { rpcId: params.rpcId, result: { ok: true, value } };
21581
+ }
21582
+ describeFallback(rpcId) {
21583
+ return {
21584
+ rpcId,
21585
+ result: {
21586
+ ok: true,
21587
+ value: {
21588
+ version: this.harnessVersion ?? "0.0.1",
21589
+ cwd: "",
21590
+ home: "",
21591
+ attachedSessions: 0,
21592
+ canOpenPath: true
21593
+ }
21594
+ }
21595
+ };
21596
+ }
21505
21597
  openTransfer(input2) {
21506
21598
  this.pruneTransfers();
21507
21599
  const params = transferOpenSchema.parse(input2);
@@ -21716,8 +21808,8 @@ function needsRemoteDirectoryFallback(response) {
21716
21808
  const result = response.result;
21717
21809
  return typeof result === "object" && result !== null && "ok" in result && result.ok === false && "error" in result && typeof result.error === "object" && result.error !== null && "code" in result.error && result.error.code === "directory-picker-unavailable";
21718
21810
  }
21719
- function normalizeHostDescribeVersion(response, harnessVersion) {
21720
- if (!response.result.ok || harnessVersion === void 0) return response;
21811
+ function normalizeHostDescribe(response, harnessVersion, canListDirectory) {
21812
+ if (!response.result.ok) return response;
21721
21813
  const value = response.result.value;
21722
21814
  if (typeof value !== "object" || value === null || Array.isArray(value)) return response;
21723
21815
  const description = value;
@@ -21725,12 +21817,19 @@ function normalizeHostDescribeVersion(response, harnessVersion) {
21725
21817
  normalizeHarnessVersion(description.version),
21726
21818
  harnessVersion
21727
21819
  );
21728
- if (selectedVersion === void 0 || description.version === selectedVersion) return response;
21820
+ const versionChanged = selectedVersion !== void 0 && description.version !== selectedVersion;
21821
+ const canOpenPathChanged = canListDirectory && description.canOpenPath !== true;
21822
+ if (!versionChanged && !canOpenPathChanged) return response;
21823
+ const normalized = {
21824
+ ...description,
21825
+ ...versionChanged ? { version: selectedVersion } : {},
21826
+ ...canOpenPathChanged ? { canOpenPath: true } : {}
21827
+ };
21729
21828
  return {
21730
21829
  ...response,
21731
21830
  result: {
21732
21831
  ...response.result,
21733
- value: { ...description, version: selectedVersion }
21832
+ value: normalized
21734
21833
  }
21735
21834
  };
21736
21835
  }
@@ -22061,6 +22160,9 @@ var transferReadSchema2 = external_exports.object({
22061
22160
  transferId: external_exports.string().uuid(),
22062
22161
  index: external_exports.number().int().nonnegative()
22063
22162
  }).strict();
22163
+ var directoryListSchema2 = external_exports.object({
22164
+ path: external_exports.string().min(1).max(4096).optional()
22165
+ }).strict();
22064
22166
  var MAX_ACTIVE_STREAMS = 16;
22065
22167
  var MAX_ACTIVE_TRANSFERS = 2;
22066
22168
  var TRANSFER_IDLE_MS = 2 * 6e4;
@@ -22093,6 +22195,7 @@ var HARNESS_REMOTE_ALLOWLIST = [
22093
22195
  "pluginInventory/list",
22094
22196
  "session/attachment",
22095
22197
  "session/cancel",
22198
+ "session/canOpenWorkspacePath",
22096
22199
  "session/control",
22097
22200
  "session/create",
22098
22201
  "session/follow",
@@ -22135,15 +22238,29 @@ var HarnessRemoteBridge = class {
22135
22238
  async call(input2) {
22136
22239
  const params = callSchema3.parse(input2);
22137
22240
  this.assertAllowed(params.endpoint);
22241
+ if (params.endpoint === "session/canOpenWorkspacePath") {
22242
+ return { ok: true, value: true };
22243
+ }
22138
22244
  const startedAt = performance.now();
22245
+ const signal = AbortSignal.timeout(6e4);
22139
22246
  try {
22140
- const result = await this.gateway.dispatch(params.endpoint, params.payload, AbortSignal.timeout(6e4));
22247
+ const nativeResult = await this.gateway.dispatch(params.endpoint, params.payload, signal);
22248
+ const result = params.endpoint === "directoryPicker/list" && needsDirectoryFallback(nativeResult) ? await this.directoryList(params.payload, signal) : nativeResult;
22141
22249
  this.logger?.debug("harness remote call ok", {
22142
22250
  endpoint: params.endpoint,
22143
22251
  durationMs: Math.round(performance.now() - startedAt)
22144
22252
  });
22145
22253
  return result;
22146
22254
  } catch (error) {
22255
+ if (params.endpoint === "directoryPicker/list") {
22256
+ const result = await this.directoryList(params.payload, signal);
22257
+ this.logger?.debug("harness remote call ok", {
22258
+ endpoint: params.endpoint,
22259
+ durationMs: Math.round(performance.now() - startedAt),
22260
+ fallback: "directory-browser"
22261
+ });
22262
+ return result;
22263
+ }
22147
22264
  this.logger?.warn("harness remote call failed", {
22148
22265
  endpoint: params.endpoint,
22149
22266
  durationMs: Math.round(performance.now() - startedAt),
@@ -22152,6 +22269,11 @@ var HarnessRemoteBridge = class {
22152
22269
  throw error;
22153
22270
  }
22154
22271
  }
22272
+ async directoryList(payload, signal) {
22273
+ const args = requestArgs(payload);
22274
+ const params = directoryListSchema2.parse(args);
22275
+ return { ok: true, value: await listRemoteDirectory(params.path, signal) };
22276
+ }
22155
22277
  openTransfer(input2) {
22156
22278
  this.pruneTransfers();
22157
22279
  const params = transferOpenSchema2.parse(input2);
@@ -22335,12 +22457,29 @@ function concatChunks2(chunks, totalBytes) {
22335
22457
  }
22336
22458
  return result;
22337
22459
  }
22460
+ function needsDirectoryFallback(result) {
22461
+ if (result.ok) return false;
22462
+ const code = result.error.code.toLocaleLowerCase();
22463
+ const message = result.error.message.toLocaleLowerCase();
22464
+ return code.includes("capability") || code === "directory-picker-unavailable" || message.includes("browser capability") || message.includes("brower capability") || message.includes("directory-picker-unavailable");
22465
+ }
22466
+ function requestArgs(payload) {
22467
+ const root = record4(payload);
22468
+ const args = isRecord7(root.args) ? root.args : root;
22469
+ return record4(args.request ?? args._request ?? args);
22470
+ }
22471
+ function record4(value) {
22472
+ return isRecord7(value) ? value : {};
22473
+ }
22474
+ function isRecord7(value) {
22475
+ return typeof value === "object" && value !== null && !Array.isArray(value);
22476
+ }
22338
22477
 
22339
22478
  // src/codex/domain.ts
22340
22479
  import { randomUUID } from "node:crypto";
22341
- import { realpath, stat as stat4 } from "node:fs/promises";
22480
+ import { readdir as readdir2, realpath, stat as stat4 } from "node:fs/promises";
22342
22481
  import { homedir as homedir3 } from "node:os";
22343
- import { isAbsolute as isAbsolute3, join as join5, resolve as resolve2 } from "node:path";
22482
+ import { basename as basename3, isAbsolute as isAbsolute3, join as join5, relative, resolve as resolve2 } from "node:path";
22344
22483
 
22345
22484
  // src/codex/app-server.ts
22346
22485
  import { spawn as spawn2 } from "node:child_process";
@@ -22526,7 +22665,7 @@ var CodexAppServerClient = class {
22526
22665
  this.handleProcessFailure("CODEX_INVALID_RESPONSE", new Error("Codex App Server emitted invalid JSON."));
22527
22666
  return;
22528
22667
  }
22529
- if (!isRecord7(value)) {
22668
+ if (!isRecord8(value)) {
22530
22669
  this.handleProcessFailure("CODEX_INVALID_RESPONSE", new Error("Codex App Server emitted an invalid message."));
22531
22670
  return;
22532
22671
  }
@@ -22571,12 +22710,12 @@ var CodexAppServerClient = class {
22571
22710
  }
22572
22711
  };
22573
22712
  function safeUpstreamError(value) {
22574
- if (!isRecord7(value) || typeof value.message !== "string") return "Codex App Server rejected the request.";
22713
+ if (!isRecord8(value) || typeof value.message !== "string") return "Codex App Server rejected the request.";
22575
22714
  const message = value.message.toLowerCase();
22576
22715
  if (message.includes("active writer")) return "Codex thread already has an active writer.";
22577
22716
  return message.includes("not initialized") ? "Codex App Server is not initialized." : "Codex App Server rejected the request.";
22578
22717
  }
22579
- function isRecord7(value) {
22718
+ function isRecord8(value) {
22580
22719
  return typeof value === "object" && value !== null && !Array.isArray(value);
22581
22720
  }
22582
22721
 
@@ -22629,6 +22768,9 @@ var schemas = {
22629
22768
  throughSeq: external_exports.number().int().min(-1).optional(),
22630
22769
  maxMessages: external_exports.number().int().min(1).max(200).optional()
22631
22770
  }).strict(),
22771
+ "dsh/directoryList": external_exports.object({
22772
+ path: external_exports.string().min(1).max(4096)
22773
+ }).strict(),
22632
22774
  "thread/start": external_exports.object({
22633
22775
  cwd: external_exports.string().min(1).max(4096),
22634
22776
  model: external_exports.string().min(1).max(128).optional(),
@@ -22935,15 +23077,15 @@ function decodeCanonicalBase643(value) {
22935
23077
  }
22936
23078
  return decoded;
22937
23079
  }
22938
- function isRecord8(value) {
23080
+ function isRecord9(value) {
22939
23081
  return typeof value === "object" && value !== null && !Array.isArray(value);
22940
23082
  }
22941
23083
  function safeErrorCode2(error) {
22942
- if (isRecord8(error) && typeof error.code === "string") return error.code;
23084
+ if (isRecord9(error) && typeof error.code === "string") return error.code;
22943
23085
  return "UNKNOWN";
22944
23086
  }
22945
23087
  function safeMethod(input2) {
22946
- return isRecord8(input2) && typeof input2.method === "string" ? input2.method : "invalid";
23088
+ return isRecord9(input2) && typeof input2.method === "string" ? input2.method : "invalid";
22947
23089
  }
22948
23090
  function concatChunks3(chunks, totalBytes) {
22949
23091
  const output = new Uint8Array(totalBytes);
@@ -22962,6 +23104,7 @@ var CODEX_PAGE_LIMIT2 = 100;
22962
23104
  var MAX_CODEX_PAGES2 = 32;
22963
23105
  var CODEX_HISTORY_PAGE_LIMIT = 25;
22964
23106
  var MAX_CODEX_HISTORY_PAGES = 64;
23107
+ var CODEX_DIRECTORY_ENTRY_LIMIT = 500;
22965
23108
  var CodexRemoteDomain = class {
22966
23109
  constructor(config, logger, createAppServer = (binary, targetLogger) => new CodexAppServerClient(binary, targetLogger), restartDelaysMs = DEFAULT_RESTART_DELAYS_MS) {
22967
23110
  this.config = config;
@@ -23061,6 +23204,9 @@ var CodexRemoteDomain = class {
23061
23204
  const activeTurnId2 = typeof page.activeTurnId === "string" ? page.activeTurnId : this.turnOwners.get(threadId)?.turnId;
23062
23205
  return activeTurnId2 === void 0 ? page : { ...page, activeTurnId: activeTurnId2 };
23063
23206
  }
23207
+ if (call.method === "dsh/directoryList") {
23208
+ return this.listCodexDirectory(call.params.path);
23209
+ }
23064
23210
  const allowedThread = threadId === void 0 ? void 0 : await this.readKnownThread(threadId);
23065
23211
  if (call.method === "thread/read") {
23066
23212
  return this.callUpstream(call.method, call.params);
@@ -23385,9 +23531,9 @@ var CodexRemoteDomain = class {
23385
23531
  itemsView,
23386
23532
  ...cursor2 === void 0 ? {} : { cursor: cursor2 }
23387
23533
  });
23388
- const pageResult = isRecord9(result) ? result : {};
23534
+ const pageResult = isRecord10(result) ? result : {};
23389
23535
  for (const rawTurn of array2(pageResult.data)) {
23390
- if (!isRecord9(rawTurn)) continue;
23536
+ if (!isRecord10(rawTurn)) continue;
23391
23537
  const turnId = typeof rawTurn.id === "string" ? rawTurn.id : void 0;
23392
23538
  const items = rawTurn.itemsView === "full" || turnId === void 0 ? array2(rawTurn.items) : await this.readThreadItems(connectionId, threadId, turnId, array2(rawTurn.items));
23393
23539
  turns.push({ ...rawTurn, items });
@@ -23409,9 +23555,9 @@ var CodexRemoteDomain = class {
23409
23555
  sortDirection: "asc",
23410
23556
  ...cursor2 === void 0 ? {} : { cursor: cursor2 }
23411
23557
  });
23412
- const pageResult = isRecord9(result) ? result : {};
23558
+ const pageResult = isRecord10(result) ? result : {};
23413
23559
  for (const entry of array2(pageResult.data)) {
23414
- if (isRecord9(entry) && entry.item !== void 0) items.push(entry.item);
23560
+ if (isRecord10(entry) && entry.item !== void 0) items.push(entry.item);
23415
23561
  }
23416
23562
  cursor2 = typeof pageResult.nextCursor === "string" && pageResult.nextCursor.length > 0 ? pageResult.nextCursor : void 0;
23417
23563
  if (cursor2 === void 0) break;
@@ -23475,7 +23621,7 @@ var CodexRemoteDomain = class {
23475
23621
  return [...this.peers.values()].some((peer) => peer.hasThreadSubscription(threadId));
23476
23622
  }
23477
23623
  resolveUpstreamApproval(params) {
23478
- if (!isRecord9(params) || typeof params.requestId !== "string" && typeof params.requestId !== "number") return;
23624
+ if (!isRecord10(params) || typeof params.requestId !== "string" && typeof params.requestId !== "number") return;
23479
23625
  for (const [handle, approval] of this.approvals) {
23480
23626
  if (approval.upstreamId === params.requestId) this.approvals.delete(handle);
23481
23627
  }
@@ -23602,6 +23748,68 @@ var CodexRemoteDomain = class {
23602
23748
  }
23603
23749
  throw new RpcError("CODEX_PATH_NOT_ALLOWED", "The CodeX working directory is not available as a Workspace.");
23604
23750
  }
23751
+ async listCodexDirectory(path) {
23752
+ const target = await this.resolveCodexDirectory(path);
23753
+ const rows = await readdir2(target.path, { withFileTypes: true }).catch(() => void 0);
23754
+ if (rows === void 0) {
23755
+ throw new RpcError("CODEX_PATH_NOT_ALLOWED", "The CodeX working directory is not available as a Workspace.");
23756
+ }
23757
+ const entries = [];
23758
+ for (const row of rows) {
23759
+ const child = resolve2(target.path, row.name);
23760
+ let directory = row.isDirectory();
23761
+ if (!directory && row.isSymbolicLink()) {
23762
+ directory = await stat4(child).then((value) => value.isDirectory()).catch(() => false);
23763
+ }
23764
+ if (!directory) continue;
23765
+ try {
23766
+ const canonicalChild = await realpath(child);
23767
+ if (!(await stat4(canonicalChild)).isDirectory()) continue;
23768
+ if (!containsCodexPath(target.canonicalRoot, canonicalChild)) continue;
23769
+ } catch {
23770
+ continue;
23771
+ }
23772
+ entries.push({
23773
+ name: row.name,
23774
+ path: child,
23775
+ hidden: process.platform !== "win32" && row.name.startsWith(".")
23776
+ });
23777
+ }
23778
+ entries.sort((left, right) => left.name.localeCompare(right.name, void 0, { sensitivity: "base" }));
23779
+ return {
23780
+ path: target.path,
23781
+ home: target.root,
23782
+ crumbs: codexDirectoryCrumbs(target.root, target.path),
23783
+ entries: entries.slice(0, CODEX_DIRECTORY_ENTRY_LIMIT),
23784
+ truncated: entries.length > CODEX_DIRECTORY_ENTRY_LIMIT
23785
+ };
23786
+ }
23787
+ async resolveCodexDirectory(path) {
23788
+ if (!isAbsolute3(path)) {
23789
+ throw new RpcError("CODEX_PATH_NOT_ALLOWED", "The CodeX working directory is not available as a Workspace.");
23790
+ }
23791
+ const authority = await this.readWorkspaceAuthority();
23792
+ const lexicalCandidate = resolve2(path);
23793
+ let canonicalCandidate;
23794
+ try {
23795
+ canonicalCandidate = await realpath(path);
23796
+ if (!(await stat4(canonicalCandidate)).isDirectory()) throw new Error("not a directory");
23797
+ } catch {
23798
+ throw new RpcError("CODEX_PATH_NOT_ALLOWED", "The CodeX working directory is not available as a Workspace.");
23799
+ }
23800
+ for (const root of authority.roots) {
23801
+ try {
23802
+ const lexicalRoot = resolve2(root);
23803
+ if (!containsCodexPath(lexicalRoot, lexicalCandidate)) continue;
23804
+ const canonicalRoot = await realpath(root);
23805
+ if (containsCodexPath(canonicalRoot, canonicalCandidate)) {
23806
+ return { path: lexicalCandidate, root: lexicalRoot, canonicalRoot };
23807
+ }
23808
+ } catch {
23809
+ }
23810
+ }
23811
+ throw new RpcError("CODEX_PATH_NOT_ALLOWED", "The CodeX working directory is not available as a Workspace.");
23812
+ }
23605
23813
  async listCodexWorkspacePaths() {
23606
23814
  const paths = /* @__PURE__ */ new Map();
23607
23815
  const authority = await this.readWorkspaceAuthority();
@@ -23618,24 +23826,24 @@ function codexBinaryCandidates(configured, hostPlatform = process.platform, user
23618
23826
  ])];
23619
23827
  }
23620
23828
  function parseCallEnvelope(input2) {
23621
- if (!isRecord9(input2) || typeof input2.method !== "string" || !("params" in input2) || Object.keys(input2).some((key) => key !== "method" && key !== "params")) {
23829
+ if (!isRecord10(input2) || typeof input2.method !== "string" || !("params" in input2) || Object.keys(input2).some((key) => key !== "method" && key !== "params")) {
23622
23830
  throw new RpcError("INVALID_MESSAGE", "The Codex call envelope is invalid.");
23623
23831
  }
23624
23832
  return { method: input2.method, params: input2.params };
23625
23833
  }
23626
23834
  function parseRespond(input2) {
23627
- if (!isRecord9(input2) || typeof input2.requestHandle !== "string" || !["accept", "decline", "cancel"].includes(String(input2.decision)) || Object.keys(input2).some((key) => key !== "requestHandle" && key !== "decision")) {
23835
+ if (!isRecord10(input2) || typeof input2.requestHandle !== "string" || !["accept", "decline", "cancel"].includes(String(input2.decision)) || Object.keys(input2).some((key) => key !== "requestHandle" && key !== "decision")) {
23628
23836
  throw new RpcError("INVALID_MESSAGE", "The Codex approval response is invalid.");
23629
23837
  }
23630
23838
  return input2;
23631
23839
  }
23632
23840
  function accountCanRun(result) {
23633
- if (!isRecord9(result) || typeof result.requiresOpenaiAuth !== "boolean") return false;
23634
- return result.requiresOpenaiAuth === false || isRecord9(result.account);
23841
+ if (!isRecord10(result) || typeof result.requiresOpenaiAuth !== "boolean") return false;
23842
+ return result.requiresOpenaiAuth === false || isRecord10(result.account);
23635
23843
  }
23636
23844
  function sanitizeAccount(result) {
23637
- if (!isRecord9(result)) throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned invalid account state.");
23638
- const account = isRecord9(result.account) ? result.account : void 0;
23845
+ if (!isRecord10(result)) throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned invalid account state.");
23846
+ const account = isRecord10(result.account) ? result.account : void 0;
23639
23847
  return {
23640
23848
  authenticated: account !== void 0 || result.requiresOpenaiAuth === false,
23641
23849
  requiresOpenaiAuth: result.requiresOpenaiAuth === true,
@@ -23648,11 +23856,11 @@ function sanitizeAccount(result) {
23648
23856
  };
23649
23857
  }
23650
23858
  function sanitizeThreadList(result) {
23651
- if (!isRecord9(result) || !Array.isArray(result.data)) {
23859
+ if (!isRecord10(result) || !Array.isArray(result.data)) {
23652
23860
  throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned an invalid thread list.");
23653
23861
  }
23654
23862
  const data = result.data.flatMap((value) => {
23655
- if (!isRecord9(value) || typeof value.id !== "string") return [];
23863
+ if (!isRecord10(value) || typeof value.id !== "string") return [];
23656
23864
  return [{
23657
23865
  id: value.id,
23658
23866
  ...typeof value.sessionId === "string" ? { sessionId: value.sessionId } : {},
@@ -23664,7 +23872,7 @@ function sanitizeThreadList(result) {
23664
23872
  ...typeof value.updatedAt === "number" && Number.isFinite(value.updatedAt) ? { updatedAt: value.updatedAt } : {},
23665
23873
  ...typeof value.archived === "boolean" ? { archived: value.archived } : {},
23666
23874
  ...typeof value.isPinned === "boolean" ? { isPinned: value.isPinned } : {},
23667
- ...isRecord9(value.status) ? { status: value.status } : {}
23875
+ ...isRecord10(value.status) ? { status: value.status } : {}
23668
23876
  }];
23669
23877
  });
23670
23878
  return {
@@ -23674,22 +23882,22 @@ function sanitizeThreadList(result) {
23674
23882
  };
23675
23883
  }
23676
23884
  function filterThreadListByWorkspaceAuthority(result, authority) {
23677
- if (!isRecord9(result) || !Array.isArray(result.data)) {
23885
+ if (!isRecord10(result) || !Array.isArray(result.data)) {
23678
23886
  throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned an invalid thread list.");
23679
23887
  }
23680
23888
  return {
23681
23889
  ...result,
23682
- data: result.data.map((record4) => isRecord9(record4) ? record4 : void 0).filter((thread) => thread !== void 0 && isThreadAllowedByWorkspaceAuthority(thread, authority))
23890
+ data: result.data.map((record5) => isRecord10(record5) ? record5 : void 0).filter((thread) => thread !== void 0 && isThreadAllowedByWorkspaceAuthority(thread, authority))
23683
23891
  };
23684
23892
  }
23685
23893
  function sanitizeProjectList(result) {
23686
- if (!isRecord9(result) || !Array.isArray(result.data)) {
23894
+ if (!isRecord10(result) || !Array.isArray(result.data)) {
23687
23895
  throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned an invalid project list.");
23688
23896
  }
23689
23897
  const data = result.data.flatMap((value) => {
23690
- if (!isRecord9(value) || typeof value.id !== "string" || typeof value.name !== "string") return [];
23898
+ if (!isRecord10(value) || typeof value.id !== "string" || typeof value.name !== "string") return [];
23691
23899
  const roots = Array.isArray(value.roots) ? value.roots.flatMap((root) => {
23692
- const path = isRecord9(root) && typeof root.path === "string" && root.path.length > 0 ? root.path : void 0;
23900
+ const path = isRecord10(root) && typeof root.path === "string" && root.path.length > 0 ? root.path : void 0;
23693
23901
  return path === void 0 || !isAbsolute3(path) ? [] : [{ path }];
23694
23902
  }) : [];
23695
23903
  if (roots.length === 0) return [];
@@ -23721,20 +23929,31 @@ function containsCodexPath(root, candidate) {
23721
23929
  function normalizeCodexPathForCompare(path) {
23722
23930
  return path.replace(/[\\/]+$/u, "") || path;
23723
23931
  }
23932
+ function codexDirectoryCrumbs(root, path) {
23933
+ const crumbs2 = [{ name: basename3(root) || root, path: root, hidden: false }];
23934
+ const remainder = relative(root, path);
23935
+ if (remainder === "") return crumbs2;
23936
+ let current = root;
23937
+ for (const segment of remainder.split(/[\\/]+/u).filter(Boolean)) {
23938
+ current = resolve2(current, segment);
23939
+ crumbs2.push({ name: segment, path: current, hidden: false });
23940
+ }
23941
+ return crumbs2;
23942
+ }
23724
23943
  function extractThread(result) {
23725
- return isRecord9(result) && isRecord9(result.thread) ? result.thread : void 0;
23944
+ return isRecord10(result) && isRecord10(result.thread) ? result.thread : void 0;
23726
23945
  }
23727
23946
  function extractTurnId(result) {
23728
- if (!isRecord9(result)) return void 0;
23947
+ if (!isRecord10(result)) return void 0;
23729
23948
  if (typeof result.turnId === "string" && result.turnId.length > 0) return result.turnId;
23730
- if (isRecord9(result.turn) && typeof result.turn.id === "string" && result.turn.id.length > 0) return result.turn.id;
23949
+ if (isRecord10(result.turn) && typeof result.turn.id === "string" && result.turn.id.length > 0) return result.turn.id;
23731
23950
  return void 0;
23732
23951
  }
23733
23952
  function extractThreadId(params) {
23734
- if (!isRecord9(params)) return void 0;
23953
+ if (!isRecord10(params)) return void 0;
23735
23954
  if (typeof params.threadId === "string") return params.threadId;
23736
- if (isRecord9(params.thread) && typeof params.thread.id === "string") return params.thread.id;
23737
- if (isRecord9(params.turn) && typeof params.turn.threadId === "string") return params.turn.threadId;
23955
+ if (isRecord10(params.thread) && typeof params.thread.id === "string") return params.thread.id;
23956
+ if (isRecord10(params.turn) && typeof params.turn.threadId === "string") return params.turn.threadId;
23738
23957
  return void 0;
23739
23958
  }
23740
23959
  function optionalInteger2(value) {
@@ -23776,13 +23995,13 @@ function mapCodexImageInputs(params) {
23776
23995
  return {
23777
23996
  ...params,
23778
23997
  input: params.input.map((value) => {
23779
- if (!isRecord9(value) || value.type !== "image" || typeof value.mediaType !== "string" || typeof value.data !== "string") return value;
23998
+ if (!isRecord10(value) || value.type !== "image" || typeof value.mediaType !== "string" || typeof value.data !== "string") return value;
23780
23999
  return { type: "image", url: `data:${value.mediaType};base64,${value.data}` };
23781
24000
  })
23782
24001
  };
23783
24002
  }
23784
24003
  function sanitizeApprovalParams(params, requestHandle) {
23785
- if (!isRecord9(params)) return { requestHandle };
24004
+ if (!isRecord10(params)) return { requestHandle };
23786
24005
  const safe = { ...params };
23787
24006
  delete safe.proposedExecpolicyAmendment;
23788
24007
  delete safe.additionalPermissions;
@@ -23813,7 +24032,7 @@ function isProjectListFallbackError(error) {
23813
24032
  function canTryNextBinary(error) {
23814
24033
  return !(error instanceof RpcError) || !["CODEX_AUTH_REQUIRED", "CODEX_CLOSED"].includes(error.code);
23815
24034
  }
23816
- function isRecord9(value) {
24035
+ function isRecord10(value) {
23817
24036
  return typeof value === "object" && value !== null && !Array.isArray(value);
23818
24037
  }
23819
24038
  function array2(value) {
@@ -24816,13 +25035,39 @@ function apply(ctx, input2 = {}) {
24816
25035
  const tuiCommandsAvailable = ctx.get("commands", false) !== void 0 && ctx.get("tuiScenes", false) !== void 0;
24817
25036
  const tuiBinding = tuiCommandsAvailable ? {} : void 0;
24818
25037
  if (tuiBinding !== void 0) installTuiRemoteCommand(ctx, () => tuiBinding.target);
24819
- ctx.inject(["settings", "typertGateway"], (runtimeContext) => {
25038
+ const activateWhenCarrierReady = (runtimeContext) => {
24820
25039
  const gateway = runtimeContext.get("typertGateway");
25040
+ const connection = runtimeContext.get("connection");
25041
+ let disposePendingControl;
25042
+ const installPendingControl = () => {
25043
+ if (connection === void 0 || disposePendingControl !== void 0) return;
25044
+ disposePendingControl = runtimeContext.effect(() => {
25045
+ const control = new PluginControlRuntime(
25046
+ resolveConfig(input2),
25047
+ new IdentityStore().directory,
25048
+ void 0,
25049
+ void 0,
25050
+ void 0
25051
+ );
25052
+ return control.register(connection);
25053
+ }, "dsh-remote pending control");
25054
+ };
25055
+ const startActiveRuntime = async (activeContext) => {
25056
+ await disposePendingControl?.();
25057
+ disposePendingControl = void 0;
25058
+ await activate(activeContext, input2, tuiBinding);
25059
+ };
24821
25060
  if (tuiCommandsAvailable || runtimeContext.get("apiProxy") !== void 0 || new TypertGatewaySwitch(gateway).supportsCarrier()) {
24822
- return activate(runtimeContext, input2, tuiBinding);
25061
+ return startActiveRuntime(runtimeContext);
24823
25062
  }
24824
- runtimeContext.inject(["apiProxy"], (legacyContext) => activate(legacyContext, input2));
24825
- });
25063
+ installPendingControl();
25064
+ runtimeContext.inject(["apiProxy"], (legacyContext) => startActiveRuntime(legacyContext));
25065
+ };
25066
+ if (tuiCommandsAvailable) {
25067
+ ctx.inject(["settings", "typertGateway"], activateWhenCarrierReady);
25068
+ } else {
25069
+ ctx.inject(["settings", "connection", "typertGateway"], activateWhenCarrierReady);
25070
+ }
24826
25071
  }
24827
25072
  async function activate(ctx, input2, tuiBinding) {
24828
25073
  const settings = ctx.get("settings");
@@ -24840,7 +25085,14 @@ async function activate(ctx, input2, tuiBinding) {
24840
25085
  const connection = ctx.get("connection");
24841
25086
  const resolvedConfig = resolveConfig(settingsScope?.get() ?? input2);
24842
25087
  const config = connection === void 0 && resolvedConfig.serverUrl === void 0 ? { ...resolvedConfig, serverUrl: DEFAULT_REMOTE_SERVER_URL } : resolvedConfig;
24843
- if (!config.enabled) return;
25088
+ const defaultIdentityDirectory = new IdentityStore().directory;
25089
+ if (!config.enabled) {
25090
+ if (connection !== void 0) {
25091
+ const controlRuntime2 = new PluginControlRuntime(config, defaultIdentityDirectory, settingsScope, void 0, void 0);
25092
+ ctx.effect(() => controlRuntime2.register(connection), "dsh-remote disabled control");
25093
+ }
25094
+ return;
25095
+ }
24844
25096
  const logger = new SafeLogger({
24845
25097
  debug: (message) => {
24846
25098
  ctx.logger.debug(message);
@@ -24859,7 +25111,6 @@ async function activate(ctx, input2, tuiBinding) {
24859
25111
  console.error(message);
24860
25112
  }
24861
25113
  }, config.logLevel);
24862
- const defaultIdentityDirectory = new IdentityStore().directory;
24863
25114
  const hostIdentities = new IdentityStore({
24864
25115
  directory: config.serverUrl === void 0 ? defaultIdentityDirectory : serverStorageDirectory(defaultIdentityDirectory, config.serverUrl, "host")
24865
25116
  });