@skylandnpm/octopus-proxy 0.1.48 → 0.1.50

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-KRNEKDBF.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,16 @@ 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";
13155
+ import { delimiter } from "node:path";
13088
13156
  import * as pty from "node-pty";
13089
13157
 
13090
13158
  // src/terminal-support.ts
13091
13159
  import { createRequire } from "node:module";
13092
- import { access, chmod as chmod2, stat as stat7 } from "node:fs/promises";
13160
+ import { access, chmod as chmod3, stat as stat8 } from "node:fs/promises";
13093
13161
  import { constants as constants4 } from "node:fs";
13094
13162
  import { dirname as dirname6, resolve as resolve4 } from "node:path";
13095
13163
  var require2 = createRequire(import.meta.url);
@@ -13099,7 +13167,7 @@ function terminalHelperPath() {
13099
13167
  return resolve4(dirname6(utilsPath), native.dir, "spawn-helper");
13100
13168
  }
13101
13169
  async function prepareTerminalHelper(path, fix) {
13102
- const info = await stat7(path);
13170
+ const info = await stat8(path);
13103
13171
  if (!info.isFile()) throw new Error(`\u7EC8\u7AEF\u8F85\u52A9\u7A0B\u5E8F\u4E0D\u662F\u666E\u901A\u6587\u4EF6\uFF1A${path}`);
13104
13172
  try {
13105
13173
  await access(path, constants4.X_OK);
@@ -13107,7 +13175,7 @@ async function prepareTerminalHelper(path, fix) {
13107
13175
  } catch {
13108
13176
  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
13177
  try {
13110
- await chmod2(path, info.mode & 511 | 73);
13178
+ await chmod3(path, info.mode & 511 | 73);
13111
13179
  await access(path, constants4.X_OK);
13112
13180
  return true;
13113
13181
  } catch (error) {
@@ -13158,15 +13226,24 @@ var TerminalManager = class {
13158
13226
  const count = [...this.terminals.values()].filter((t) => t.owner === owner).length;
13159
13227
  if (count >= 8) throw new Error("Terminal limit reached");
13160
13228
  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");
13229
+ if (!(await stat9(cwd)).isDirectory()) throw new Error(`\u7EC8\u7AEF\u5DE5\u4F5C\u76EE\u5F55\u4E0D\u662F\u76EE\u5F55\uFF1A${cwd}`);
13230
+ const shell = process.env.SHELL || (platform3() === "darwin" ? "/bin/zsh" : "/bin/sh");
13163
13231
  try {
13164
13232
  await access2(shell, constants5.X_OK);
13165
13233
  } catch {
13166
13234
  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
13235
  }
13168
13236
  const { helper } = await ensureTerminalSupport();
13169
- const id = randomUUID14();
13237
+ const id = randomUUID15();
13238
+ const path = [
13239
+ process.env.PATH,
13240
+ "/usr/bin",
13241
+ "/bin",
13242
+ "/usr/sbin",
13243
+ "/sbin",
13244
+ "/usr/local/bin",
13245
+ ...platform3() === "darwin" ? ["/opt/homebrew/bin"] : []
13246
+ ].filter(Boolean).join(delimiter);
13170
13247
  let terminalProcess;
13171
13248
  try {
13172
13249
  terminalProcess = pty.spawn(shell, [], {
@@ -13174,7 +13251,7 @@ var TerminalManager = class {
13174
13251
  cols: 80,
13175
13252
  rows: 24,
13176
13253
  cwd,
13177
- env: { PATH: process.env.PATH || "/usr/bin:/bin", HOME: userInfo2().homedir, TERM: "xterm-256color", USER: userInfo2().username }
13254
+ env: { PATH: path, HOME: userInfo3().homedir, TERM: "xterm-256color", USER: userInfo3().username }
13178
13255
  });
13179
13256
  } catch (error) {
13180
13257
  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 +13304,48 @@ var TerminalManager = class {
13227
13304
  }
13228
13305
  };
13229
13306
 
13307
+ // src/desktop-session.ts
13308
+ import { execFile as execFile6 } from "node:child_process";
13309
+ import { promisify as promisify6 } from "node:util";
13310
+ import { userInfo as userInfo4 } from "node:os";
13311
+ var exec = promisify6(execFile6);
13312
+ var PlatformDesktopSession = class {
13313
+ constructor(system = process.platform, environment = process.env, execute3 = async (args) => (await exec("loginctl", ["--no-ask-password", ...args], { timeout: 5e3, maxBuffer: 16384 })).stdout, account = userInfo4()) {
13314
+ this.system = system;
13315
+ this.environment = environment;
13316
+ this.execute = execute3;
13317
+ this.account = account;
13318
+ }
13319
+ async sessionIdentity() {
13320
+ if (this.system === "darwin") return String(this.account.uid);
13321
+ if (this.system !== "linux") throw new Error("\u6B64\u5E73\u53F0\u4E0D\u652F\u6301\u8FDC\u7A0B\u684C\u9762\u4F1A\u8BDD");
13322
+ 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);
13323
+ const sessions = [];
13324
+ for (const id of ids) {
13325
+ if (!/^[a-zA-Z0-9_-]+$/.test(id)) continue;
13326
+ const properties = Object.fromEntries((await this.execute(["show-session", id, "-p", "User", "-p", "Type", "-p", "Active"])).trim().split("\n").map((line) => line.split("=")));
13327
+ if (properties.User === String(this.account.uid) && properties.Type === "x11" && properties.Active === "yes") sessions.push(id);
13328
+ }
13329
+ 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");
13330
+ return sessions[0];
13331
+ }
13332
+ };
13333
+
13230
13334
  // src/remote-manager.ts
13231
13335
  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";
13336
+ import { randomBytes as randomBytes2, randomUUID as randomUUID16 } from "node:crypto";
13337
+ import { fileURLToPath as fileURLToPath4 } from "node:url";
13234
13338
  function spawnWorker(message) {
13235
- const worker = fileURLToPath3(new URL(import.meta.url.endsWith(".ts") ? "./remote-worker.ts" : "./remote-worker.js", import.meta.url));
13339
+ const worker = fileURLToPath4(new URL(import.meta.url.endsWith(".ts") ? "./remote-worker.ts" : "./remote-worker.js", import.meta.url));
13236
13340
  const child = fork(worker, [], { stdio: ["ignore", "ignore", "ignore", "ipc"], execArgv: import.meta.url.endsWith(".ts") ? ["--import", "tsx"] : [] });
13237
13341
  child.send({ kind: "init", ...message });
13238
13342
  return child;
13239
13343
  }
13240
13344
  var RemoteManager = class {
13241
- constructor(host, desktop, lock, send, touch, workerFactory = spawnWorker, probe = probeRemote) {
13345
+ constructor(host, desktop, desktopSession, send, touch, workerFactory = spawnWorker, probe = probeRemote) {
13242
13346
  this.host = host;
13243
13347
  this.desktop = desktop;
13244
- this.lock = lock;
13348
+ this.desktopSession = desktopSession;
13245
13349
  this.send = send;
13246
13350
  this.touch = touch;
13247
13351
  this.workerFactory = workerFactory;
@@ -13255,12 +13359,13 @@ var RemoteManager = class {
13255
13359
  return this.config.stunUrls ?? [`stun:${new URL(this.host.hubUrl).hostname}:3478`];
13256
13360
  }
13257
13361
  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 } };
13362
+ 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
13363
  if (!this.config.enabled) return { ...base, available: false, message: "\u4E3B\u673A\u5DF2\u5173\u95ED\u8FDC\u7A0B\u63A7\u5236" };
13260
13364
  try {
13261
13365
  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" } : {} };
13366
+ const lockUnsupported = permissions.locked === true && permissions.lockCapture === false;
13367
+ const available = permissions.session && permissions.capture && permissions.input && !lockUnsupported;
13368
+ 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
13369
  } catch (error) {
13265
13370
  return { ...base, available: false, message: error instanceof Error ? error.message : "\u8FDC\u63A7\u4F9D\u8D56\u4E0D\u53EF\u7528" };
13266
13371
  }
@@ -13294,20 +13399,21 @@ var RemoteManager = class {
13294
13399
  async start(payload, owner) {
13295
13400
  if (!this.config.enabled) throw new Error("\u4E3B\u673A\u5DF2\u5173\u95ED\u8FDC\u7A0B\u63A7\u5236");
13296
13401
  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 };
13402
+ const quality = RemoteQualitySchema.parse(payload.quality ?? "hd");
13403
+ const budget = remoteSessionBudget(this.config, quality, payload.quality === void 0);
13404
+ const session = { remoteId: randomUUID16(), owner, closed: false, signaling: Promise.resolve(), signals: 0, signalWindow: Date.now(), candidates: 0 };
13298
13405
  this.current = session;
13299
13406
  try {
13300
13407
  const permissions = await this.probe(this.host.stateDirectory, this.config);
13301
13408
  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?.();
13409
+ 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");
13410
+ const identity = await this.desktopSession.sessionIdentity();
13305
13411
  const display = (await this.desktop.list()).find((d) => d.id === payload.displayId);
13306
13412
  if (!display) throw new Error("\u663E\u793A\u5668\u5DF2\u65AD\u5F00\uFF0C\u8BF7\u5237\u65B0\u663E\u793A\u5668\u5217\u8868");
13307
13413
  const layout = await this.desktop.layoutIdentity?.(display.id);
13308
13414
  if (session.closed || this.current !== session) throw new Error("\u5DE5\u4F5C\u533A\u8FDE\u63A5\u5DF2\u5173\u95ED\uFF0C\u8FDC\u63A7\u5DF2\u53D6\u6D88");
13309
13415
  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() });
13416
+ 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
13417
  session.child = child;
13312
13418
  child.on("message", (message) => {
13313
13419
  if (this.current !== session || session.closed || !message || typeof message !== "object") return;
@@ -13330,8 +13436,7 @@ var RemoteManager = class {
13330
13436
  if (checking || session.closed) return;
13331
13437
  checking = true;
13332
13438
  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");
13439
+ 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
13440
  const current = (await this.desktop.list()).find((d) => d.id === display.id);
13336
13441
  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
13442
  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 +13447,7 @@ var RemoteManager = class {
13342
13447
  }
13343
13448
  }, 1e3);
13344
13449
  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 };
13450
+ 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
13451
  } catch (error) {
13347
13452
  this.finish(session);
13348
13453
  throw error;
@@ -13372,18 +13477,18 @@ var RemoteManager = class {
13372
13477
  };
13373
13478
 
13374
13479
  // src/tool-selection.ts
13375
- import { randomBytes as randomBytes3, randomUUID as randomUUID16 } from "node:crypto";
13480
+ import { randomBytes as randomBytes3, randomUUID as randomUUID17 } from "node:crypto";
13376
13481
  import { access as access3, constants as constants6, readFile as readFile9 } from "node:fs/promises";
13377
- import { platform as platform4 } from "node:os";
13482
+ import { platform as platform5 } from "node:os";
13378
13483
  import { isDeepStrictEqual } from "node:util";
13379
13484
 
13380
13485
  // 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);
13486
+ import { realpath as realpath7, stat as stat10 } from "node:fs/promises";
13487
+ import { execFile as execFile7 } from "node:child_process";
13488
+ import { promisify as promisify7 } from "node:util";
13489
+ import { join as join12 } from "node:path";
13490
+ import { homedir as homedir2, platform as platform4 } from "node:os";
13491
+ var run4 = promisify7(execFile7);
13387
13492
  var VERSION_MANAGED_SEGMENTS = /* @__PURE__ */ new Set([".nvm", "nvm", ".volta", "volta", ".fnm", "fnm", ".asdf", "asdf", ".mise", "mise"]);
13388
13493
  var execProbe = async (path) => {
13389
13494
  try {
@@ -13397,7 +13502,7 @@ function isVersionManagedPath(path) {
13397
13502
  return path.split("/").some((segment) => VERSION_MANAGED_SEGMENTS.has(segment));
13398
13503
  }
13399
13504
  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"];
13505
+ 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
13506
  }
13402
13507
  async function resolveNodePath(options = {}) {
13403
13508
  const probe = options.probe ?? execProbe;
@@ -13405,26 +13510,26 @@ async function resolveNodePath(options = {}) {
13405
13510
  if (!await probe(options.pinned)) throw new Error(`--node-path ${options.pinned} is not a working node executable`);
13406
13511
  return { nodePath: options.pinned, source: "pinned" };
13407
13512
  }
13408
- let exec = options.execPath ?? process.execPath;
13513
+ let exec2 = options.execPath ?? process.execPath;
13409
13514
  try {
13410
- exec = await realpath7(exec);
13515
+ exec2 = await realpath7(exec2);
13411
13516
  } catch {
13412
13517
  }
13413
- if (!isVersionManagedPath(exec) && await probe(exec)) return { nodePath: exec, source: "exec" };
13518
+ if (!isVersionManagedPath(exec2) && await probe(exec2)) return { nodePath: exec2, source: "exec" };
13414
13519
  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` };
13520
+ 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
13521
  return { nodePath: null, source: "exec" };
13417
13522
  }
13418
13523
  function toolSearchDirs(kind) {
13419
- const dirs = [...describeTool(kind).searchDirs, join11(homedir2(), ".local", "bin"), join11(homedir2(), ".opencode", "bin"), join11(homedir2(), ".codex", "bin")];
13524
+ const dirs = [...describeTool(kind).searchDirs, join12(homedir2(), ".local", "bin"), join12(homedir2(), ".opencode", "bin"), join12(homedir2(), ".codex", "bin")];
13420
13525
  return [...new Set(dirs)];
13421
13526
  }
13422
13527
  async function resolveCommandOnPath(name, extraDirs = []) {
13423
13528
  const dirs = [...(process.env.PATH ?? "").split(":"), ...extraDirs].filter(Boolean);
13424
13529
  for (const dir of new Set(dirs)) {
13425
- const full = join11(dir, name);
13530
+ const full = join12(dir, name);
13426
13531
  try {
13427
- const info = await stat9(full);
13532
+ const info = await stat10(full);
13428
13533
  if (info.isFile() && info.mode & 73) return full;
13429
13534
  } catch {
13430
13535
  }
@@ -13507,7 +13612,7 @@ var ToolSelection = class {
13507
13612
  });
13508
13613
  }
13509
13614
  modelCommand(projectId) {
13510
- return { type: "command", id: randomUUID16(), botId: this.config.botId, operation: "configuration.models", projectId, payload: {} };
13615
+ return { type: "command", id: randomUUID17(), botId: this.config.botId, operation: "configuration.models", projectId, payload: {} };
13511
13616
  }
13512
13617
  profiles(raw) {
13513
13618
  return raw.toolProfiles && typeof raw.toolProfiles === "object" && !Array.isArray(raw.toolProfiles) ? raw.toolProfiles : {};
@@ -13522,7 +13627,7 @@ var ToolSelection = class {
13522
13627
  const discovered = this.discovered.get(kind);
13523
13628
  if (discovered) return discovered;
13524
13629
  let command = await resolveCommandOnPath(kind, toolSearchDirs(kind));
13525
- if (!command && platform4() === "darwin") {
13630
+ if (!command && platform5() === "darwin") {
13526
13631
  const candidates = kind === "codex" ? [
13527
13632
  "/Applications/ChatGPT.app/Contents/Resources/codex-cli/CodexCLI.app/Contents/MacOS/codex",
13528
13633
  "/Applications/ChatGPT.app/Contents/Resources/codex"
@@ -13566,19 +13671,19 @@ var ToolSelection = class {
13566
13671
  // src/daemon.ts
13567
13672
  async function runDaemon(configPath, authenticate = verifyHostPassword) {
13568
13673
  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"));
13674
+ await mkdir9(config.stateDirectory, { recursive: true, mode: 448 });
13675
+ const workspace = new Workspace(config.projects), uploads = new Uploads(workspace, join13(config.stateDirectory, "uploads")), journal = new Journal(join13(config.stateDirectory, "journal"));
13571
13676
  let secureChat;
13572
13677
  function emit(projectId, event, data) {
13573
13678
  secureChat?.publish(projectId, event, data);
13574
13679
  }
13575
13680
  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);
13681
+ const desktopBackend = new PlatformDesktop(config.stateDirectory), desktopSession = new PlatformDesktopSession();
13682
+ const desktop = new DesktopManager(desktopBackend);
13578
13683
  const remote = new RemoteManager(
13579
13684
  config,
13580
13685
  desktopBackend,
13581
- screenLock,
13686
+ desktopSession,
13582
13687
  (owner, event, data) => secureChat?.sendTo(owner, "", event, data),
13583
13688
  (owner) => secureChat?.touchUserActivity(owner) ?? false
13584
13689
  );
@@ -13589,7 +13694,7 @@ async function runDaemon(configPath, authenticate = verifyHostPassword) {
13589
13694
  });
13590
13695
  return { runtime, adapter };
13591
13696
  });
13592
- const artifacts = new Artifacts(workspace, join12(config.stateDirectory, "artifacts"), emit);
13697
+ const artifacts = new Artifacts(workspace, join13(config.stateDirectory, "artifacts"), emit);
13593
13698
  async function dispatch(c, secureSessionId) {
13594
13699
  if (c.operation.startsWith("desktop.remote.")) return remote.dispatch(c, secureSessionId);
13595
13700
  if (c.operation.startsWith("desktop.")) return desktop.dispatch(c, secureSessionId);
@@ -13712,11 +13817,13 @@ export {
13712
13817
  Workspace,
13713
13818
  atomicJson,
13714
13819
  Uploads,
13820
+ ensureHostAuth,
13715
13821
  proxyVersion,
13716
13822
  ensureHostIdentity,
13717
13823
  createRuntime,
13718
13824
  createAdapter,
13719
13825
  checkTerminalSupport,
13826
+ PlatformDesktopSession,
13720
13827
  RemoteManager,
13721
13828
  isVersionManagedPath,
13722
13829
  resolveNodePath,