@skylandnpm/octopus-proxy 0.1.47 → 0.1.49

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.
@@ -2,19 +2,17 @@
2
2
  import {
3
3
  DesktopManager,
4
4
  PlatformDesktop,
5
- PlatformScreenLock,
6
5
  REMOTE_DEFAULTS,
7
6
  RemoteConfigSchema,
8
- ScreenLockError,
7
+ RemoteQualitySchema,
9
8
  SignalSchema,
10
9
  __commonJS,
11
10
  __toESM,
12
11
  assertDirectSignal,
13
- ensureHostAuth,
14
12
  probeRemote,
15
- verifyHostPassword,
13
+ remoteSessionBudget,
16
14
  z
17
- } from "./chunk-ZS2F5EU4.js";
15
+ } from "./chunk-PPH4OB7Y.js";
18
16
 
19
17
  // ../../node_modules/.pnpm/ajv@8.17.1/node_modules/ajv/dist/compile/codegen/code.js
20
18
  var require_code = __commonJS({
@@ -7646,8 +7644,8 @@ async function ensureHostIdentity(stateDirectory) {
7646
7644
  }
7647
7645
 
7648
7646
  // src/daemon.ts
7649
- import { mkdir as mkdir8 } from "node:fs/promises";
7650
- import { join as join12 } from "node:path";
7647
+ import { mkdir as mkdir9 } from "node:fs/promises";
7648
+ import { join as join13 } from "node:path";
7651
7649
 
7652
7650
  // src/workspace-diff.ts
7653
7651
  import { execFile as execFile2 } from "node:child_process";
@@ -8184,8 +8182,6 @@ var Operations = [
8184
8182
  "desktop.list",
8185
8183
  "desktop.capture",
8186
8184
  "desktop.read",
8187
- "desktop.lockStatus",
8188
- "desktop.unlock",
8189
8185
  "desktop.remote.status",
8190
8186
  "desktop.remote.start",
8191
8187
  "desktop.remote.signal",
@@ -8240,7 +8236,7 @@ var Journal = class {
8240
8236
  try {
8241
8237
  response = { type: "response", id: command.id, botId: command.botId, status: "completed", data: await action() };
8242
8238
  } catch (error) {
8243
- response = this.failure(command, command.operation === "desktop.unlock" ? error instanceof ScreenLockError ? error.message : "\u5C4F\u5E55\u89E3\u9501\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u4E3B\u673A\u72B6\u6001\u4E0E\u6743\u9650\u540E\u91CD\u8BD5\u3002" : isAuthentication(command.operation) ? "\u8BA4\u8BC1\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u7F16\u7801\u5DE5\u5177\u7684\u767B\u5F55\u72B6\u6001\u540E\u91CD\u8BD5" : error instanceof Error ? error.message : "Operation failed");
8239
+ response = this.failure(command, isAuthentication(command.operation) ? "\u8BA4\u8BC1\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u7F16\u7801\u5DE5\u5177\u7684\u767B\u5F55\u72B6\u6001\u540E\u91CD\u8BD5" : error instanceof Error ? error.message : "Operation failed");
8244
8240
  if (!readOnly && error instanceof Error && ["TimeoutError", "AbortError", "UnknownResultError"].includes(error.name)) response.status = "unknown";
8245
8241
  }
8246
8242
  if (!readOnly) await atomicJson(path, { hash, response, finishedAt: (/* @__PURE__ */ new Date()).toISOString() });
@@ -8457,9 +8453,82 @@ var Artifacts = class {
8457
8453
  // src/secure-chat.ts
8458
8454
  import WebSocket from "ws";
8459
8455
  import { Duplex } from "node:stream";
8460
- import { createPublicKey, randomUUID as randomUUID6, verify } from "node:crypto";
8461
- import { userInfo } from "node:os";
8456
+ import { createPublicKey, randomUUID as randomUUID7, verify } from "node:crypto";
8457
+ import { userInfo as userInfo2 } from "node:os";
8462
8458
  import { TLSSocket, createSecureContext } from "node:tls";
8459
+
8460
+ // src/host-auth.ts
8461
+ import { spawn } from "node:child_process";
8462
+ import { chmod as chmod2, mkdir as mkdir5, rename as rename3, stat as stat6, unlink as unlink4 } from "node:fs/promises";
8463
+ import { userInfo, platform as platform2 } from "node:os";
8464
+ import { randomUUID as randomUUID6 } from "node:crypto";
8465
+ import { join as join6 } from "node:path";
8466
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
8467
+ function helperPath(stateDirectory) {
8468
+ return join6(stateDirectory, "octopus-host-auth");
8469
+ }
8470
+ async function ensureHostAuth(stateDirectory) {
8471
+ await mkdir5(stateDirectory, { recursive: true, mode: 448 });
8472
+ const target = helperPath(stateDirectory);
8473
+ const system = platform2();
8474
+ if (system !== "darwin" && system !== "linux") throw new Error("Host authentication is supported only on macOS and Linux");
8475
+ const mac = system === "darwin";
8476
+ const source = fileURLToPath2(new URL(mac ? "../native/auth-macos.m" : "../native/auth-linux.c", import.meta.url));
8477
+ const sourceInfo = await stat6(source);
8478
+ const existing = await stat6(target).catch(() => null);
8479
+ if (existing?.isFile() && existing.mtimeMs >= sourceInfo.mtimeMs && (existing.mode & 63) === 0) return target;
8480
+ const temporary = `${target}.${randomUUID6()}`;
8481
+ const args = mac ? ["-O2", "-framework", "Foundation", "-framework", "OpenDirectory", source, "-o", target] : ["-O2", source, "-o", temporary, "-lpam"];
8482
+ if (mac) args[args.length - 1] = temporary;
8483
+ const compiler = spawn(mac ? "clang" : "cc", args, { stdio: ["ignore", "ignore", "pipe"] });
8484
+ let diagnostics = "";
8485
+ compiler.stderr.on("data", (chunk) => {
8486
+ diagnostics += String(chunk).slice(0, 4096);
8487
+ });
8488
+ const exit = await new Promise((resolve5, reject) => {
8489
+ compiler.on("error", reject);
8490
+ compiler.on("exit", (code) => resolve5(code ?? 1));
8491
+ });
8492
+ if (exit !== 0) {
8493
+ await unlink4(temporary).catch(() => {
8494
+ });
8495
+ throw new Error(`Host authentication helper build failed: ${diagnostics.trim()}`);
8496
+ }
8497
+ try {
8498
+ await chmod2(temporary, 448);
8499
+ await rename3(temporary, target);
8500
+ return target;
8501
+ } finally {
8502
+ await unlink4(temporary).catch(() => {
8503
+ });
8504
+ }
8505
+ }
8506
+ async function verifyHostPassword(stateDirectory, username, password) {
8507
+ if (username !== userInfo().username || !password || Buffer.byteLength(password) > 1024) return false;
8508
+ const path = await ensureHostAuth(stateDirectory);
8509
+ const child = spawn(path, [], { stdio: ["pipe", "pipe", "ignore"] });
8510
+ const bytes = Buffer.from(password, "utf8");
8511
+ const header2 = Buffer.alloc(2);
8512
+ header2.writeUInt16BE(bytes.length);
8513
+ child.stdin.end(Buffer.concat([header2, bytes]));
8514
+ bytes.fill(0);
8515
+ let output = "";
8516
+ child.stdout.on("data", (chunk) => {
8517
+ output += String(chunk).slice(0, 16);
8518
+ });
8519
+ const timer = setTimeout(() => child.kill("SIGKILL"), 1e4);
8520
+ try {
8521
+ const code = await new Promise((resolve5, reject) => {
8522
+ child.on("error", reject);
8523
+ child.on("exit", (value) => resolve5(value ?? 1));
8524
+ });
8525
+ return code === 0 && output.trim() === "OK";
8526
+ } finally {
8527
+ clearTimeout(timer);
8528
+ }
8529
+ }
8530
+
8531
+ // src/secure-chat.ts
8463
8532
  var OPEN = 1;
8464
8533
  var DATA = 2;
8465
8534
  var CLOSE = 3;
@@ -8517,13 +8586,13 @@ var SecureChat = class {
8517
8586
  }
8518
8587
  outer;
8519
8588
  sessions = /* @__PURE__ */ new Map();
8520
- epoch = randomUUID6();
8589
+ epoch = randomUUID7();
8521
8590
  sequence = 0;
8522
8591
  events = [];
8523
8592
  eventBytes = 0;
8524
8593
  stopping = false;
8525
8594
  context;
8526
- serviceAccount = userInfo().username;
8595
+ serviceAccount = userInfo2().username;
8527
8596
  timer;
8528
8597
  start() {
8529
8598
  this.connect();
@@ -8735,7 +8804,7 @@ var SecureChat = class {
8735
8804
  };
8736
8805
 
8737
8806
  // src/proxy-snapshot.ts
8738
- import { randomUUID as randomUUID7 } from "node:crypto";
8807
+ import { randomUUID as randomUUID8 } from "node:crypto";
8739
8808
  import { execFile as execFile3 } from "node:child_process";
8740
8809
  import { promisify as promisify3 } from "node:util";
8741
8810
  var execute2 = promisify3(execFile3);
@@ -8789,7 +8858,7 @@ async function buildProxySnapshot(config, runtime, adapter, requestedProjectId)
8789
8858
  let modelCatalogStatus = capabilities.includes("configuration.models") ? "unavailable" : "unsupported";
8790
8859
  if (status.healthy === true && projectId && capabilities.includes("configuration.models")) {
8791
8860
  try {
8792
- const command = { type: "command", id: randomUUID7(), botId: config.botId, operation: "configuration.models", projectId, payload: {} };
8861
+ const command = { type: "command", id: randomUUID8(), botId: config.botId, operation: "configuration.models", projectId, payload: {} };
8793
8862
  models.push(...publicModels(await adapter.dispatch(command)));
8794
8863
  modelCatalogStatus = models.length ? "available" : "unavailable";
8795
8864
  } catch {
@@ -8816,11 +8885,11 @@ async function buildProxySnapshot(config, runtime, adapter, requestedProjectId)
8816
8885
  }
8817
8886
 
8818
8887
  // src/media.ts
8819
- import { createHash as createHash6, randomUUID as randomUUID8 } from "node:crypto";
8888
+ import { createHash as createHash6, randomUUID as randomUUID9 } from "node:crypto";
8820
8889
  import { constants as constants3 } from "node:fs";
8821
- import { open as open4, unlink as unlink4, realpath as realpath3 } from "node:fs/promises";
8890
+ import { open as open4, unlink as unlink5, realpath as realpath3 } from "node:fs/promises";
8822
8891
  import { basename as basename3, isAbsolute as isAbsolute3, relative as relative3, resolve as resolve3, sep as sep2 } from "node:path";
8823
- import { fileURLToPath as fileURLToPath2 } from "node:url";
8892
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
8824
8893
 
8825
8894
  // src/screenshot-delivery.ts
8826
8895
  var instructions = "\n\n[Octopus screenshot delivery]\nFor requested UI tests/screenshots, run the real application with the available test tools. Save each requested key-step or failure screenshot under the authorized project (prefer test-results/screenshots/ with a unique filename). After saving, immediately report it as ![step and result](relative/project/path.png) in your progress or final response. Octopus Proxy will attach the actual file to Claw. Never claim a screenshot or test succeeded without producing it; explain environment/tool blockers. Do not substitute generated mockups for test screenshots.\n[/Octopus screenshot delivery]";
@@ -8937,7 +9006,7 @@ var RemoteMedia = class {
8937
9006
  } else {
8938
9007
  let path = value.path ?? value.localPath ?? value.artifactUri ?? source.path ?? source.localPath ?? url;
8939
9008
  if (typeof path !== "string") throw new Error("Image has no readable data or project path");
8940
- if (path.startsWith("file:")) path = fileURLToPath2(path);
9009
+ if (path.startsWith("file:")) path = fileURLToPath3(path);
8941
9010
  if (/^[a-z][a-z0-9+.-]*:/i.test(path)) throw new Error("Only images inside the authorized project can be returned");
8942
9011
  if (isAbsolute3(path)) {
8943
9012
  const root = await this.workspace.root(project);
@@ -9033,7 +9102,7 @@ var RemoteMedia = class {
9033
9102
  if (bytes.length === 0 || bytes.length > this.imageLimit()) throw new Error("Image is empty or exceeds size limit");
9034
9103
  const extension = { "image/png": "png", "image/jpeg": "jpg", "image/gif": "gif", "image/webp": "webp", "application/pdf": "pdf" }[mime] ?? "bin";
9035
9104
  const directory = await this.workspace.attachmentDirectory(project, "media", sha256), path = directory + "/asset." + extension;
9036
- const temp = await this.workspace.path(project, directory + "/.octopus-media-" + randomUUID8(), true);
9105
+ const temp = await this.workspace.path(project, directory + "/.octopus-media-" + randomUUID9(), true);
9037
9106
  const file = await open4(temp, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | constants3.O_NOFOLLOW, 384);
9038
9107
  try {
9039
9108
  await file.writeFile(bytes);
@@ -9044,7 +9113,7 @@ var RemoteMedia = class {
9044
9113
  try {
9045
9114
  await this.workspace.publish(project, temp, path);
9046
9115
  } catch (error) {
9047
- await unlink4(temp).catch(() => {
9116
+ await unlink5(temp).catch(() => {
9048
9117
  });
9049
9118
  if (error.code !== "EEXIST") throw error;
9050
9119
  const existing = await open4(await this.workspace.path(project, path), constants3.O_RDONLY | constants3.O_NOFOLLOW);
@@ -10450,8 +10519,8 @@ function applyEdits(text, edits) {
10450
10519
  }
10451
10520
 
10452
10521
  // src/project-config.ts
10453
- import { readFile as readFile5, writeFile as writeFile3, unlink as unlink5 } from "node:fs/promises";
10454
- import { createHash as createHash7, randomUUID as randomUUID9 } from "node:crypto";
10522
+ import { readFile as readFile5, writeFile as writeFile3, unlink as unlink6 } from "node:fs/promises";
10523
+ import { createHash as createHash7, randomUUID as randomUUID10 } from "node:crypto";
10455
10524
  var ProjectConfig = class {
10456
10525
  constructor(workspace, request) {
10457
10526
  this.workspace = workspace;
@@ -10502,12 +10571,12 @@ var ProjectConfig = class {
10502
10571
  if (!this.validator(parse2(updated))) throw new Error("Merged OpenCode configuration is invalid: " + JSON.stringify(this.validator.errors));
10503
10572
  if (exists) await this.workspace.write(project, name, updated, createHash7("sha256").update(original).digest("hex"));
10504
10573
  else {
10505
- const temporary = await this.workspace.path(project, `.octopus-config-${randomUUID9()}.tmp`, true);
10574
+ const temporary = await this.workspace.path(project, `.octopus-config-${randomUUID10()}.tmp`, true);
10506
10575
  try {
10507
10576
  await writeFile3(temporary, updated, { mode: 384, flag: "wx" });
10508
10577
  await this.workspace.publish(project, temporary, name);
10509
10578
  } finally {
10510
- await unlink5(temporary).catch(() => {
10579
+ await unlink6(temporary).catch(() => {
10511
10580
  });
10512
10581
  }
10513
10582
  }
@@ -10963,11 +11032,11 @@ function redact(value) {
10963
11032
  }
10964
11033
 
10965
11034
  // src/runtime.ts
10966
- import { spawn } from "node:child_process";
11035
+ import { spawn as spawn2 } from "node:child_process";
10967
11036
  import { createHash as createHash8 } from "node:crypto";
10968
- import { mkdir as mkdir5, open as open5, readFile as readFile6, rm, writeFile as writeFile4 } from "node:fs/promises";
11037
+ import { mkdir as mkdir6, open as open5, readFile as readFile6, rm, writeFile as writeFile4 } from "node:fs/promises";
10969
11038
  import { createServer } from "node:net";
10970
- import { join as join6 } from "node:path";
11039
+ import { join as join7 } from "node:path";
10971
11040
  var Runtime = class {
10972
11041
  constructor(config) {
10973
11042
  this.config = config;
@@ -10983,7 +11052,7 @@ var Runtime = class {
10983
11052
  return this.endpoint;
10984
11053
  }
10985
11054
  get endpointStatePath() {
10986
- return join6(this.config.stateDirectory, "opencode-endpoint.json");
11055
+ return join7(this.config.stateDirectory, "opencode-endpoint.json");
10987
11056
  }
10988
11057
  get passwordHash() {
10989
11058
  return createHash8("sha256").update(this.config.tool.password).digest("hex");
@@ -11045,13 +11114,13 @@ var Runtime = class {
11045
11114
  throw new Error(`OpenCode \u5730\u5740 ${url.origin} \u5DF2\u6709 HTTP \u670D\u52A1\uFF0C\u4F46\u5065\u5EB7\u68C0\u67E5\u5931\u8D25\uFF08HTTP ${response.status}\uFF09\uFF1B\u8BF7\u68C0\u67E5\u670D\u52A1\u5BC6\u7801\u6216\u7AEF\u53E3`);
11046
11115
  }
11047
11116
  }
11048
- await mkdir5(this.config.stateDirectory, { recursive: true, mode: 448 });
11049
- const logPath = join6(this.config.stateDirectory, "opencode-serve.log");
11117
+ await mkdir6(this.config.stateDirectory, { recursive: true, mode: 448 });
11118
+ const logPath = join7(this.config.stateDirectory, "opencode-serve.log");
11050
11119
  const log = await open5(logPath, "a", 384);
11051
11120
  const env = { ...process.env, OPENCODE_SERVER_PASSWORD: this.config.tool.password, OPENCODE_SERVER_USERNAME: "opencode" };
11052
11121
  let child;
11053
11122
  try {
11054
- child = spawn(this.config.tool.command, ["serve", "--hostname", url.hostname === "[::1]" ? "::1" : url.hostname, "--port", url.port || "80"], {
11123
+ child = spawn2(this.config.tool.command, ["serve", "--hostname", url.hostname === "[::1]" ? "::1" : url.hostname, "--port", url.port || "80"], {
11055
11124
  cwd: this.config.projects[0].directory,
11056
11125
  env,
11057
11126
  detached: true,
@@ -11138,24 +11207,24 @@ var Runtime = class {
11138
11207
 
11139
11208
  // src/tools/zcode-env.ts
11140
11209
  import { existsSync } from "node:fs";
11141
- import { dirname as dirname4, join as join7 } from "node:path";
11210
+ import { dirname as dirname4, join as join8 } from "node:path";
11142
11211
  function zcodeEnvironment(command) {
11143
11212
  const env = { ...process.env };
11144
11213
  if (!env.ZCODE_BUILTIN_PROVIDER_CONFIG_FILE) {
11145
- const bundled = join7(dirname4(command), "..", "config", "provider", "zcode-builtin.json");
11214
+ const bundled = join8(dirname4(command), "..", "config", "provider", "zcode-builtin.json");
11146
11215
  if (existsSync(bundled)) env.ZCODE_BUILTIN_PROVIDER_CONFIG_FILE = bundled;
11147
11216
  }
11148
11217
  return env;
11149
11218
  }
11150
11219
 
11151
11220
  // src/tools/stdio-runtime.ts
11152
- import { spawn as spawn2, execFile as execFile4 } from "node:child_process";
11221
+ import { spawn as spawn3, execFile as execFile4 } from "node:child_process";
11153
11222
  import { promisify as promisify4 } from "node:util";
11154
- import { mkdir as mkdir6, open as open6 } from "node:fs/promises";
11155
- import { join as join8 } from "node:path";
11223
+ import { mkdir as mkdir7, open as open6 } from "node:fs/promises";
11224
+ import { join as join9 } from "node:path";
11156
11225
 
11157
11226
  // src/tools/line-rpc.ts
11158
- import { randomUUID as randomUUID10 } from "node:crypto";
11227
+ import { randomUUID as randomUUID11 } from "node:crypto";
11159
11228
  import { crc32 } from "node:zlib";
11160
11229
  import { StringDecoder } from "node:string_decoder";
11161
11230
  function interrupted(message) {
@@ -11340,7 +11409,7 @@ var LineRpc = class {
11340
11409
  }
11341
11410
  };
11342
11411
  function wrapEnvelope(frame) {
11343
- return { wireVersion: 3, kind: "complete", deliveryKind: "desktop-continuous", logicalFrameId: randomUUID10(), logicalFrameOrdinal: 1, topic: "outbound", subscriptionId: "outbound", frame };
11412
+ return { wireVersion: 3, kind: "complete", deliveryKind: "desktop-continuous", logicalFrameId: randomUUID11(), logicalFrameOrdinal: 1, topic: "outbound", subscriptionId: "outbound", frame };
11344
11413
  }
11345
11414
 
11346
11415
  // src/tools/stdio-runtime.ts
@@ -11516,12 +11585,12 @@ ${bytes.toString("utf8")}`);
11516
11585
  if (inFlight) return inFlight;
11517
11586
  if (this.children.has(projectId)) return;
11518
11587
  const spawnOnce = (async () => {
11519
- const logPath = join8(this.config.stateDirectory, `${this.kind}-${projectId}.log`);
11520
- await mkdir6(this.config.stateDirectory, { recursive: true, mode: 448 });
11588
+ const logPath = join9(this.config.stateDirectory, `${this.kind}-${projectId}.log`);
11589
+ await mkdir7(this.config.stateDirectory, { recursive: true, mode: 448 });
11521
11590
  const log = await open6(logPath, "a", 384);
11522
11591
  let child;
11523
11592
  try {
11524
- child = spawn2(this.config.tool.command, [...this.descriptor.serverArgs], { cwd: directory, env: this.kind === "zcode" ? zcodeEnvironment(this.config.tool.command) : { ...process.env }, stdio: ["pipe", "pipe", log.fd] });
11593
+ child = spawn3(this.config.tool.command, [...this.descriptor.serverArgs], { cwd: directory, env: this.kind === "zcode" ? zcodeEnvironment(this.config.tool.command) : { ...process.env }, stdio: ["pipe", "pipe", log.fd] });
11525
11594
  } catch (error) {
11526
11595
  await log.close();
11527
11596
  throw error;
@@ -11596,7 +11665,7 @@ ${bytes.toString("utf8")}`);
11596
11665
  };
11597
11666
 
11598
11667
  // src/tools/codex.ts
11599
- import { join as join9 } from "node:path";
11668
+ import { join as join10 } from "node:path";
11600
11669
 
11601
11670
  // src/adapter.ts
11602
11671
  var DAEMON_OPERATIONS = [
@@ -11635,8 +11704,6 @@ var DAEMON_OPERATIONS = [
11635
11704
  "desktop.list",
11636
11705
  "desktop.capture",
11637
11706
  "desktop.read",
11638
- "desktop.lockStatus",
11639
- "desktop.unlock",
11640
11707
  "desktop.remote.status",
11641
11708
  "desktop.remote.start",
11642
11709
  "desktop.remote.signal",
@@ -11647,7 +11714,7 @@ function unsupportedOperation(kind, operation) {
11647
11714
  }
11648
11715
 
11649
11716
  // src/tools/codex.ts
11650
- import { randomUUID as randomUUID11 } from "node:crypto";
11717
+ import { randomUUID as randomUUID12 } from "node:crypto";
11651
11718
  var OPERATIONS = [
11652
11719
  ...DAEMON_OPERATIONS,
11653
11720
  ...AUTH_OPERATIONS,
@@ -11742,7 +11809,7 @@ var CodexAdapter = class {
11742
11809
  }
11743
11810
  onServerRequest(project, id, method, params, respond) {
11744
11811
  const detail = params ?? {};
11745
- const requestId = randomUUID11();
11812
+ const requestId = randomUUID12();
11746
11813
  const threadId = String(detail.threadId ?? "");
11747
11814
  if (["item/permissions/requestApproval", "item/commandExecution/requestApproval", "item/fileChange/requestApproval"].includes(method)) {
11748
11815
  this.approvals.set(requestId, { project, method, requestId, threadId, detail, respond });
@@ -11757,21 +11824,21 @@ var CodexAdapter = class {
11757
11824
  respond({ decision: "decline" });
11758
11825
  }
11759
11826
  async index(project) {
11760
- const stored = await readJson(join9(this.runtime.config.stateDirectory, `codex-${project}.json`));
11827
+ const stored = await readJson(join10(this.runtime.config.stateDirectory, `codex-${project}.json`));
11761
11828
  return { threadIds: stored?.threadIds ?? [] };
11762
11829
  }
11763
11830
  async remember(project, threadId) {
11764
11831
  const index = await this.index(project);
11765
11832
  if (!index.threadIds.includes(threadId)) {
11766
11833
  index.threadIds.push(threadId);
11767
- await atomicJson(join9(this.runtime.config.stateDirectory, `codex-${project}.json`), index);
11834
+ await atomicJson(join10(this.runtime.config.stateDirectory, `codex-${project}.json`), index);
11768
11835
  }
11769
11836
  }
11770
11837
  async forget(project, threadId) {
11771
11838
  const index = await this.index(project);
11772
11839
  if (index.threadIds.includes(threadId)) {
11773
11840
  index.threadIds = index.threadIds.filter((entry) => entry !== threadId);
11774
- await atomicJson(join9(this.runtime.config.stateDirectory, `codex-${project}.json`), index);
11841
+ await atomicJson(join10(this.runtime.config.stateDirectory, `codex-${project}.json`), index);
11775
11842
  }
11776
11843
  }
11777
11844
  async resume(project, threadId, rpc) {
@@ -12142,8 +12209,8 @@ var CodexAdapter = class {
12142
12209
  };
12143
12210
 
12144
12211
  // src/tools/zcode.ts
12145
- import { spawn as spawn3 } from "node:child_process";
12146
- import { randomUUID as randomUUID12 } from "node:crypto";
12212
+ import { spawn as spawn4 } from "node:child_process";
12213
+ import { randomUUID as randomUUID13 } from "node:crypto";
12147
12214
  import { realpath as realpath5 } from "node:fs/promises";
12148
12215
  var OPERATIONS2 = [
12149
12216
  ...DAEMON_OPERATIONS,
@@ -12198,7 +12265,7 @@ var ZcodeAdapter = class {
12198
12265
  }
12199
12266
  onServerRequest(project, id, method, params, respond, respondError) {
12200
12267
  const detail = params ?? {};
12201
- const requestId = randomUUID12();
12268
+ const requestId = randomUUID13();
12202
12269
  const sessionId = String(detail.sessionId ?? "");
12203
12270
  if (method === "session/requestRuntimePreferences") {
12204
12271
  respond({ nativeSearchEnhancementsEnabled: false });
@@ -12335,7 +12402,7 @@ var ZcodeAdapter = class {
12335
12402
  const flow = this.auth.create(project, () => {
12336
12403
  child?.kill("SIGTERM");
12337
12404
  });
12338
- child = spawn3(this.runtime.config.tool.command, ["login", "--no-browser", "--json"], { cwd: await this.workspace.root(project), env: zcodeEnvironment(this.runtime.config.tool.command), stdio: ["ignore", "pipe", "pipe"] });
12405
+ child = spawn4(this.runtime.config.tool.command, ["login", "--no-browser", "--json"], { cwd: await this.workspace.root(project), env: zcodeEnvironment(this.runtime.config.tool.command), stdio: ["ignore", "pipe", "pipe"] });
12339
12406
  let output = "", diagnostic = "";
12340
12407
  child.stdout?.on("data", (data) => {
12341
12408
  output = (output + data.toString()).slice(-65536);
@@ -12473,7 +12540,7 @@ var ZcodeAdapter = class {
12473
12540
  };
12474
12541
 
12475
12542
  // src/tools/pi.ts
12476
- import { randomUUID as randomUUID13 } from "node:crypto";
12543
+ import { randomUUID as randomUUID14 } from "node:crypto";
12477
12544
  import { readFile as readFile7, readdir as readdir3, rm as rm2 } from "node:fs/promises";
12478
12545
  import { dirname as dirname5 } from "node:path";
12479
12546
  var OPERATIONS3 = [
@@ -12568,7 +12635,7 @@ var PiAdapter = class {
12568
12635
  }
12569
12636
  onQuestion(project, sessionId, detail) {
12570
12637
  if (!["select", "confirm", "input", "editor"].includes(String(detail.method))) return;
12571
- const id = randomUUID13();
12638
+ const id = randomUUID14();
12572
12639
  void this.rpc(project, sessionId).then((rpc) => {
12573
12640
  if (rpc.exited) return;
12574
12641
  const question = { id, project, sessionId, detail, rpc };
@@ -12674,7 +12741,7 @@ var PiAdapter = class {
12674
12741
  return sessions.sort((a, b) => b.time.updated - a.time.updated);
12675
12742
  }
12676
12743
  if (op === "sessions.create") {
12677
- const session2 = { id: randomUUID13(), title: typeof p.title === "string" && p.title.trim() ? p.title : "\u65B0\u4EFB\u52A1", directory: await this.workspace.root(project), time: { created: Date.now(), updated: Date.now() } };
12744
+ const session2 = { id: randomUUID14(), title: typeof p.title === "string" && p.title.trim() ? p.title : "\u65B0\u4EFB\u52A1", directory: await this.workspace.root(project), time: { created: Date.now(), updated: Date.now() } };
12678
12745
  try {
12679
12746
  const rpc2 = await this.rpc(project, session2.id);
12680
12747
  await rpc2.request("set_session_name", { name: session2.title });
@@ -12772,9 +12839,9 @@ var PiAdapter = class {
12772
12839
  };
12773
12840
 
12774
12841
  // src/tools/pi-runtime.ts
12775
- import { execFile as execFile5, spawn as spawn4 } from "node:child_process";
12776
- import { mkdir as mkdir7, open as open7 } from "node:fs/promises";
12777
- import { join as join10 } from "node:path";
12842
+ import { execFile as execFile5, spawn as spawn5 } from "node:child_process";
12843
+ import { mkdir as mkdir8, open as open7 } from "node:fs/promises";
12844
+ import { join as join11 } from "node:path";
12778
12845
  import { promisify as promisify5 } from "node:util";
12779
12846
  var run3 = promisify5(execFile5);
12780
12847
  var PiRuntime = class {
@@ -12860,7 +12927,7 @@ ${bytes.toString("utf8")}`);
12860
12927
  sessionFile(projectId, sessionId) {
12861
12928
  this.project(projectId);
12862
12929
  if (!describeTool("pi").sessionPattern.test(sessionId)) throw new Error("Invalid session id");
12863
- return join10(this.config.stateDirectory, "pi-sessions", projectId, `${sessionId}.jsonl`);
12930
+ return join11(this.config.stateDirectory, "pi-sessions", projectId, `${sessionId}.jsonl`);
12864
12931
  }
12865
12932
  project(id) {
12866
12933
  const project = this.config.projects.find((entry) => entry.id === id);
@@ -12891,15 +12958,15 @@ ${bytes.toString("utf8")}`);
12891
12958
  async launch(key, projectId, sessionId, handlers, generation) {
12892
12959
  await this.kill(key);
12893
12960
  const directory = this.project(projectId).directory;
12894
- await mkdir7(join10(this.config.stateDirectory, "pi-sessions", projectId), { recursive: true, mode: 448 });
12895
- const logPath = join10(this.config.stateDirectory, `pi-${key}.log`);
12961
+ await mkdir8(join11(this.config.stateDirectory, "pi-sessions", projectId), { recursive: true, mode: 448 });
12962
+ const logPath = join11(this.config.stateDirectory, `pi-${key}.log`);
12896
12963
  const log = await open7(logPath, "a", 384);
12897
12964
  if (generation !== this.generation) {
12898
12965
  await log.close();
12899
12966
  throw new Error("Pi runtime stopped during startup");
12900
12967
  }
12901
12968
  const args = [...describeTool("pi").serverArgs, ...sessionId ? ["--session", this.sessionFile(projectId, sessionId)] : ["--no-session"]];
12902
- const child = spawn4(this.config.tool.command, args, { cwd: directory, env: { ...process.env }, stdio: ["pipe", "pipe", log.fd] });
12969
+ const child = spawn5(this.config.tool.command, args, { cwd: directory, env: { ...process.env }, stdio: ["pipe", "pipe", log.fd] });
12903
12970
  child.once("close", () => {
12904
12971
  void log.close();
12905
12972
  });
@@ -12954,7 +13021,7 @@ function createAdapter(runtime, workspace, uploads, emit) {
12954
13021
  }
12955
13022
 
12956
13023
  // src/project-registry.ts
12957
- import { realpath as realpath6, readFile as readFile8, stat as stat6 } from "node:fs/promises";
13024
+ import { realpath as realpath6, readFile as readFile8, stat as stat7 } from "node:fs/promises";
12958
13025
  import { isAbsolute as isAbsolute4 } from "node:path";
12959
13026
  var ProjectMutationSchema = ProjectSchema.extend({
12960
13027
  name: ProjectSchema.shape.name.trim().max(120),
@@ -13014,7 +13081,7 @@ var ProjectRegistry = class {
13014
13081
  const parsed = ProjectMutationSchema.parse(payload);
13015
13082
  const directory = expandHome(parsed.directory);
13016
13083
  if (!isAbsolute4(directory)) throw new Error("\u9879\u76EE\u76EE\u5F55\u5FC5\u987B\u662F Proxy \u4E3B\u673A\u4E0A\u7684\u7EDD\u5BF9\u8DEF\u5F84");
13017
- const info = await stat6(directory).catch(() => void 0);
13084
+ const info = await stat7(directory).catch(() => void 0);
13018
13085
  if (!info?.isDirectory()) throw new Error(`\u9879\u76EE\u76EE\u5F55\u4E0D\u5B58\u5728\u6216\u4E0D\u662F\u76EE\u5F55\uFF1A${directory}`);
13019
13086
  const canonical = await realpath6(directory);
13020
13087
  if (this.config.projects.some((project2) => project2.id === parsed.id)) throw new Error(`\u9879\u76EE ID \u5DF2\u5B58\u5728\uFF1A${parsed.id}`);
@@ -13034,7 +13101,7 @@ var ProjectRegistry = class {
13034
13101
  if (index < 0) throw new Error(`\u672A\u77E5\u6388\u6743\u9879\u76EE\uFF1A${parsed.id}`);
13035
13102
  const directory = expandHome(parsed.directory);
13036
13103
  if (!isAbsolute4(directory)) throw new Error("\u9879\u76EE\u76EE\u5F55\u5FC5\u987B\u662F Proxy \u4E3B\u673A\u4E0A\u7684\u7EDD\u5BF9\u8DEF\u5F84");
13037
- const info = await stat6(directory).catch(() => void 0);
13104
+ const info = await stat7(directory).catch(() => void 0);
13038
13105
  if (!info?.isDirectory()) throw new Error(`\u9879\u76EE\u76EE\u5F55\u4E0D\u5B58\u5728\u6216\u4E0D\u662F\u76EE\u5F55\uFF1A${directory}`);
13039
13106
  const canonical = await realpath6(directory);
13040
13107
  for (const project of this.config.projects) {
@@ -13081,15 +13148,15 @@ var ProjectRegistry = class {
13081
13148
  };
13082
13149
 
13083
13150
  // src/terminal.ts
13084
- import { randomUUID as randomUUID14 } from "node:crypto";
13085
- import { access as access2, stat as stat8 } from "node:fs/promises";
13151
+ import { randomUUID as randomUUID15 } from "node:crypto";
13152
+ import { access as access2, stat as stat9 } from "node:fs/promises";
13086
13153
  import { constants as constants5 } from "node:fs";
13087
- import { platform as platform2, userInfo as userInfo2 } from "node:os";
13154
+ import { platform as platform3, userInfo as userInfo3 } from "node:os";
13088
13155
  import * as pty from "node-pty";
13089
13156
 
13090
13157
  // src/terminal-support.ts
13091
13158
  import { createRequire } from "node:module";
13092
- import { access, chmod as chmod2, stat as stat7 } from "node:fs/promises";
13159
+ import { access, chmod as chmod3, stat as stat8 } from "node:fs/promises";
13093
13160
  import { constants as constants4 } from "node:fs";
13094
13161
  import { dirname as dirname6, resolve as resolve4 } from "node:path";
13095
13162
  var require2 = createRequire(import.meta.url);
@@ -13099,7 +13166,7 @@ function terminalHelperPath() {
13099
13166
  return resolve4(dirname6(utilsPath), native.dir, "spawn-helper");
13100
13167
  }
13101
13168
  async function prepareTerminalHelper(path, fix) {
13102
- const info = await stat7(path);
13169
+ const info = await stat8(path);
13103
13170
  if (!info.isFile()) throw new Error(`\u7EC8\u7AEF\u8F85\u52A9\u7A0B\u5E8F\u4E0D\u662F\u666E\u901A\u6587\u4EF6\uFF1A${path}`);
13104
13171
  try {
13105
13172
  await access(path, constants4.X_OK);
@@ -13107,7 +13174,7 @@ async function prepareTerminalHelper(path, fix) {
13107
13174
  } catch {
13108
13175
  if (!fix) throw new Error(`\u7EC8\u7AEF\u8F85\u52A9\u7A0B\u5E8F\u7F3A\u5C11\u6267\u884C\u6743\u9650\uFF1A${path}\uFF1B\u8FD0\u884C octopus-proxy doctor --fix \u4FEE\u590D`);
13109
13176
  try {
13110
- await chmod2(path, info.mode & 511 | 73);
13177
+ await chmod3(path, info.mode & 511 | 73);
13111
13178
  await access(path, constants4.X_OK);
13112
13179
  return true;
13113
13180
  } catch (error) {
@@ -13158,15 +13225,15 @@ var TerminalManager = class {
13158
13225
  const count = [...this.terminals.values()].filter((t) => t.owner === owner).length;
13159
13226
  if (count >= 8) throw new Error("Terminal limit reached");
13160
13227
  const cwd = await this.workspace.path(project, typeof p.cwd === "string" ? p.cwd : ".");
13161
- if (!(await stat8(cwd)).isDirectory()) throw new Error(`\u7EC8\u7AEF\u5DE5\u4F5C\u76EE\u5F55\u4E0D\u662F\u76EE\u5F55\uFF1A${cwd}`);
13162
- const shell = process.env.SHELL || (platform2() === "darwin" ? "/bin/zsh" : "/bin/sh");
13228
+ if (!(await stat9(cwd)).isDirectory()) throw new Error(`\u7EC8\u7AEF\u5DE5\u4F5C\u76EE\u5F55\u4E0D\u662F\u76EE\u5F55\uFF1A${cwd}`);
13229
+ const shell = process.env.SHELL || (platform3() === "darwin" ? "/bin/zsh" : "/bin/sh");
13163
13230
  try {
13164
13231
  await access2(shell, constants5.X_OK);
13165
13232
  } catch {
13166
13233
  throw new Error(`\u7EC8\u7AEF Shell \u4E0D\u5B58\u5728\u6216\u4E0D\u53EF\u6267\u884C\uFF1A${shell}\uFF1B\u8BF7\u4FEE\u6B63 Proxy \u670D\u52A1\u7684 SHELL \u73AF\u5883\u53D8\u91CF`);
13167
13234
  }
13168
13235
  const { helper } = await ensureTerminalSupport();
13169
- const id = randomUUID14();
13236
+ const id = randomUUID15();
13170
13237
  let terminalProcess;
13171
13238
  try {
13172
13239
  terminalProcess = pty.spawn(shell, [], {
@@ -13174,7 +13241,7 @@ var TerminalManager = class {
13174
13241
  cols: 80,
13175
13242
  rows: 24,
13176
13243
  cwd,
13177
- env: { PATH: process.env.PATH || "/usr/bin:/bin", HOME: userInfo2().homedir, TERM: "xterm-256color", USER: userInfo2().username }
13244
+ env: { PATH: process.env.PATH || "/usr/bin:/bin", HOME: userInfo3().homedir, TERM: "xterm-256color", USER: userInfo3().username }
13178
13245
  });
13179
13246
  } catch (error) {
13180
13247
  throw new Error(`\u7EC8\u7AEF\u542F\u52A8\u5931\u8D25\uFF1A${error instanceof Error ? error.message : String(error)}\uFF08shell=${shell}\uFF0Ccwd=${cwd}${helper ? `\uFF0Chelper=${helper}` : ""}\uFF0C${process.platform}/${process.arch}\uFF0CNode ${process.version}\uFF09\uFF1B\u8BF7\u5728 Proxy \u4E3B\u673A\u8FD0\u884C octopus-proxy doctor --fix \u68C0\u67E5`);
@@ -13227,21 +13294,48 @@ var TerminalManager = class {
13227
13294
  }
13228
13295
  };
13229
13296
 
13297
+ // src/desktop-session.ts
13298
+ import { execFile as execFile6 } from "node:child_process";
13299
+ import { promisify as promisify6 } from "node:util";
13300
+ import { userInfo as userInfo4 } from "node:os";
13301
+ var exec = promisify6(execFile6);
13302
+ var PlatformDesktopSession = class {
13303
+ constructor(system = process.platform, environment = process.env, execute3 = async (args) => (await exec("loginctl", ["--no-ask-password", ...args], { timeout: 5e3, maxBuffer: 16384 })).stdout, account = userInfo4()) {
13304
+ this.system = system;
13305
+ this.environment = environment;
13306
+ this.execute = execute3;
13307
+ this.account = account;
13308
+ }
13309
+ async sessionIdentity() {
13310
+ if (this.system === "darwin") return String(this.account.uid);
13311
+ if (this.system !== "linux") throw new Error("\u6B64\u5E73\u53F0\u4E0D\u652F\u6301\u8FDC\u7A0B\u684C\u9762\u4F1A\u8BDD");
13312
+ const ids = this.environment.XDG_SESSION_ID ? [this.environment.XDG_SESSION_ID] : (await this.execute(["list-sessions", "--no-legend"])).trim().split("\n").map((line) => line.trim().split(/\s+/)[0]).filter(Boolean);
13313
+ const sessions = [];
13314
+ for (const id of ids) {
13315
+ if (!/^[a-zA-Z0-9_-]+$/.test(id)) continue;
13316
+ const properties = Object.fromEntries((await this.execute(["show-session", id, "-p", "User", "-p", "Type", "-p", "Active"])).trim().split("\n").map((line) => line.split("=")));
13317
+ if (properties.User === String(this.account.uid) && properties.Type === "x11" && properties.Active === "yes") sessions.push(id);
13318
+ }
13319
+ if (sessions.length !== 1) throw new Error("\u65E0\u6CD5\u786E\u5B9A Proxy \u7528\u6237\u7684\u552F\u4E00\u6D3B\u52A8 X11 \u4F1A\u8BDD\uFF0C\u8BF7\u68C0\u67E5 XDG_SESSION_ID");
13320
+ return sessions[0];
13321
+ }
13322
+ };
13323
+
13230
13324
  // src/remote-manager.ts
13231
13325
  import { fork } from "node:child_process";
13232
- import { randomBytes as randomBytes2, randomUUID as randomUUID15 } from "node:crypto";
13233
- import { fileURLToPath as fileURLToPath3 } from "node:url";
13326
+ import { randomBytes as randomBytes2, randomUUID as randomUUID16 } from "node:crypto";
13327
+ import { fileURLToPath as fileURLToPath4 } from "node:url";
13234
13328
  function spawnWorker(message) {
13235
- const worker = fileURLToPath3(new URL(import.meta.url.endsWith(".ts") ? "./remote-worker.ts" : "./remote-worker.js", import.meta.url));
13329
+ const worker = fileURLToPath4(new URL(import.meta.url.endsWith(".ts") ? "./remote-worker.ts" : "./remote-worker.js", import.meta.url));
13236
13330
  const child = fork(worker, [], { stdio: ["ignore", "ignore", "ignore", "ipc"], execArgv: import.meta.url.endsWith(".ts") ? ["--import", "tsx"] : [] });
13237
13331
  child.send({ kind: "init", ...message });
13238
13332
  return child;
13239
13333
  }
13240
13334
  var RemoteManager = class {
13241
- constructor(host, desktop, lock, send, touch, workerFactory = spawnWorker, probe = probeRemote) {
13335
+ constructor(host, desktop, desktopSession, send, touch, workerFactory = spawnWorker, probe = probeRemote) {
13242
13336
  this.host = host;
13243
13337
  this.desktop = desktop;
13244
- this.lock = lock;
13338
+ this.desktopSession = desktopSession;
13245
13339
  this.send = send;
13246
13340
  this.touch = touch;
13247
13341
  this.workerFactory = workerFactory;
@@ -13255,12 +13349,13 @@ var RemoteManager = class {
13255
13349
  return this.config.stunUrls ?? [`stun:${new URL(this.host.hubUrl).hostname}:3478`];
13256
13350
  }
13257
13351
  async status() {
13258
- const base = { platform: process.platform, enabled: this.config.enabled, busy: !!this.current, stunUrls: this.stunUrls(), budget: { ...REMOTE_DEFAULTS, totalBitrate: this.config.totalBitrate, videoMaxBitrate: this.config.videoMaxBitrate } };
13352
+ const base = { qualityModes: RemoteQualitySchema.options, platform: process.platform, passwordInput: process.platform === "darwin", enabled: this.config.enabled, busy: !!this.current, stunUrls: this.stunUrls(), budget: { ...REMOTE_DEFAULTS, totalBitrate: this.config.totalBitrate, videoMaxBitrate: this.config.videoMaxBitrate } };
13259
13353
  if (!this.config.enabled) return { ...base, available: false, message: "\u4E3B\u673A\u5DF2\u5173\u95ED\u8FDC\u7A0B\u63A7\u5236" };
13260
13354
  try {
13261
13355
  const permissions = await this.probe(this.host.stateDirectory, this.config);
13262
- const available = permissions.session && permissions.capture && permissions.input;
13263
- return { ...base, available, permissions, ...!available ? { message: "\u8BF7\u68C0\u67E5\u4E3B\u673A\u56FE\u5F62\u4F1A\u8BDD\uFF0C\u5E76\u4E3A\u5B9E\u9645\u8FD0\u884C Proxy \u7684\u7A0B\u5E8F\u6388\u6743\u5C4F\u5E55\u5F55\u5236\u548C\u8F85\u52A9\u529F\u80FD" } : {} };
13356
+ const lockUnsupported = permissions.locked === true && permissions.lockCapture === false;
13357
+ const available = permissions.session && permissions.capture && permissions.input && !lockUnsupported;
13358
+ return { ...base, available, permissions, ...!available ? { message: lockUnsupported ? "\u9501\u5C4F\u8FDC\u63A7\u9700\u8981 macOS 14 \u53CA\u4EE5\u4E0A\u53CA\u65B0\u7248 Xcode Command Line Tools\uFF1B\u5F53\u524D\u4E3B\u673A\u8BF7\u5148\u672C\u5730\u89E3\u9501" : "\u8BF7\u68C0\u67E5\u4E3B\u673A\u56FE\u5F62\u4F1A\u8BDD\uFF0C\u5E76\u4E3A\u5B9E\u9645\u8FD0\u884C Proxy \u7684\u7A0B\u5E8F\u6388\u6743\u5C4F\u5E55\u5F55\u5236\u548C\u8F85\u52A9\u529F\u80FD" } : {} };
13264
13359
  } catch (error) {
13265
13360
  return { ...base, available: false, message: error instanceof Error ? error.message : "\u8FDC\u63A7\u4F9D\u8D56\u4E0D\u53EF\u7528" };
13266
13361
  }
@@ -13294,20 +13389,21 @@ var RemoteManager = class {
13294
13389
  async start(payload, owner) {
13295
13390
  if (!this.config.enabled) throw new Error("\u4E3B\u673A\u5DF2\u5173\u95ED\u8FDC\u7A0B\u63A7\u5236");
13296
13391
  if (this.current) throw new Error("\u4E3B\u673A\u6B63\u5728\u88AB\u63A7\u5236");
13297
- const session = { remoteId: randomUUID15(), owner, closed: false, signaling: Promise.resolve(), signals: 0, signalWindow: Date.now(), candidates: 0 };
13392
+ const quality = RemoteQualitySchema.parse(payload.quality ?? "hd");
13393
+ const budget = remoteSessionBudget(this.config, quality, payload.quality === void 0);
13394
+ const session = { remoteId: randomUUID16(), owner, closed: false, signaling: Promise.resolve(), signals: 0, signalWindow: Date.now(), candidates: 0 };
13298
13395
  this.current = session;
13299
13396
  try {
13300
13397
  const permissions = await this.probe(this.host.stateDirectory, this.config);
13301
13398
  if (!permissions.session || !permissions.capture || !permissions.input) throw new Error("\u8FDC\u63A7\u9700\u8981\u4E3B\u673A\u56FE\u5F62\u4F1A\u8BDD\u3001\u5C4F\u5E55\u5F55\u5236\u53CA\u8F85\u52A9\u529F\u80FD\u6743\u9650");
13302
- const lock = await this.lock.status();
13303
- if (lock.locked) throw new Error("\u4E3B\u673A\u5C4F\u5E55\u5DF2\u9501\u5B9A\uFF0C\u8BF7\u5728\u88AB\u63A7\u4E3B\u673A\u4E0A\u89E3\u9501\u540E\u91CD\u8BD5");
13304
- const identity = await this.lock.sessionIdentity?.();
13399
+ if (permissions.locked === true && permissions.lockCapture === false) throw new Error("\u9501\u5C4F\u8FDC\u63A7\u9700\u8981 macOS 14 \u53CA\u4EE5\u4E0A\u53CA\u65B0\u7248 Xcode Command Line Tools\uFF1B\u5F53\u524D\u4E3B\u673A\u8BF7\u5148\u672C\u5730\u89E3\u9501");
13400
+ const identity = await this.desktopSession.sessionIdentity();
13305
13401
  const display = (await this.desktop.list()).find((d) => d.id === payload.displayId);
13306
13402
  if (!display) throw new Error("\u663E\u793A\u5668\u5DF2\u65AD\u5F00\uFF0C\u8BF7\u5237\u65B0\u663E\u793A\u5668\u5217\u8868");
13307
13403
  const layout = await this.desktop.layoutIdentity?.(display.id);
13308
13404
  if (session.closed || this.current !== session) throw new Error("\u5DE5\u4F5C\u533A\u8FDE\u63A5\u5DF2\u5173\u95ED\uFF0C\u8FDC\u63A7\u5DF2\u53D6\u6D88");
13309
13405
  const bindToken = randomBytes2(32).toString("base64url"), expiresAt = Date.now() + 2e4;
13310
- const child = this.workerFactory({ remoteId: session.remoteId, bindToken, expiresAt, display, helper: permissions.helper, config: this.config, stunUrls: this.stunUrls() });
13406
+ const child = this.workerFactory({ remoteId: session.remoteId, bindToken, expiresAt, display, helper: permissions.helper, quality, legacy: payload.quality === void 0, config: this.config, stunUrls: this.stunUrls() });
13311
13407
  session.child = child;
13312
13408
  child.on("message", (message) => {
13313
13409
  if (this.current !== session || session.closed || !message || typeof message !== "object") return;
@@ -13330,8 +13426,7 @@ var RemoteManager = class {
13330
13426
  if (checking || session.closed) return;
13331
13427
  checking = true;
13332
13428
  try {
13333
- if ((await this.lock.status()).locked) this.finish(session, "\u4E3B\u673A\u5C4F\u5E55\u5DF2\u9501\u5B9A\uFF0C\u8FDC\u63A7\u5DF2\u505C\u6B62");
13334
- if (identity !== void 0 && await this.lock.sessionIdentity?.() !== identity) this.finish(session, "\u4E3B\u673A\u56FE\u5F62\u4F1A\u8BDD\u5DF2\u53D8\u5316\uFF0C\u8FDC\u63A7\u5DF2\u505C\u6B62");
13429
+ if (await this.desktopSession.sessionIdentity() !== identity) this.finish(session, "\u4E3B\u673A\u56FE\u5F62\u4F1A\u8BDD\u5DF2\u53D8\u5316\uFF0C\u8FDC\u63A7\u5DF2\u505C\u6B62");
13335
13430
  const current = (await this.desktop.list()).find((d) => d.id === display.id);
13336
13431
  if (!current || current.width !== display.width || current.height !== display.height) this.finish(session, "\u663E\u793A\u5668\u5DF2\u53D8\u5316\uFF0C\u8FDC\u63A7\u5DF2\u505C\u6B62");
13337
13432
  if (layout !== void 0 && await this.desktop.layoutIdentity?.(display.id) !== layout) this.finish(session, "\u663E\u793A\u5668\u5E03\u5C40\u5DF2\u53D8\u5316\uFF0C\u8FDC\u63A7\u5DF2\u505C\u6B62");
@@ -13342,7 +13437,7 @@ var RemoteManager = class {
13342
13437
  }
13343
13438
  }, 1e3);
13344
13439
  session.monitor.unref();
13345
- return { remoteId: session.remoteId, bindToken, expiresAt: new Date(expiresAt).toISOString(), display, revision: 1, stunUrls: this.stunUrls(), budget: { ...REMOTE_DEFAULTS, totalBitrate: this.config.totalBitrate, videoMaxBitrate: this.config.videoMaxBitrate }, protocolVersion: 1, heartbeatAck: true };
13440
+ return { remoteId: session.remoteId, bindToken, expiresAt: new Date(expiresAt).toISOString(), display, revision: 1, stunUrls: this.stunUrls(), budget, quality, qualityModes: RemoteQualitySchema.options, protocolVersion: 1, heartbeatAck: true };
13346
13441
  } catch (error) {
13347
13442
  this.finish(session);
13348
13443
  throw error;
@@ -13372,18 +13467,18 @@ var RemoteManager = class {
13372
13467
  };
13373
13468
 
13374
13469
  // src/tool-selection.ts
13375
- import { randomBytes as randomBytes3, randomUUID as randomUUID16 } from "node:crypto";
13470
+ import { randomBytes as randomBytes3, randomUUID as randomUUID17 } from "node:crypto";
13376
13471
  import { access as access3, constants as constants6, readFile as readFile9 } from "node:fs/promises";
13377
- import { platform as platform4 } from "node:os";
13472
+ import { platform as platform5 } from "node:os";
13378
13473
  import { isDeepStrictEqual } from "node:util";
13379
13474
 
13380
13475
  // src/node-resolution.ts
13381
- import { realpath as realpath7, stat as stat9 } from "node:fs/promises";
13382
- import { execFile as execFile6 } from "node:child_process";
13383
- import { promisify as promisify6 } from "node:util";
13384
- import { join as join11 } from "node:path";
13385
- import { homedir as homedir2, platform as platform3 } from "node:os";
13386
- var run4 = promisify6(execFile6);
13476
+ import { realpath as realpath7, stat as stat10 } from "node:fs/promises";
13477
+ import { execFile as execFile7 } from "node:child_process";
13478
+ import { promisify as promisify7 } from "node:util";
13479
+ import { join as join12 } from "node:path";
13480
+ import { homedir as homedir2, platform as platform4 } from "node:os";
13481
+ var run4 = promisify7(execFile7);
13387
13482
  var VERSION_MANAGED_SEGMENTS = /* @__PURE__ */ new Set([".nvm", "nvm", ".volta", "volta", ".fnm", "fnm", ".asdf", "asdf", ".mise", "mise"]);
13388
13483
  var execProbe = async (path) => {
13389
13484
  try {
@@ -13397,7 +13492,7 @@ function isVersionManagedPath(path) {
13397
13492
  return path.split("/").some((segment) => VERSION_MANAGED_SEGMENTS.has(segment));
13398
13493
  }
13399
13494
  function nodeCandidates() {
13400
- return platform3() === "darwin" ? ["/opt/homebrew/bin/node", "/opt/homebrew/opt/node/bin/node", "/usr/local/bin/node", "/usr/bin/node"] : ["/usr/local/bin/node", "/usr/bin/node", "/snap/bin/node"];
13495
+ return platform4() === "darwin" ? ["/opt/homebrew/bin/node", "/opt/homebrew/opt/node/bin/node", "/usr/local/bin/node", "/usr/bin/node"] : ["/usr/local/bin/node", "/usr/bin/node", "/snap/bin/node"];
13401
13496
  }
13402
13497
  async function resolveNodePath(options = {}) {
13403
13498
  const probe = options.probe ?? execProbe;
@@ -13405,26 +13500,26 @@ async function resolveNodePath(options = {}) {
13405
13500
  if (!await probe(options.pinned)) throw new Error(`--node-path ${options.pinned} is not a working node executable`);
13406
13501
  return { nodePath: options.pinned, source: "pinned" };
13407
13502
  }
13408
- let exec = options.execPath ?? process.execPath;
13503
+ let exec2 = options.execPath ?? process.execPath;
13409
13504
  try {
13410
- exec = await realpath7(exec);
13505
+ exec2 = await realpath7(exec2);
13411
13506
  } catch {
13412
13507
  }
13413
- if (!isVersionManagedPath(exec) && await probe(exec)) return { nodePath: exec, source: "exec" };
13508
+ if (!isVersionManagedPath(exec2) && await probe(exec2)) return { nodePath: exec2, source: "exec" };
13414
13509
  for (const candidate of nodeCandidates()) if (!isVersionManagedPath(candidate) && await probe(candidate)) return { nodePath: candidate, source: "candidate" };
13415
- if (await probe(exec)) return { nodePath: exec, source: "exec", warning: `only a version-manager node was found (${exec}); install node with the system package manager or pass --node-path, otherwise the service breaks when that node is upgraded` };
13510
+ if (await probe(exec2)) return { nodePath: exec2, source: "exec", warning: `only a version-manager node was found (${exec2}); install node with the system package manager or pass --node-path, otherwise the service breaks when that node is upgraded` };
13416
13511
  return { nodePath: null, source: "exec" };
13417
13512
  }
13418
13513
  function toolSearchDirs(kind) {
13419
- const dirs = [...describeTool(kind).searchDirs, join11(homedir2(), ".local", "bin"), join11(homedir2(), ".opencode", "bin"), join11(homedir2(), ".codex", "bin")];
13514
+ const dirs = [...describeTool(kind).searchDirs, join12(homedir2(), ".local", "bin"), join12(homedir2(), ".opencode", "bin"), join12(homedir2(), ".codex", "bin")];
13420
13515
  return [...new Set(dirs)];
13421
13516
  }
13422
13517
  async function resolveCommandOnPath(name, extraDirs = []) {
13423
13518
  const dirs = [...(process.env.PATH ?? "").split(":"), ...extraDirs].filter(Boolean);
13424
13519
  for (const dir of new Set(dirs)) {
13425
- const full = join11(dir, name);
13520
+ const full = join12(dir, name);
13426
13521
  try {
13427
- const info = await stat9(full);
13522
+ const info = await stat10(full);
13428
13523
  if (info.isFile() && info.mode & 73) return full;
13429
13524
  } catch {
13430
13525
  }
@@ -13507,7 +13602,7 @@ var ToolSelection = class {
13507
13602
  });
13508
13603
  }
13509
13604
  modelCommand(projectId) {
13510
- return { type: "command", id: randomUUID16(), botId: this.config.botId, operation: "configuration.models", projectId, payload: {} };
13605
+ return { type: "command", id: randomUUID17(), botId: this.config.botId, operation: "configuration.models", projectId, payload: {} };
13511
13606
  }
13512
13607
  profiles(raw) {
13513
13608
  return raw.toolProfiles && typeof raw.toolProfiles === "object" && !Array.isArray(raw.toolProfiles) ? raw.toolProfiles : {};
@@ -13522,7 +13617,7 @@ var ToolSelection = class {
13522
13617
  const discovered = this.discovered.get(kind);
13523
13618
  if (discovered) return discovered;
13524
13619
  let command = await resolveCommandOnPath(kind, toolSearchDirs(kind));
13525
- if (!command && platform4() === "darwin") {
13620
+ if (!command && platform5() === "darwin") {
13526
13621
  const candidates = kind === "codex" ? [
13527
13622
  "/Applications/ChatGPT.app/Contents/Resources/codex-cli/CodexCLI.app/Contents/MacOS/codex",
13528
13623
  "/Applications/ChatGPT.app/Contents/Resources/codex"
@@ -13566,19 +13661,19 @@ var ToolSelection = class {
13566
13661
  // src/daemon.ts
13567
13662
  async function runDaemon(configPath, authenticate = verifyHostPassword) {
13568
13663
  const config = await loadConfig(configPath);
13569
- await mkdir8(config.stateDirectory, { recursive: true, mode: 448 });
13570
- const workspace = new Workspace(config.projects), uploads = new Uploads(workspace, join12(config.stateDirectory, "uploads")), journal = new Journal(join12(config.stateDirectory, "journal"));
13664
+ await mkdir9(config.stateDirectory, { recursive: true, mode: 448 });
13665
+ const workspace = new Workspace(config.projects), uploads = new Uploads(workspace, join13(config.stateDirectory, "uploads")), journal = new Journal(join13(config.stateDirectory, "journal"));
13571
13666
  let secureChat;
13572
13667
  function emit(projectId, event, data) {
13573
13668
  secureChat?.publish(projectId, event, data);
13574
13669
  }
13575
13670
  const terminals = new TerminalManager(workspace, (owner, project, event, data) => secureChat?.sendTo(owner, project, event, data));
13576
- const desktopBackend = new PlatformDesktop(config.stateDirectory), screenLock = new PlatformScreenLock(config.stateDirectory);
13577
- const desktop = new DesktopManager(desktopBackend, screenLock);
13671
+ const desktopBackend = new PlatformDesktop(config.stateDirectory), desktopSession = new PlatformDesktopSession();
13672
+ const desktop = new DesktopManager(desktopBackend);
13578
13673
  const remote = new RemoteManager(
13579
13674
  config,
13580
13675
  desktopBackend,
13581
- screenLock,
13676
+ desktopSession,
13582
13677
  (owner, event, data) => secureChat?.sendTo(owner, "", event, data),
13583
13678
  (owner) => secureChat?.touchUserActivity(owner) ?? false
13584
13679
  );
@@ -13589,7 +13684,7 @@ async function runDaemon(configPath, authenticate = verifyHostPassword) {
13589
13684
  });
13590
13685
  return { runtime, adapter };
13591
13686
  });
13592
- const artifacts = new Artifacts(workspace, join12(config.stateDirectory, "artifacts"), emit);
13687
+ const artifacts = new Artifacts(workspace, join13(config.stateDirectory, "artifacts"), emit);
13593
13688
  async function dispatch(c, secureSessionId) {
13594
13689
  if (c.operation.startsWith("desktop.remote.")) return remote.dispatch(c, secureSessionId);
13595
13690
  if (c.operation.startsWith("desktop.")) return desktop.dispatch(c, secureSessionId);
@@ -13712,11 +13807,13 @@ export {
13712
13807
  Workspace,
13713
13808
  atomicJson,
13714
13809
  Uploads,
13810
+ ensureHostAuth,
13715
13811
  proxyVersion,
13716
13812
  ensureHostIdentity,
13717
13813
  createRuntime,
13718
13814
  createAdapter,
13719
13815
  checkTerminalSupport,
13816
+ PlatformDesktopSession,
13720
13817
  RemoteManager,
13721
13818
  isVersionManagedPath,
13722
13819
  resolveNodePath,