@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.
- package/dist/{chunk-DSAJ4SAH.js → chunk-6NV726RL.js} +225 -118
- package/dist/{chunk-KRNEKDBF.js → chunk-PPH4OB7Y.js} +78 -320
- package/dist/cli.js +5 -5
- package/dist/main.js +2 -2
- package/dist/remote-worker.js +83 -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,16 @@ 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";
|
|
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
|
|
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
|
|
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
|
|
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
|
|
13162
|
-
const shell = process.env.SHELL || (
|
|
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 =
|
|
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:
|
|
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
|
|
13233
|
-
import { fileURLToPath as
|
|
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 =
|
|
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,
|
|
13345
|
+
constructor(host, desktop, desktopSession, send, touch, workerFactory = spawnWorker, probe = probeRemote) {
|
|
13242
13346
|
this.host = host;
|
|
13243
13347
|
this.desktop = desktop;
|
|
13244
|
-
this.
|
|
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
|
|
13263
|
-
|
|
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
|
|
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
|
-
|
|
13303
|
-
|
|
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 (
|
|
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
|
|
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
|
|
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
|
|
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
|
|
13382
|
-
import { execFile as
|
|
13383
|
-
import { promisify as
|
|
13384
|
-
import { join as
|
|
13385
|
-
import { homedir as homedir2, platform as
|
|
13386
|
-
var run4 =
|
|
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
|
|
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
|
|
13513
|
+
let exec2 = options.execPath ?? process.execPath;
|
|
13409
13514
|
try {
|
|
13410
|
-
|
|
13515
|
+
exec2 = await realpath7(exec2);
|
|
13411
13516
|
} catch {
|
|
13412
13517
|
}
|
|
13413
|
-
if (!isVersionManagedPath(
|
|
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(
|
|
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,
|
|
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 =
|
|
13530
|
+
const full = join12(dir, name);
|
|
13426
13531
|
try {
|
|
13427
|
-
const info = await
|
|
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:
|
|
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 &&
|
|
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
|
|
13570
|
-
const workspace = new Workspace(config.projects), uploads = new Uploads(workspace,
|
|
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),
|
|
13577
|
-
const desktop = new DesktopManager(desktopBackend
|
|
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
|
-
|
|
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,
|
|
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,
|