@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.
- package/dist/{chunk-ARGKTEJ4.js → chunk-AIAW33NC.js} +215 -118
- package/dist/{chunk-ZS2F5EU4.js → chunk-PPH4OB7Y.js} +78 -320
- package/dist/cli.js +5 -5
- package/dist/main.js +2 -2
- package/dist/remote-worker.js +84 -26
- package/native/remote-macos.m +100 -15
- package/package.json +1 -1
- package/proxy.example.json +2 -2
- package/native/screen-unlock-macos.m +0 -140
|
@@ -2,19 +2,17 @@
|
|
|
2
2
|
import {
|
|
3
3
|
DesktopManager,
|
|
4
4
|
PlatformDesktop,
|
|
5
|
-
PlatformScreenLock,
|
|
6
5
|
REMOTE_DEFAULTS,
|
|
7
6
|
RemoteConfigSchema,
|
|
8
|
-
|
|
7
|
+
RemoteQualitySchema,
|
|
9
8
|
SignalSchema,
|
|
10
9
|
__commonJS,
|
|
11
10
|
__toESM,
|
|
12
11
|
assertDirectSignal,
|
|
13
|
-
ensureHostAuth,
|
|
14
12
|
probeRemote,
|
|
15
|
-
|
|
13
|
+
remoteSessionBudget,
|
|
16
14
|
z
|
|
17
|
-
} from "./chunk-
|
|
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
|
|
7650
|
-
import { join as
|
|
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,
|
|
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
|
|
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 =
|
|
8589
|
+
epoch = randomUUID7();
|
|
8521
8590
|
sequence = 0;
|
|
8522
8591
|
events = [];
|
|
8523
8592
|
eventBytes = 0;
|
|
8524
8593
|
stopping = false;
|
|
8525
8594
|
context;
|
|
8526
|
-
serviceAccount =
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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  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 =
|
|
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-" +
|
|
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
|
|
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
|
|
10454
|
-
import { createHash as createHash7, randomUUID as
|
|
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-${
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
11049
|
-
const logPath =
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
11155
|
-
import { join as
|
|
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
|
|
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:
|
|
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 =
|
|
11520
|
-
await
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
12146
|
-
import { randomUUID as
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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:
|
|
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
|
|
12776
|
-
import { mkdir as
|
|
12777
|
-
import { join as
|
|
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
|
|
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
|
|
12895
|
-
const logPath =
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
13085
|
-
import { access as access2, stat as
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
13162
|
-
const shell = process.env.SHELL || (
|
|
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 =
|
|
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:
|
|
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
|
|
13233
|
-
import { fileURLToPath as
|
|
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 =
|
|
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,
|
|
13335
|
+
constructor(host, desktop, desktopSession, send, touch, workerFactory = spawnWorker, probe = probeRemote) {
|
|
13242
13336
|
this.host = host;
|
|
13243
13337
|
this.desktop = desktop;
|
|
13244
|
-
this.
|
|
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
|
|
13263
|
-
|
|
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
|
|
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
|
-
|
|
13303
|
-
|
|
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 (
|
|
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
|
|
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
|
|
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
|
|
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
|
|
13382
|
-
import { execFile as
|
|
13383
|
-
import { promisify as
|
|
13384
|
-
import { join as
|
|
13385
|
-
import { homedir as homedir2, platform as
|
|
13386
|
-
var run4 =
|
|
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
|
|
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
|
|
13503
|
+
let exec2 = options.execPath ?? process.execPath;
|
|
13409
13504
|
try {
|
|
13410
|
-
|
|
13505
|
+
exec2 = await realpath7(exec2);
|
|
13411
13506
|
} catch {
|
|
13412
13507
|
}
|
|
13413
|
-
if (!isVersionManagedPath(
|
|
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(
|
|
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,
|
|
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 =
|
|
13520
|
+
const full = join12(dir, name);
|
|
13426
13521
|
try {
|
|
13427
|
-
const info = await
|
|
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:
|
|
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 &&
|
|
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
|
|
13570
|
-
const workspace = new Workspace(config.projects), uploads = new Uploads(workspace,
|
|
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),
|
|
13577
|
-
const desktop = new DesktopManager(desktopBackend
|
|
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
|
-
|
|
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,
|
|
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,
|