@skylandnpm/octopus-proxy 0.1.41 → 0.1.43
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/README.md +6 -2
- package/dist/{chunk-WSL4LYYW.js → chunk-HB3ETNMW.js} +256 -44
- package/dist/cli.js +56 -122
- package/dist/main.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -98,9 +98,9 @@ octopus-proxy onboard --tool pi
|
|
|
98
98
|
octopus-proxy update
|
|
99
99
|
```
|
|
100
100
|
|
|
101
|
-
命令通过 `npm view @skylandnpm/octopus-proxy@latest version` 查询最新版本。存在新版本时,输入 `y` 或 `yes`
|
|
101
|
+
命令通过 `npm view @skylandnpm/octopus-proxy@latest version` 查询最新版本。存在新版本时,输入 `y` 或 `yes` 确认覆盖安装;直接回车或输入其他内容会取消。确认后使用 npm 全局安装查到的版本,校验安装版本成功后才重启已安装的服务,并检查运行状态后反馈结果。安装期间不会主动停止服务。配置、主机身份和已有服务定义会保留;未安装服务时仅更新 npm 包。
|
|
102
102
|
|
|
103
|
-
|
|
103
|
+
安装或版本校验失败时不会触发服务重启;包安装失败不保证旧版本文件完整保留。已安装新版本但重启失败时会单独提示,可运行 `octopus-proxy doctor` 和 `octopus-proxy logs` 排查。此命令用于 npm 全局安装,覆盖安装前会检查 npm 安装目录是否与当前命令一致;源码、本地安装或 Node.js 环境不一致时会中止并给出提示。
|
|
104
104
|
|
|
105
105
|
## 发布
|
|
106
106
|
|
|
@@ -116,6 +116,10 @@ npm run publish
|
|
|
116
116
|
|
|
117
117
|
## 编码工具账号与 OpenCode 服务
|
|
118
118
|
|
|
119
|
+
Claw 机器人设置可通过已解锁的端到端会话查询和切换当前机器人连接的 Proxy 智能体。`runtime.models` 接收 `{ "kind": "codex" }`(也支持 `opencode`、`zcode`、`pi`),临时查询所选工具原生模型目录,不修改当前 `tool`。`runtime.switchTool` 使用相同参数,先启动新工具并验证原生连接,成功后以 0600 权限原子保存配置,再关闭旧工具的 Proxy 管理进程;外部 OpenCode 服务继续由主机管理。
|
|
120
|
+
|
|
121
|
+
各工具的命令、OpenCode 地址和服务密码保存在配置文件的 `toolProfiles` 中,切回时恢复。首次选择工具会在此 Proxy 主机探测命令;自定义安装位置可预先设置 `toolProfiles.<kind>`,格式与 `tool` 相同,且其中 `kind` 必须匹配。智能体未安装、启动或连接失败时保留当前工具和配置。已打开工作区会通过 `octopus.resync` 更新能力、会话和模型。
|
|
122
|
+
|
|
119
123
|
OpenCode 2.0.19 使用 external HTTP 模式。`tool.command` 应为 `/Users/luhuili/.opencode/bin/opencode`,`tool.baseUrl` 明确指向回环服务,`tool.password` 必填并由 onboard 自动生成。Proxy 可拉起未监听的服务,也可连接以同一密码预先启动的服务;若配置端口已被密码不同的服务占用,Proxy 会在随机回环端口启动自己的实例并在后续重启时复用,避免接管或停止未知进程。启动日志保存在 `stateDirectory/opencode-serve.log`。如需只连接自行管理的服务,可设置 `autoStart: false`。旧顶层配置、managed 模式和 v1 API 不再兼容;Proxy 不停止或重启已有 OpenCode 服务。
|
|
120
124
|
|
|
121
125
|
Claw 的“编码工具账号”窗口支持 Codex API Key/设备码、ZCode 原生 Z.AI OAuth、OpenCode 原生 API Key/OAuth。凭据由编码工具保存,模型列表从原生接口读取。详细行为与验证边界见 [编码工具账号](../../docs/tool-authentication.md)。
|
|
@@ -402,11 +402,11 @@ var require_codegen = __commonJS({
|
|
|
402
402
|
const rhs = this.rhs === void 0 ? "" : ` = ${this.rhs}`;
|
|
403
403
|
return `${varKind} ${this.name}${rhs};` + _n;
|
|
404
404
|
}
|
|
405
|
-
optimizeNames(names,
|
|
405
|
+
optimizeNames(names, constants7) {
|
|
406
406
|
if (!names[this.name.str])
|
|
407
407
|
return;
|
|
408
408
|
if (this.rhs)
|
|
409
|
-
this.rhs = optimizeExpr(this.rhs, names,
|
|
409
|
+
this.rhs = optimizeExpr(this.rhs, names, constants7);
|
|
410
410
|
return this;
|
|
411
411
|
}
|
|
412
412
|
get names() {
|
|
@@ -423,10 +423,10 @@ var require_codegen = __commonJS({
|
|
|
423
423
|
render({ _n }) {
|
|
424
424
|
return `${this.lhs} = ${this.rhs};` + _n;
|
|
425
425
|
}
|
|
426
|
-
optimizeNames(names,
|
|
426
|
+
optimizeNames(names, constants7) {
|
|
427
427
|
if (this.lhs instanceof code_1.Name && !names[this.lhs.str] && !this.sideEffects)
|
|
428
428
|
return;
|
|
429
|
-
this.rhs = optimizeExpr(this.rhs, names,
|
|
429
|
+
this.rhs = optimizeExpr(this.rhs, names, constants7);
|
|
430
430
|
return this;
|
|
431
431
|
}
|
|
432
432
|
get names() {
|
|
@@ -487,8 +487,8 @@ var require_codegen = __commonJS({
|
|
|
487
487
|
optimizeNodes() {
|
|
488
488
|
return `${this.code}` ? this : void 0;
|
|
489
489
|
}
|
|
490
|
-
optimizeNames(names,
|
|
491
|
-
this.code = optimizeExpr(this.code, names,
|
|
490
|
+
optimizeNames(names, constants7) {
|
|
491
|
+
this.code = optimizeExpr(this.code, names, constants7);
|
|
492
492
|
return this;
|
|
493
493
|
}
|
|
494
494
|
get names() {
|
|
@@ -517,12 +517,12 @@ var require_codegen = __commonJS({
|
|
|
517
517
|
}
|
|
518
518
|
return nodes.length > 0 ? this : void 0;
|
|
519
519
|
}
|
|
520
|
-
optimizeNames(names,
|
|
520
|
+
optimizeNames(names, constants7) {
|
|
521
521
|
const { nodes } = this;
|
|
522
522
|
let i = nodes.length;
|
|
523
523
|
while (i--) {
|
|
524
524
|
const n = nodes[i];
|
|
525
|
-
if (n.optimizeNames(names,
|
|
525
|
+
if (n.optimizeNames(names, constants7))
|
|
526
526
|
continue;
|
|
527
527
|
subtractNames(names, n.names);
|
|
528
528
|
nodes.splice(i, 1);
|
|
@@ -575,12 +575,12 @@ var require_codegen = __commonJS({
|
|
|
575
575
|
return void 0;
|
|
576
576
|
return this;
|
|
577
577
|
}
|
|
578
|
-
optimizeNames(names,
|
|
578
|
+
optimizeNames(names, constants7) {
|
|
579
579
|
var _a;
|
|
580
|
-
this.else = (_a = this.else) === null || _a === void 0 ? void 0 : _a.optimizeNames(names,
|
|
581
|
-
if (!(super.optimizeNames(names,
|
|
580
|
+
this.else = (_a = this.else) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants7);
|
|
581
|
+
if (!(super.optimizeNames(names, constants7) || this.else))
|
|
582
582
|
return;
|
|
583
|
-
this.condition = optimizeExpr(this.condition, names,
|
|
583
|
+
this.condition = optimizeExpr(this.condition, names, constants7);
|
|
584
584
|
return this;
|
|
585
585
|
}
|
|
586
586
|
get names() {
|
|
@@ -603,10 +603,10 @@ var require_codegen = __commonJS({
|
|
|
603
603
|
render(opts) {
|
|
604
604
|
return `for(${this.iteration})` + super.render(opts);
|
|
605
605
|
}
|
|
606
|
-
optimizeNames(names,
|
|
607
|
-
if (!super.optimizeNames(names,
|
|
606
|
+
optimizeNames(names, constants7) {
|
|
607
|
+
if (!super.optimizeNames(names, constants7))
|
|
608
608
|
return;
|
|
609
|
-
this.iteration = optimizeExpr(this.iteration, names,
|
|
609
|
+
this.iteration = optimizeExpr(this.iteration, names, constants7);
|
|
610
610
|
return this;
|
|
611
611
|
}
|
|
612
612
|
get names() {
|
|
@@ -642,10 +642,10 @@ var require_codegen = __commonJS({
|
|
|
642
642
|
render(opts) {
|
|
643
643
|
return `for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})` + super.render(opts);
|
|
644
644
|
}
|
|
645
|
-
optimizeNames(names,
|
|
646
|
-
if (!super.optimizeNames(names,
|
|
645
|
+
optimizeNames(names, constants7) {
|
|
646
|
+
if (!super.optimizeNames(names, constants7))
|
|
647
647
|
return;
|
|
648
|
-
this.iterable = optimizeExpr(this.iterable, names,
|
|
648
|
+
this.iterable = optimizeExpr(this.iterable, names, constants7);
|
|
649
649
|
return this;
|
|
650
650
|
}
|
|
651
651
|
get names() {
|
|
@@ -687,11 +687,11 @@ var require_codegen = __commonJS({
|
|
|
687
687
|
(_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNodes();
|
|
688
688
|
return this;
|
|
689
689
|
}
|
|
690
|
-
optimizeNames(names,
|
|
690
|
+
optimizeNames(names, constants7) {
|
|
691
691
|
var _a, _b;
|
|
692
|
-
super.optimizeNames(names,
|
|
693
|
-
(_a = this.catch) === null || _a === void 0 ? void 0 : _a.optimizeNames(names,
|
|
694
|
-
(_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names,
|
|
692
|
+
super.optimizeNames(names, constants7);
|
|
693
|
+
(_a = this.catch) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants7);
|
|
694
|
+
(_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names, constants7);
|
|
695
695
|
return this;
|
|
696
696
|
}
|
|
697
697
|
get names() {
|
|
@@ -992,7 +992,7 @@ var require_codegen = __commonJS({
|
|
|
992
992
|
function addExprNames(names, from) {
|
|
993
993
|
return from instanceof code_1._CodeOrName ? addNames(names, from.names) : names;
|
|
994
994
|
}
|
|
995
|
-
function optimizeExpr(expr, names,
|
|
995
|
+
function optimizeExpr(expr, names, constants7) {
|
|
996
996
|
if (expr instanceof code_1.Name)
|
|
997
997
|
return replaceName(expr);
|
|
998
998
|
if (!canOptimize(expr))
|
|
@@ -1007,14 +1007,14 @@ var require_codegen = __commonJS({
|
|
|
1007
1007
|
return items;
|
|
1008
1008
|
}, []));
|
|
1009
1009
|
function replaceName(n) {
|
|
1010
|
-
const c =
|
|
1010
|
+
const c = constants7[n.str];
|
|
1011
1011
|
if (c === void 0 || names[n.str] !== 1)
|
|
1012
1012
|
return n;
|
|
1013
1013
|
delete names[n.str];
|
|
1014
1014
|
return c;
|
|
1015
1015
|
}
|
|
1016
1016
|
function canOptimize(e) {
|
|
1017
|
-
return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 &&
|
|
1017
|
+
return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 && constants7[c.str] !== void 0);
|
|
1018
1018
|
}
|
|
1019
1019
|
}
|
|
1020
1020
|
function subtractNames(names, from) {
|
|
@@ -11715,7 +11715,7 @@ async function ensureHostIdentity(stateDirectory) {
|
|
|
11715
11715
|
|
|
11716
11716
|
// src/daemon.ts
|
|
11717
11717
|
import { mkdir as mkdir11 } from "node:fs/promises";
|
|
11718
|
-
import { join as
|
|
11718
|
+
import { join as join15 } from "node:path";
|
|
11719
11719
|
|
|
11720
11720
|
// src/workspace-diff.ts
|
|
11721
11721
|
import { execFile as execFile2 } from "node:child_process";
|
|
@@ -11987,9 +11987,9 @@ var Uploads = class {
|
|
|
11987
11987
|
this.directory = directory;
|
|
11988
11988
|
}
|
|
11989
11989
|
pending = /* @__PURE__ */ new Map();
|
|
11990
|
-
serial(id,
|
|
11990
|
+
serial(id, run5) {
|
|
11991
11991
|
const work = (this.pending.get(id) ?? Promise.resolve()).catch(() => {
|
|
11992
|
-
}).then(
|
|
11992
|
+
}).then(run5);
|
|
11993
11993
|
this.pending.set(id, work);
|
|
11994
11994
|
const clear = () => {
|
|
11995
11995
|
if (this.pending.get(id) === work) this.pending.delete(id);
|
|
@@ -12197,6 +12197,8 @@ var Operations = [
|
|
|
12197
12197
|
"auth.cancel",
|
|
12198
12198
|
"runtime.status",
|
|
12199
12199
|
"runtime.snapshot",
|
|
12200
|
+
"runtime.models",
|
|
12201
|
+
"runtime.switchTool",
|
|
12200
12202
|
"runtime.start",
|
|
12201
12203
|
"runtime.stop",
|
|
12202
12204
|
"runtime.restart",
|
|
@@ -12538,7 +12540,7 @@ var Journal = class {
|
|
|
12538
12540
|
if (active.hash !== hash) return Promise.resolve(this.failure(command, "Request id reused"));
|
|
12539
12541
|
return active.result;
|
|
12540
12542
|
}
|
|
12541
|
-
const
|
|
12543
|
+
const run5 = async () => {
|
|
12542
12544
|
const path = join6(this.directory, `${command.id}.json`);
|
|
12543
12545
|
const readOnly = command.operation.startsWith("desktop.") || isReadOperation(command.operation) || isAuthentication(command.operation);
|
|
12544
12546
|
if (!readOnly) {
|
|
@@ -12560,8 +12562,8 @@ var Journal = class {
|
|
|
12560
12562
|
return response;
|
|
12561
12563
|
};
|
|
12562
12564
|
const key = this.queueKey(command);
|
|
12563
|
-
const result = key === void 0 ?
|
|
12564
|
-
}).then(
|
|
12565
|
+
const result = key === void 0 ? run5() : (this.queues.get(key) ?? Promise.resolve()).catch(() => {
|
|
12566
|
+
}).then(run5);
|
|
12565
12567
|
if (key !== void 0) {
|
|
12566
12568
|
this.queues.set(key, result);
|
|
12567
12569
|
result.finally(() => {
|
|
@@ -12669,9 +12671,9 @@ var Artifacts = class {
|
|
|
12669
12671
|
this.emit = emit;
|
|
12670
12672
|
}
|
|
12671
12673
|
pending = /* @__PURE__ */ new Map();
|
|
12672
|
-
serial(project,
|
|
12674
|
+
serial(project, run5) {
|
|
12673
12675
|
const result = (this.pending.get(project) ?? Promise.resolve()).catch(() => {
|
|
12674
|
-
}).then(
|
|
12676
|
+
}).then(run5);
|
|
12675
12677
|
this.pending.set(project, result);
|
|
12676
12678
|
return result;
|
|
12677
12679
|
}
|
|
@@ -15073,7 +15075,7 @@ var OpenCodeAdapter = class {
|
|
|
15073
15075
|
});
|
|
15074
15076
|
}
|
|
15075
15077
|
};
|
|
15076
|
-
const
|
|
15078
|
+
const run5 = async () => {
|
|
15077
15079
|
if (method.type === "key") {
|
|
15078
15080
|
if (typeof input.key !== "string" || !input.key.trim()) throw new Error("API Key required");
|
|
15079
15081
|
await this.request(project, "POST", `/api/integration/${this.identifier(integration.id)}/connect/key`, { key: input.key, ...method.label ? { label: method.label } : {}, ...Object.keys(input).length > 1 ? { answer: input } : {} }, controller.signal);
|
|
@@ -15091,7 +15093,7 @@ var OpenCodeAdapter = class {
|
|
|
15091
15093
|
};
|
|
15092
15094
|
} else await wait();
|
|
15093
15095
|
};
|
|
15094
|
-
void
|
|
15096
|
+
void run5().catch(() => this.auth.update(project, flow.flowId, { status: "failed", message: "OpenCode \u8BA4\u8BC1\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u8BA4\u8BC1\u4FE1\u606F\u4E0E\u4F9B\u5E94\u5546\u914D\u7F6E\u540E\u91CD\u8BD5" }));
|
|
15095
15097
|
return { ...flow };
|
|
15096
15098
|
}
|
|
15097
15099
|
subscribe(project) {
|
|
@@ -15906,6 +15908,8 @@ import { join as join11 } from "node:path";
|
|
|
15906
15908
|
var DAEMON_OPERATIONS = [
|
|
15907
15909
|
"runtime.status",
|
|
15908
15910
|
"runtime.snapshot",
|
|
15911
|
+
"runtime.models",
|
|
15912
|
+
"runtime.switchTool",
|
|
15909
15913
|
"runtime.start",
|
|
15910
15914
|
"runtime.stop",
|
|
15911
15915
|
"runtime.restart",
|
|
@@ -17770,23 +17774,228 @@ var DesktopManager = class {
|
|
|
17770
17774
|
}
|
|
17771
17775
|
};
|
|
17772
17776
|
|
|
17777
|
+
// src/tool-selection.ts
|
|
17778
|
+
import { randomBytes as randomBytes2, randomUUID as randomUUID18 } from "node:crypto";
|
|
17779
|
+
import { access as access3, constants as constants6, readFile as readFile10 } from "node:fs/promises";
|
|
17780
|
+
import { platform as platform5 } from "node:os";
|
|
17781
|
+
import { isDeepStrictEqual } from "node:util";
|
|
17782
|
+
|
|
17783
|
+
// src/node-resolution.ts
|
|
17784
|
+
import { realpath as realpath7, stat as stat12 } from "node:fs/promises";
|
|
17785
|
+
import { execFile as execFile7 } from "node:child_process";
|
|
17786
|
+
import { promisify as promisify7 } from "node:util";
|
|
17787
|
+
import { join as join14 } from "node:path";
|
|
17788
|
+
import { homedir as homedir2, platform as platform4 } from "node:os";
|
|
17789
|
+
var run4 = promisify7(execFile7);
|
|
17790
|
+
var VERSION_MANAGED_SEGMENTS = /* @__PURE__ */ new Set([".nvm", "nvm", ".volta", "volta", ".fnm", "fnm", ".asdf", "asdf", ".mise", "mise"]);
|
|
17791
|
+
var execProbe = async (path) => {
|
|
17792
|
+
try {
|
|
17793
|
+
await run4(path, ["--version"], { timeout: 5e3 });
|
|
17794
|
+
return true;
|
|
17795
|
+
} catch {
|
|
17796
|
+
return false;
|
|
17797
|
+
}
|
|
17798
|
+
};
|
|
17799
|
+
function isVersionManagedPath(path) {
|
|
17800
|
+
return path.split("/").some((segment) => VERSION_MANAGED_SEGMENTS.has(segment));
|
|
17801
|
+
}
|
|
17802
|
+
function nodeCandidates() {
|
|
17803
|
+
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"];
|
|
17804
|
+
}
|
|
17805
|
+
async function resolveNodePath(options = {}) {
|
|
17806
|
+
const probe = options.probe ?? execProbe;
|
|
17807
|
+
if (options.pinned) {
|
|
17808
|
+
if (!await probe(options.pinned)) throw new Error(`--node-path ${options.pinned} is not a working node executable`);
|
|
17809
|
+
return { nodePath: options.pinned, source: "pinned" };
|
|
17810
|
+
}
|
|
17811
|
+
let exec = options.execPath ?? process.execPath;
|
|
17812
|
+
try {
|
|
17813
|
+
exec = await realpath7(exec);
|
|
17814
|
+
} catch {
|
|
17815
|
+
}
|
|
17816
|
+
if (!isVersionManagedPath(exec) && await probe(exec)) return { nodePath: exec, source: "exec" };
|
|
17817
|
+
for (const candidate of nodeCandidates()) if (!isVersionManagedPath(candidate) && await probe(candidate)) return { nodePath: candidate, source: "candidate" };
|
|
17818
|
+
if (await probe(exec)) return { nodePath: exec, source: "exec", warning: `only a version-manager node was found (${exec}); install node with the system package manager or pass --node-path, otherwise the service breaks when that node is upgraded` };
|
|
17819
|
+
return { nodePath: null, source: "exec" };
|
|
17820
|
+
}
|
|
17821
|
+
function toolSearchDirs(kind) {
|
|
17822
|
+
const dirs = [...describeTool(kind).searchDirs, join14(homedir2(), ".local", "bin"), join14(homedir2(), ".opencode", "bin"), join14(homedir2(), ".codex", "bin")];
|
|
17823
|
+
return [...new Set(dirs)];
|
|
17824
|
+
}
|
|
17825
|
+
async function resolveCommandOnPath(name, extraDirs = []) {
|
|
17826
|
+
const dirs = [...(process.env.PATH ?? "").split(":"), ...extraDirs].filter(Boolean);
|
|
17827
|
+
for (const dir of new Set(dirs)) {
|
|
17828
|
+
const full = join14(dir, name);
|
|
17829
|
+
try {
|
|
17830
|
+
const info = await stat12(full);
|
|
17831
|
+
if (info.isFile() && info.mode & 73) return full;
|
|
17832
|
+
} catch {
|
|
17833
|
+
}
|
|
17834
|
+
}
|
|
17835
|
+
return null;
|
|
17836
|
+
}
|
|
17837
|
+
|
|
17838
|
+
// src/tool-selection.ts
|
|
17839
|
+
var selectionSchema = z.object({ kind: z.enum(TOOL_KINDS) }).strict();
|
|
17840
|
+
var ToolSelection = class {
|
|
17841
|
+
constructor(configPath, config, create) {
|
|
17842
|
+
this.configPath = configPath;
|
|
17843
|
+
this.config = config;
|
|
17844
|
+
this.create = create;
|
|
17845
|
+
this.current = create({ ...config, tool: { ...config.tool } });
|
|
17846
|
+
}
|
|
17847
|
+
current;
|
|
17848
|
+
pending = Promise.resolve();
|
|
17849
|
+
closed = false;
|
|
17850
|
+
discovered = /* @__PURE__ */ new Map();
|
|
17851
|
+
exclusive(work) {
|
|
17852
|
+
const result = this.pending.catch(() => {
|
|
17853
|
+
}).then(() => {
|
|
17854
|
+
if (this.closed) throw new Error("Proxy \u5DF2\u505C\u6B62");
|
|
17855
|
+
return work();
|
|
17856
|
+
});
|
|
17857
|
+
this.pending = result;
|
|
17858
|
+
return result;
|
|
17859
|
+
}
|
|
17860
|
+
models(payload, projectId) {
|
|
17861
|
+
const { kind } = selectionSchema.parse(payload);
|
|
17862
|
+
return this.exclusive(async () => {
|
|
17863
|
+
if (kind === this.current.runtime.kind) {
|
|
17864
|
+
return { ...await buildProxySnapshot(this.config, this.current.runtime, this.current.adapter, projectId), activeTool: kind };
|
|
17865
|
+
}
|
|
17866
|
+
const raw = await this.readCurrent();
|
|
17867
|
+
const tool = await this.toolConfig(kind, raw);
|
|
17868
|
+
const candidateConfig = { ...this.config, tool };
|
|
17869
|
+
candidateConfig.projects = [this.config.projects.find((project) => project.id === projectId) ?? this.config.projects[0]];
|
|
17870
|
+
const candidate = this.create(candidateConfig);
|
|
17871
|
+
try {
|
|
17872
|
+
await candidate.runtime.init();
|
|
17873
|
+
return { ...await buildProxySnapshot(candidateConfig, candidate.runtime, candidate.adapter, projectId), activeTool: this.current.runtime.kind };
|
|
17874
|
+
} finally {
|
|
17875
|
+
await this.release(candidate);
|
|
17876
|
+
}
|
|
17877
|
+
});
|
|
17878
|
+
}
|
|
17879
|
+
switchTool(payload) {
|
|
17880
|
+
const { kind } = selectionSchema.parse(payload);
|
|
17881
|
+
return this.exclusive(async () => {
|
|
17882
|
+
if (kind === this.current.runtime.kind) return this.current.runtime.status();
|
|
17883
|
+
const raw = await this.readCurrent();
|
|
17884
|
+
const tool = await this.toolConfig(kind, raw);
|
|
17885
|
+
const candidate = this.create({ ...this.config, tool });
|
|
17886
|
+
let committed = false;
|
|
17887
|
+
try {
|
|
17888
|
+
await candidate.runtime.init();
|
|
17889
|
+
const status = await candidate.runtime.status();
|
|
17890
|
+
if (!status.healthy) throw new Error(status.error ?? `${describeTool(kind).label} \u670D\u52A1\u672A\u5C31\u7EEA`);
|
|
17891
|
+
if (candidate.adapter.supportedOperations().includes("configuration.models")) {
|
|
17892
|
+
await candidate.adapter.dispatch(this.modelCommand(candidate.runtime.config.projects[0].id));
|
|
17893
|
+
}
|
|
17894
|
+
const latest = await this.readCurrent();
|
|
17895
|
+
if (this.closed) throw new Error("Proxy \u5DF2\u505C\u6B62");
|
|
17896
|
+
await atomicJson(this.configPath, {
|
|
17897
|
+
...latest,
|
|
17898
|
+
tool,
|
|
17899
|
+
toolProfiles: { ...this.profiles(latest), [this.config.tool.kind]: this.config.tool, [kind]: tool }
|
|
17900
|
+
});
|
|
17901
|
+
const previous = this.current;
|
|
17902
|
+
this.current = candidate;
|
|
17903
|
+
this.config.tool = tool;
|
|
17904
|
+
committed = true;
|
|
17905
|
+
await this.release(previous);
|
|
17906
|
+
return candidate.runtime.status();
|
|
17907
|
+
} finally {
|
|
17908
|
+
if (!committed) await this.release(candidate);
|
|
17909
|
+
}
|
|
17910
|
+
});
|
|
17911
|
+
}
|
|
17912
|
+
modelCommand(projectId) {
|
|
17913
|
+
return { type: "command", id: randomUUID18(), botId: this.config.botId, operation: "configuration.models", projectId, payload: {} };
|
|
17914
|
+
}
|
|
17915
|
+
profiles(raw) {
|
|
17916
|
+
return raw.toolProfiles && typeof raw.toolProfiles === "object" && !Array.isArray(raw.toolProfiles) ? raw.toolProfiles : {};
|
|
17917
|
+
}
|
|
17918
|
+
async toolConfig(kind, raw) {
|
|
17919
|
+
const saved = this.profiles(raw)[kind];
|
|
17920
|
+
if (saved) {
|
|
17921
|
+
const tool2 = ToolSchema.parse(saved);
|
|
17922
|
+
if (tool2.kind !== kind) throw new Error("Proxy \u667A\u80FD\u4F53\u914D\u7F6E\u4E0E\u7C7B\u578B\u4E0D\u4E00\u81F4");
|
|
17923
|
+
return { ...tool2, command: expandHome(tool2.command || describeTool(kind).defaultCommand), autoStart: true };
|
|
17924
|
+
}
|
|
17925
|
+
const discovered = this.discovered.get(kind);
|
|
17926
|
+
if (discovered) return discovered;
|
|
17927
|
+
let command = await resolveCommandOnPath(kind, toolSearchDirs(kind));
|
|
17928
|
+
if (!command && platform5() === "darwin") {
|
|
17929
|
+
const candidates = kind === "codex" ? [
|
|
17930
|
+
"/Applications/ChatGPT.app/Contents/Resources/codex-cli/CodexCLI.app/Contents/MacOS/codex",
|
|
17931
|
+
"/Applications/ChatGPT.app/Contents/Resources/codex"
|
|
17932
|
+
] : kind === "zcode" ? ["/Applications/ZCode.app/Contents/Resources/glm/zcode.cjs"] : [];
|
|
17933
|
+
for (const path of candidates) {
|
|
17934
|
+
if (await access3(path, constants6.X_OK).then(() => true, () => false)) {
|
|
17935
|
+
command = path;
|
|
17936
|
+
break;
|
|
17937
|
+
}
|
|
17938
|
+
}
|
|
17939
|
+
}
|
|
17940
|
+
if (!command) throw new Error(`${describeTool(kind).label} \u672A\u5B89\u88C5\u6216\u547D\u4EE4\u4E0D\u53EF\u6267\u884C\uFF1B\u8BF7\u5728\u6B64 Proxy \u4E3B\u673A\u914D\u7F6E toolProfiles.${kind}.command \u540E\u91CD\u8BD5`);
|
|
17941
|
+
const tool = ToolSchema.parse({
|
|
17942
|
+
kind,
|
|
17943
|
+
command,
|
|
17944
|
+
autoStart: true,
|
|
17945
|
+
...kind === "opencode" ? { baseUrl: "http://127.0.0.1:4096", password: randomBytes2(32).toString("base64url") } : {}
|
|
17946
|
+
});
|
|
17947
|
+
this.discovered.set(kind, tool);
|
|
17948
|
+
return tool;
|
|
17949
|
+
}
|
|
17950
|
+
async readCurrent() {
|
|
17951
|
+
const disk = await loadConfig(this.configPath);
|
|
17952
|
+
if (!isDeepStrictEqual(JSON.parse(JSON.stringify(disk.tool)), JSON.parse(JSON.stringify(this.config.tool))) || !isDeepStrictEqual(disk.projects, this.config.projects) || disk.botId !== this.config.botId || disk.secretKey !== this.config.secretKey) {
|
|
17953
|
+
throw new Error("Proxy \u914D\u7F6E\u5DF2\u5728\u4E3B\u673A\u4E0A\u88AB\u4FEE\u6539\uFF1B\u8BF7\u91CD\u542F Proxy \u540E\u518D\u5207\u6362\u667A\u80FD\u4F53");
|
|
17954
|
+
}
|
|
17955
|
+
return JSON.parse(await readFile10(this.configPath, "utf8"));
|
|
17956
|
+
}
|
|
17957
|
+
async release(instance) {
|
|
17958
|
+
instance.adapter.close();
|
|
17959
|
+
instance.runtime.detach();
|
|
17960
|
+
if (instance.runtime.kind !== "opencode") await instance.runtime.stop().catch(() => {
|
|
17961
|
+
});
|
|
17962
|
+
}
|
|
17963
|
+
close() {
|
|
17964
|
+
this.closed = true;
|
|
17965
|
+
void this.release(this.current);
|
|
17966
|
+
}
|
|
17967
|
+
};
|
|
17968
|
+
|
|
17773
17969
|
// src/daemon.ts
|
|
17774
17970
|
async function runDaemon(configPath, authenticate = verifyHostPassword) {
|
|
17775
17971
|
const config = await loadConfig(configPath);
|
|
17776
17972
|
await mkdir11(config.stateDirectory, { recursive: true, mode: 448 });
|
|
17777
|
-
const workspace = new Workspace(config.projects),
|
|
17973
|
+
const workspace = new Workspace(config.projects), uploads = new Uploads(workspace, join15(config.stateDirectory, "uploads")), journal = new Journal(join15(config.stateDirectory, "journal"));
|
|
17778
17974
|
let secureChat;
|
|
17779
17975
|
function emit(projectId, event, data) {
|
|
17780
17976
|
secureChat?.publish(projectId, event, data);
|
|
17781
17977
|
}
|
|
17782
17978
|
const terminals = new TerminalManager(workspace, (owner, project, event, data) => secureChat?.sendTo(owner, project, event, data));
|
|
17783
17979
|
const desktop = new DesktopManager(new PlatformDesktop(config.stateDirectory), new PlatformScreenLock(config.stateDirectory));
|
|
17784
|
-
const
|
|
17785
|
-
|
|
17980
|
+
const tools = new ToolSelection(configPath, config, (toolConfig) => {
|
|
17981
|
+
const runtime = createRuntime(toolConfig);
|
|
17982
|
+
const adapter = createAdapter(runtime, workspace, uploads, (project, event, data) => {
|
|
17983
|
+
if (tools.current.runtime === runtime) emit(project, event, data);
|
|
17984
|
+
});
|
|
17985
|
+
return { runtime, adapter };
|
|
17986
|
+
});
|
|
17987
|
+
const artifacts = new Artifacts(workspace, join15(config.stateDirectory, "artifacts"), emit);
|
|
17786
17988
|
async function dispatch(c, secureSessionId) {
|
|
17787
17989
|
if (c.operation.startsWith("desktop.")) return desktop.dispatch(c, secureSessionId);
|
|
17788
17990
|
if (c.operation.startsWith("terminal.")) return terminals.dispatch(c, secureSessionId);
|
|
17789
17991
|
const p = c.payload;
|
|
17992
|
+
if (c.operation === "runtime.models") return tools.models(p, c.projectId);
|
|
17993
|
+
if (c.operation === "runtime.switchTool") {
|
|
17994
|
+
const result2 = await tools.switchTool(p);
|
|
17995
|
+
emit("", "octopus.resync", { toolChanged: true });
|
|
17996
|
+
return result2;
|
|
17997
|
+
}
|
|
17998
|
+
const { runtime, adapter } = tools.current;
|
|
17790
17999
|
if (c.operation === "runtime.status") return { ...await runtime.status(), capabilities: adapter.supportedOperations() };
|
|
17791
18000
|
if (c.operation === "runtime.snapshot") return buildProxySnapshot(config, runtime, adapter, c.projectId);
|
|
17792
18001
|
if (c.operation === "runtime.logs") return runtime.logs();
|
|
@@ -17795,17 +18004,17 @@ async function runDaemon(configPath, authenticate = verifyHostPassword) {
|
|
|
17795
18004
|
if (c.operation === "runtime.restart") return runtime.restart();
|
|
17796
18005
|
if (c.operation === "workspace.projects") return publicProjects(config);
|
|
17797
18006
|
if (c.operation === "workspace.projects.add") {
|
|
17798
|
-
const result2 = await
|
|
18007
|
+
const result2 = await tools.exclusive(() => new ProjectRegistry(configPath, config, tools.current.runtime, tools.current.adapter).add(p));
|
|
17799
18008
|
emit(String(p.id), "octopus.resync", {});
|
|
17800
18009
|
return result2;
|
|
17801
18010
|
}
|
|
17802
18011
|
if (c.operation === "workspace.projects.update") {
|
|
17803
|
-
const result2 = await
|
|
18012
|
+
const result2 = await tools.exclusive(() => new ProjectRegistry(configPath, config, tools.current.runtime, tools.current.adapter).update(p));
|
|
17804
18013
|
emit(String(p.id), "octopus.resync", {});
|
|
17805
18014
|
return result2;
|
|
17806
18015
|
}
|
|
17807
18016
|
if (c.operation === "workspace.projects.remove") {
|
|
17808
|
-
const result2 = await
|
|
18017
|
+
const result2 = await tools.exclusive(() => new ProjectRegistry(configPath, config, tools.current.runtime, tools.current.adapter).remove(p));
|
|
17809
18018
|
emit(String(p.id), "octopus.resync", {});
|
|
17810
18019
|
return result2;
|
|
17811
18020
|
}
|
|
@@ -17847,7 +18056,7 @@ async function runDaemon(configPath, authenticate = verifyHostPassword) {
|
|
|
17847
18056
|
terminals.closeOwned(owner);
|
|
17848
18057
|
desktop.closeOwned(owner);
|
|
17849
18058
|
}, authenticate);
|
|
17850
|
-
await runtime.init().catch((error) => console.error(`${runtime.kind} startup:`, error.message));
|
|
18059
|
+
await tools.current.runtime.init().catch((error) => console.error(`${tools.current.runtime.kind} startup:`, error.message));
|
|
17851
18060
|
secureChat.start();
|
|
17852
18061
|
const scan = setInterval(async () => {
|
|
17853
18062
|
for (const p of config.projects) await artifacts.scan(p.id).catch(() => {
|
|
@@ -17864,8 +18073,7 @@ async function runDaemon(configPath, authenticate = verifyHostPassword) {
|
|
|
17864
18073
|
secureChat?.stop();
|
|
17865
18074
|
terminals.close();
|
|
17866
18075
|
desktop.close();
|
|
17867
|
-
|
|
17868
|
-
adapter.close();
|
|
18076
|
+
tools.close();
|
|
17869
18077
|
}
|
|
17870
18078
|
function onSignal() {
|
|
17871
18079
|
stop();
|
|
@@ -17905,5 +18113,9 @@ export {
|
|
|
17905
18113
|
DESKTOP_PERMISSION_GUIDANCE,
|
|
17906
18114
|
requestDesktopPermission,
|
|
17907
18115
|
checkDesktop,
|
|
18116
|
+
isVersionManagedPath,
|
|
18117
|
+
resolveNodePath,
|
|
18118
|
+
toolSearchDirs,
|
|
18119
|
+
resolveCommandOnPath,
|
|
17908
18120
|
runDaemon
|
|
17909
18121
|
};
|
package/dist/cli.js
CHANGED
|
@@ -19,93 +19,42 @@ import {
|
|
|
19
19
|
ensureHostIdentity,
|
|
20
20
|
expandHome,
|
|
21
21
|
isMacOS,
|
|
22
|
+
isVersionManagedPath,
|
|
22
23
|
launchdLogPath,
|
|
23
24
|
launchdPlistPath,
|
|
24
25
|
loadConfig,
|
|
25
26
|
proxyVersion,
|
|
26
27
|
requestDesktopPermission,
|
|
28
|
+
resolveCommandOnPath,
|
|
27
29
|
resolveConfigPath,
|
|
30
|
+
resolveNodePath,
|
|
28
31
|
runDaemon,
|
|
29
32
|
supportedPlatform,
|
|
30
|
-
systemdUnitPath
|
|
31
|
-
|
|
33
|
+
systemdUnitPath,
|
|
34
|
+
toolSearchDirs
|
|
35
|
+
} from "./chunk-HB3ETNMW.js";
|
|
32
36
|
|
|
33
37
|
// src/cli.ts
|
|
34
38
|
import { spawn as spawn2 } from "node:child_process";
|
|
35
39
|
import { access as access4 } from "node:fs/promises";
|
|
36
40
|
|
|
37
41
|
// src/doctor.ts
|
|
38
|
-
import { access as access2, chmod, constants, mkdir as mkdir3, stat
|
|
39
|
-
import { isAbsolute as isAbsolute2, join
|
|
40
|
-
import { execFile as
|
|
41
|
-
import { promisify as
|
|
42
|
+
import { access as access2, chmod, constants, mkdir as mkdir3, stat } from "node:fs/promises";
|
|
43
|
+
import { isAbsolute as isAbsolute2, join } from "node:path";
|
|
44
|
+
import { execFile as execFile3 } from "node:child_process";
|
|
45
|
+
import { promisify as promisify3 } from "node:util";
|
|
42
46
|
|
|
43
47
|
// src/service.ts
|
|
44
48
|
import { readFile, access } from "node:fs/promises";
|
|
45
49
|
import { dirname as dirname3, isAbsolute, resolve } from "node:path";
|
|
46
50
|
|
|
47
|
-
// src/node-resolution.ts
|
|
48
|
-
import { realpath, stat } from "node:fs/promises";
|
|
49
|
-
import { execFile } from "node:child_process";
|
|
50
|
-
import { promisify } from "node:util";
|
|
51
|
-
import { join } from "node:path";
|
|
52
|
-
import { homedir, platform } from "node:os";
|
|
53
|
-
var run = promisify(execFile);
|
|
54
|
-
var VERSION_MANAGED_SEGMENTS = /* @__PURE__ */ new Set([".nvm", "nvm", ".volta", "volta", ".fnm", "fnm", ".asdf", "asdf", ".mise", "mise"]);
|
|
55
|
-
var execProbe = async (path) => {
|
|
56
|
-
try {
|
|
57
|
-
await run(path, ["--version"], { timeout: 5e3 });
|
|
58
|
-
return true;
|
|
59
|
-
} catch {
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
|
-
};
|
|
63
|
-
function isVersionManagedPath(path) {
|
|
64
|
-
return path.split("/").some((segment) => VERSION_MANAGED_SEGMENTS.has(segment));
|
|
65
|
-
}
|
|
66
|
-
function nodeCandidates() {
|
|
67
|
-
return platform() === "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"];
|
|
68
|
-
}
|
|
69
|
-
async function resolveNodePath(options = {}) {
|
|
70
|
-
const probe = options.probe ?? execProbe;
|
|
71
|
-
if (options.pinned) {
|
|
72
|
-
if (!await probe(options.pinned)) throw new Error(`--node-path ${options.pinned} is not a working node executable`);
|
|
73
|
-
return { nodePath: options.pinned, source: "pinned" };
|
|
74
|
-
}
|
|
75
|
-
let exec = options.execPath ?? process.execPath;
|
|
76
|
-
try {
|
|
77
|
-
exec = await realpath(exec);
|
|
78
|
-
} catch {
|
|
79
|
-
}
|
|
80
|
-
if (!isVersionManagedPath(exec) && await probe(exec)) return { nodePath: exec, source: "exec" };
|
|
81
|
-
for (const candidate of nodeCandidates()) if (!isVersionManagedPath(candidate) && await probe(candidate)) return { nodePath: candidate, source: "candidate" };
|
|
82
|
-
if (await probe(exec)) return { nodePath: exec, source: "exec", warning: `only a version-manager node was found (${exec}); install node with the system package manager or pass --node-path, otherwise the service breaks when that node is upgraded` };
|
|
83
|
-
return { nodePath: null, source: "exec" };
|
|
84
|
-
}
|
|
85
|
-
function toolSearchDirs(kind) {
|
|
86
|
-
const dirs = [...describeTool(kind).searchDirs, join(homedir(), ".local", "bin"), join(homedir(), ".opencode", "bin"), join(homedir(), ".codex", "bin")];
|
|
87
|
-
return [...new Set(dirs)];
|
|
88
|
-
}
|
|
89
|
-
async function resolveCommandOnPath(name, extraDirs = []) {
|
|
90
|
-
const dirs = [...(process.env.PATH ?? "").split(":"), ...extraDirs].filter(Boolean);
|
|
91
|
-
for (const dir of new Set(dirs)) {
|
|
92
|
-
const full = join(dir, name);
|
|
93
|
-
try {
|
|
94
|
-
const info = await stat(full);
|
|
95
|
-
if (info.isFile() && info.mode & 73) return full;
|
|
96
|
-
} catch {
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
return null;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
51
|
// src/launchd.ts
|
|
103
52
|
import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
104
53
|
import { dirname } from "node:path";
|
|
105
|
-
import { execFile
|
|
106
|
-
import { promisify
|
|
54
|
+
import { execFile } from "node:child_process";
|
|
55
|
+
import { promisify } from "node:util";
|
|
107
56
|
import { userInfo } from "node:os";
|
|
108
|
-
var
|
|
57
|
+
var run = promisify(execFile);
|
|
109
58
|
var guiDomain = () => `gui/${userInfo().uid}`;
|
|
110
59
|
function xml(value) {
|
|
111
60
|
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
@@ -138,22 +87,22 @@ async function ignoreMissing(action) {
|
|
|
138
87
|
}
|
|
139
88
|
}
|
|
140
89
|
async function launchdLoad() {
|
|
141
|
-
await ignoreMissing(() =>
|
|
142
|
-
await
|
|
90
|
+
await ignoreMissing(() => run("launchctl", ["bootout", `${guiDomain()}/${SERVICE_LABEL}`]));
|
|
91
|
+
await run("launchctl", ["bootstrap", guiDomain(), launchdPlistPath()]);
|
|
143
92
|
}
|
|
144
93
|
async function launchdStart() {
|
|
145
|
-
await ignoreMissing(() =>
|
|
146
|
-
await
|
|
94
|
+
await ignoreMissing(() => run("launchctl", ["bootstrap", guiDomain(), launchdPlistPath()]));
|
|
95
|
+
await run("launchctl", ["kickstart", `${guiDomain()}/${SERVICE_LABEL}`]);
|
|
147
96
|
}
|
|
148
97
|
async function launchdStop() {
|
|
149
|
-
await ignoreMissing(() =>
|
|
98
|
+
await ignoreMissing(() => run("launchctl", ["bootout", `${guiDomain()}/${SERVICE_LABEL}`]));
|
|
150
99
|
}
|
|
151
100
|
async function launchdRestart() {
|
|
152
101
|
const target = `${guiDomain()}/${SERVICE_LABEL}`;
|
|
153
102
|
if ((await launchdStatus()).loaded) {
|
|
154
|
-
await
|
|
103
|
+
await run("launchctl", ["kickstart", "-k", target]);
|
|
155
104
|
} else {
|
|
156
|
-
await
|
|
105
|
+
await run("launchctl", ["bootstrap", guiDomain(), launchdPlistPath()]);
|
|
157
106
|
}
|
|
158
107
|
}
|
|
159
108
|
async function launchdUninstall() {
|
|
@@ -162,7 +111,7 @@ async function launchdUninstall() {
|
|
|
162
111
|
}
|
|
163
112
|
async function launchdStatus() {
|
|
164
113
|
try {
|
|
165
|
-
const { stdout: stdout3 } = await
|
|
114
|
+
const { stdout: stdout3 } = await run("launchctl", ["print", `${guiDomain()}/${SERVICE_LABEL}`]);
|
|
166
115
|
const running = /state = running/.test(stdout3);
|
|
167
116
|
const pid = Number(/pid = (\d+)/.exec(stdout3)?.[1]);
|
|
168
117
|
return { loaded: true, running, pid: Number.isFinite(pid) && pid > 0 ? pid : void 0 };
|
|
@@ -174,9 +123,9 @@ async function launchdStatus() {
|
|
|
174
123
|
// src/systemd.ts
|
|
175
124
|
import { mkdir as mkdir2, rm as rm2, writeFile as writeFile2 } from "node:fs/promises";
|
|
176
125
|
import { dirname as dirname2 } from "node:path";
|
|
177
|
-
import { execFile as
|
|
178
|
-
import { promisify as
|
|
179
|
-
var
|
|
126
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
127
|
+
import { promisify as promisify2 } from "node:util";
|
|
128
|
+
var run2 = promisify2(execFile2);
|
|
180
129
|
function quoteSystemdArg(value) {
|
|
181
130
|
const escaped = value.replaceAll("%", "%%").replaceAll('"', '\\"');
|
|
182
131
|
return /[\s"\\]/.test(value) ? `"${escaped}"` : escaped;
|
|
@@ -213,7 +162,7 @@ async function writeUnitFile(text) {
|
|
|
213
162
|
}
|
|
214
163
|
async function systemctl(args) {
|
|
215
164
|
try {
|
|
216
|
-
const { stdout: stdout3 } = await
|
|
165
|
+
const { stdout: stdout3 } = await run2("systemctl", ["--user", ...args]);
|
|
217
166
|
return stdout3;
|
|
218
167
|
} catch (error) {
|
|
219
168
|
const stderr = error instanceof Error ? error.stderr ?? error.message : String(error);
|
|
@@ -339,14 +288,14 @@ function logLocationHint() {
|
|
|
339
288
|
|
|
340
289
|
// src/doctor.ts
|
|
341
290
|
import { randomUUID } from "node:crypto";
|
|
342
|
-
var
|
|
291
|
+
var run3 = promisify3(execFile3);
|
|
343
292
|
async function runDoctor(opts) {
|
|
344
293
|
const results = [];
|
|
345
294
|
const record = (name, status, detail) => results.push({ name, status, detail });
|
|
346
295
|
results.push(await checkTerminalSupport(opts.fix));
|
|
347
296
|
let config;
|
|
348
297
|
try {
|
|
349
|
-
const info = await
|
|
298
|
+
const info = await stat(opts.configPath).catch((error) => {
|
|
350
299
|
if (error.code === "ENOENT") throw new Error(`\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${opts.configPath}\uFF1B\u5148\u8FD0\u884C octopus-proxy onboard`);
|
|
351
300
|
throw error;
|
|
352
301
|
});
|
|
@@ -379,7 +328,7 @@ async function runDoctor(opts) {
|
|
|
379
328
|
record(checkName, "fail", `${command2} \u4E0D\u5B58\u5728\u6216\u4E0D\u53EF\u6267\u884C`);
|
|
380
329
|
} else {
|
|
381
330
|
try {
|
|
382
|
-
const { stdout: stdout3 } = await
|
|
331
|
+
const { stdout: stdout3 } = await run3(command2, [...descriptor.versionArgs], { timeout: 15e3 });
|
|
383
332
|
const version = stdout3.trim().split("\n")[0].trim();
|
|
384
333
|
if (version === descriptor.baseline) record(checkName, "ok", `${command2}\uFF08${version}\uFF09`);
|
|
385
334
|
else if (descriptor.versionPolicy === "exact") record(checkName, "warn", `${command2} \u7248\u672C\u4E3A ${version}\uFF0C\u5DF2\u9A8C\u8BC1\u57FA\u7EBF\u662F ${descriptor.baseline}\uFF08\u8BF7\u786E\u8BA4\u5916\u90E8\u670D\u52A1 API \u517C\u5BB9\uFF09`);
|
|
@@ -396,7 +345,7 @@ async function runDoctor(opts) {
|
|
|
396
345
|
if (config) {
|
|
397
346
|
const probeConfig = { ...config, tool: { ...config.tool, autoStart: false } };
|
|
398
347
|
const runtime = createRuntime(probeConfig), workspace = new Workspace(config.projects);
|
|
399
|
-
const adapter = createAdapter(runtime, workspace, new Uploads(workspace,
|
|
348
|
+
const adapter = createAdapter(runtime, workspace, new Uploads(workspace, join(config.stateDirectory, "uploads")), () => {
|
|
400
349
|
});
|
|
401
350
|
try {
|
|
402
351
|
await runtime.init();
|
|
@@ -455,11 +404,11 @@ async function runDoctor(opts) {
|
|
|
455
404
|
import { createInterface } from "node:readline/promises";
|
|
456
405
|
import { randomBytes } from "node:crypto";
|
|
457
406
|
import { stdin, stdout } from "node:process";
|
|
458
|
-
import { access as access3, constants as constants2, mkdir as mkdir4, stat as
|
|
459
|
-
import { execFile as
|
|
460
|
-
import { platform
|
|
461
|
-
import { isAbsolute as isAbsolute3, join as
|
|
462
|
-
import { promisify as
|
|
407
|
+
import { access as access3, constants as constants2, mkdir as mkdir4, stat as stat2 } from "node:fs/promises";
|
|
408
|
+
import { execFile as execFile4 } from "node:child_process";
|
|
409
|
+
import { platform } from "node:os";
|
|
410
|
+
import { isAbsolute as isAbsolute3, join as join2 } from "node:path";
|
|
411
|
+
import { promisify as promisify4 } from "node:util";
|
|
463
412
|
var DEFAULT_HUB_URL = "https://octopus.wuyehao.com";
|
|
464
413
|
async function ask(rl, question, options = {}) {
|
|
465
414
|
for (; ; ) {
|
|
@@ -488,7 +437,7 @@ var validUrl = (value) => {
|
|
|
488
437
|
var validId = (value) => !value ? "\u9879\u76EE ID \u4E0D\u80FD\u4E3A\u7A7A" : /^[a-zA-Z0-9_-]+$/.test(value) ? void 0 : "\u4EC5\u9650\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u4E0B\u5212\u7EBF\u548C\u8FDE\u5B57\u7B26";
|
|
489
438
|
var validateProjectId = (value, previousProjects) => validId(value) ?? (previousProjects.some((project) => project.id === value) ? `\u9879\u76EE ID\u300C${value}\u300D\u5DF2\u5B58\u5728\uFF0C\u8BF7\u8F93\u5165\u5176\u4ED6 ID` : void 0);
|
|
490
439
|
var validBotId = (value) => /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value) ? void 0 : "\u8BF7\u8F93\u5165\u673A\u5668\u4EBA UUID";
|
|
491
|
-
var
|
|
440
|
+
var run4 = promisify4(execFile4);
|
|
492
441
|
var MACOS_TOOL_EXAMPLES = {
|
|
493
442
|
opencode: "/Users/luhuili/.opencode/bin/opencode",
|
|
494
443
|
codex: "/Applications/ChatGPT.app/Contents/Resources/codex",
|
|
@@ -499,20 +448,20 @@ async function validateToolCommand(value, kind) {
|
|
|
499
448
|
const command2 = expandHome(value);
|
|
500
449
|
if (!isAbsolute3(command2)) return "\u670D\u52A1\u73AF\u5883\u9700\u8981\u7EDD\u5BF9\u8DEF\u5F84";
|
|
501
450
|
try {
|
|
502
|
-
if (!(await
|
|
451
|
+
if (!(await stat2(command2)).isFile()) return "\u547D\u4EE4\u8DEF\u5F84\u5FC5\u987B\u6307\u5411\u53EF\u6267\u884C\u6587\u4EF6";
|
|
503
452
|
await access3(command2, constants2.X_OK);
|
|
504
453
|
} catch {
|
|
505
454
|
return `\u547D\u4EE4\u4E0D\u5B58\u5728\u6216\u4E0D\u53EF\u6267\u884C\uFF1A${command2}`;
|
|
506
455
|
}
|
|
507
456
|
try {
|
|
508
|
-
await
|
|
457
|
+
await run4(command2, [...describeTool(kind).versionArgs], { timeout: 1e4 });
|
|
509
458
|
} catch {
|
|
510
459
|
return `${describeTool(kind).label} \u7248\u672C\u68C0\u67E5\u5931\u8D25\uFF0C\u8BF7\u786E\u8BA4\u547D\u4EE4\u8DEF\u5F84\uFF1A${command2}`;
|
|
511
460
|
}
|
|
512
461
|
return void 0;
|
|
513
462
|
}
|
|
514
463
|
async function runOnboard(opts) {
|
|
515
|
-
const configExists = await
|
|
464
|
+
const configExists = await stat2(opts.configPath).then(() => true, (error) => {
|
|
516
465
|
if (error.code === "ENOENT") return false;
|
|
517
466
|
throw error;
|
|
518
467
|
});
|
|
@@ -536,7 +485,7 @@ async function runOnboard(opts) {
|
|
|
536
485
|
if (toolKind !== previousToolKind) toolCommand = toolKind === existing?.tool.kind ? existing.tool.command : "";
|
|
537
486
|
const descriptor = describeTool(toolKind);
|
|
538
487
|
const found = await resolveCommandOnPath(toolKind, toolSearchDirs(toolKind));
|
|
539
|
-
const example =
|
|
488
|
+
const example = platform() === "darwin" ? MACOS_TOOL_EXAMPLES[toolKind] : void 0;
|
|
540
489
|
if (toolKind === "opencode") {
|
|
541
490
|
toolCommand = expandHome(await ask(rl, "OpenCode \u547D\u4EE4\uFF08Claw \u8FDC\u7A0B\u542F\u52A8\u65F6\u4F7F\u7528\uFF09", { default: toolCommand || found || example, validate: (value) => validateToolCommand(value, toolKind) }));
|
|
542
491
|
console.log(" Proxy \u53EF\u4ECE Claw \u8FDC\u7A0B\u542F\u52A8 OpenCode\uFF1B\u5DF2\u6709\u670D\u52A1\u4F1A\u76F4\u63A5\u8FDE\u63A5\u3002");
|
|
@@ -561,7 +510,7 @@ async function runOnboard(opts) {
|
|
|
561
510
|
console.log(" \u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84");
|
|
562
511
|
continue;
|
|
563
512
|
}
|
|
564
|
-
if (!await
|
|
513
|
+
if (!await stat2(project.directory).then(() => true, () => false)) {
|
|
565
514
|
console.log(" \u76EE\u5F55\u4E0D\u5B58\u5728");
|
|
566
515
|
continue;
|
|
567
516
|
}
|
|
@@ -580,7 +529,7 @@ async function runOnboard(opts) {
|
|
|
580
529
|
console.log(" \u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84");
|
|
581
530
|
continue;
|
|
582
531
|
}
|
|
583
|
-
if (!await
|
|
532
|
+
if (!await stat2(directory).then(() => true, () => false)) {
|
|
584
533
|
console.log(" \u76EE\u5F55\u4E0D\u5B58\u5728");
|
|
585
534
|
continue;
|
|
586
535
|
}
|
|
@@ -647,8 +596,8 @@ async function runOnboard(opts) {
|
|
|
647
596
|
stateDirectory = expandHome(stateDirectory);
|
|
648
597
|
await mkdir4(stateDirectory, { recursive: true, mode: 448 });
|
|
649
598
|
try {
|
|
650
|
-
if (
|
|
651
|
-
console.log(`\u622A\u5C4F\u8F85\u52A9\u7A0B\u5E8F\u8DEF\u5F84\uFF1A${
|
|
599
|
+
if (platform() === "darwin") {
|
|
600
|
+
console.log(`\u622A\u5C4F\u8F85\u52A9\u7A0B\u5E8F\u8DEF\u5F84\uFF1A${join2(stateDirectory, "octopus-desktop-capture")}\uFF08\u6587\u4EF6\u540D\u4E0D\u4E00\u5B9A\u662F\u7CFB\u7EDF\u6388\u6743\u540D\u79F0\uFF09`);
|
|
652
601
|
console.log("\u6B63\u5728\u68C0\u67E5\u5C4F\u5E55\u5F55\u5236\u6743\u9650\uFF1B\u5982\u51FA\u73B0\u7CFB\u7EDF\u6388\u6743\u63D0\u793A\uFF0C\u8BF7\u6309\u5F39\u7A97\u4E2D\u7684\u5B9E\u9645\u7A0B\u5E8F\u540D\u79F0\u6388\u6743\uFF0C\u53EF\u80FD\u663E\u793A Terminal\u3001iTerm\u3001\u7F16\u8F91\u5668\u6216 node\u3002");
|
|
653
602
|
}
|
|
654
603
|
const permission = await requestDesktopPermission(stateDirectory);
|
|
@@ -722,18 +671,18 @@ async function manageProjects(configPath2, remove) {
|
|
|
722
671
|
}
|
|
723
672
|
|
|
724
673
|
// src/update.ts
|
|
725
|
-
import { execFile as
|
|
726
|
-
import { readFile as readFile3, realpath
|
|
727
|
-
import { join as
|
|
674
|
+
import { execFile as execFile5, spawn } from "node:child_process";
|
|
675
|
+
import { readFile as readFile3, realpath } from "node:fs/promises";
|
|
676
|
+
import { join as join3 } from "node:path";
|
|
728
677
|
import { stdin as stdin2, stdout as stdout2 } from "node:process";
|
|
729
678
|
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
730
679
|
import { setTimeout as delay } from "node:timers/promises";
|
|
731
680
|
import { fileURLToPath } from "node:url";
|
|
732
|
-
import { promisify as
|
|
681
|
+
import { promisify as promisify5 } from "node:util";
|
|
733
682
|
var PACKAGE = "@skylandnpm/octopus-proxy";
|
|
734
683
|
var REGISTRY = "https://registry.npmjs.org";
|
|
735
684
|
var packageDirectory = fileURLToPath(new URL("../", import.meta.url));
|
|
736
|
-
var
|
|
685
|
+
var run5 = promisify5(execFile5);
|
|
737
686
|
function parseVersion(version) {
|
|
738
687
|
const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([\da-zA-Z-]+(?:\.[\da-zA-Z-]+)*))?(?:\+[\da-zA-Z-]+(?:\.[\da-zA-Z-]+)*)?$/.exec(version);
|
|
739
688
|
if (!match) throw new Error(`\u65E0\u6548\u7684\u7248\u672C\u53F7\uFF1A${version}`);
|
|
@@ -762,7 +711,7 @@ function message(error) {
|
|
|
762
711
|
}
|
|
763
712
|
async function npmOutput(args) {
|
|
764
713
|
try {
|
|
765
|
-
const { stdout: stdout3 } = await
|
|
714
|
+
const { stdout: stdout3 } = await run5("npm", args, { timeout: 3e4, maxBuffer: 1024 * 1024 });
|
|
766
715
|
return stdout3.trim();
|
|
767
716
|
} catch (error) {
|
|
768
717
|
throw new Error(`npm ${args[0]} \u5931\u8D25\uFF1A${message(error)}`);
|
|
@@ -772,7 +721,7 @@ async function confirmUpdate(latest) {
|
|
|
772
721
|
if (!stdin2.isTTY) throw new Error("\u66F4\u65B0\u9700\u8981\u5728\u4EA4\u4E92\u5F0F\u7EC8\u7AEF\u4E2D\u786E\u8BA4\uFF1B\u8BF7\u5728\u7EC8\u7AEF\u8FD0\u884C octopus-proxy update");
|
|
773
722
|
const rl = createInterface2({ input: stdin2, output: stdout2 });
|
|
774
723
|
try {
|
|
775
|
-
return /^(y|yes|是|确认)$/i.test((await rl.question(`\u662F\u5426\u8986\u76D6\u5B89\u88C5 ${proxyVersion} \u2192 ${latest}\uFF1F\
|
|
724
|
+
return /^(y|yes|是|确认)$/i.test((await rl.question(`\u662F\u5426\u8986\u76D6\u5B89\u88C5 ${proxyVersion} \u2192 ${latest}\uFF1F\u5B89\u88C5\u5E76\u6821\u9A8C\u6210\u529F\u540E\u4F1A\u91CD\u542F\u670D\u52A1 [y/N]: `)).trim());
|
|
776
725
|
} finally {
|
|
777
726
|
rl.close();
|
|
778
727
|
}
|
|
@@ -812,34 +761,19 @@ async function runUpdate() {
|
|
|
812
761
|
return;
|
|
813
762
|
}
|
|
814
763
|
const prefix = await npmOutput(["prefix", "--global"]);
|
|
815
|
-
const target =
|
|
816
|
-
const installedDirectory = await
|
|
817
|
-
if (installedDirectory !== await
|
|
764
|
+
const target = join3(prefix, "lib", "node_modules", PACKAGE);
|
|
765
|
+
const installedDirectory = await realpath(target).catch(() => null);
|
|
766
|
+
if (installedDirectory !== await realpath(packageDirectory)) {
|
|
818
767
|
throw new Error("\u5F53\u524D\u547D\u4EE4\u4E0D\u5C5E\u4E8E npm \u7684\u5168\u5C40\u5B89\u88C5\u76EE\u5F55\uFF1B\u8BF7\u786E\u8BA4 npm \u4E0E octopus-proxy \u4F7F\u7528\u540C\u4E00\u5957 Node.js \u73AF\u5883\uFF0C\u5E76\u4ECE\u5168\u5C40\u5B89\u88C5\u8FD0\u884C update");
|
|
819
768
|
}
|
|
820
769
|
const state = await serviceStatus();
|
|
821
770
|
try {
|
|
822
|
-
if (state.installed) {
|
|
823
|
-
console.log("\u6B63\u5728\u505C\u6B62\u670D\u52A1\u2026");
|
|
824
|
-
await controlService("stop");
|
|
825
|
-
if ((await serviceStatus()).active) throw new Error("\u670D\u52A1\u4ECD\u5728\u8FD0\u884C\uFF0C\u5DF2\u4E2D\u6B62\u8986\u76D6\u5B89\u88C5");
|
|
826
|
-
}
|
|
827
771
|
console.log(`\u6B63\u5728\u5B89\u88C5 ${PACKAGE}@${latest}\u2026`);
|
|
828
772
|
await installVersion(latest, prefix);
|
|
829
|
-
const installed = JSON.parse(await readFile3(
|
|
773
|
+
const installed = JSON.parse(await readFile3(join3(target, "package.json"), "utf8"));
|
|
830
774
|
if (installed.version !== latest) throw new Error(`\u5B89\u88C5\u7248\u672C\u6821\u9A8C\u5931\u8D25\uFF1A\u671F\u671B ${latest}\uFF0C\u5B9E\u9645 ${installed.version ?? "\u672A\u77E5"}`);
|
|
831
775
|
} catch (error) {
|
|
832
|
-
|
|
833
|
-
if (state.active) {
|
|
834
|
-
console.log("\u66F4\u65B0\u5931\u8D25\uFF0C\u6B63\u5728\u5C1D\u8BD5\u6062\u590D\u670D\u52A1\u2026");
|
|
835
|
-
try {
|
|
836
|
-
await restartAndVerify();
|
|
837
|
-
recovery = "\uFF1B\u670D\u52A1\u5DF2\u6062\u590D\u8FD0\u884C";
|
|
838
|
-
} catch (restartError) {
|
|
839
|
-
recovery = `\uFF1B\u670D\u52A1\u6062\u590D\u5931\u8D25\uFF1A${message(restartError)}`;
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
throw new Error(`\u66F4\u65B0\u5931\u8D25\uFF1A${message(error)}${recovery}`);
|
|
776
|
+
throw new Error(`\u66F4\u65B0\u5931\u8D25\uFF1A${message(error)}`);
|
|
843
777
|
}
|
|
844
778
|
if (state.installed) {
|
|
845
779
|
console.log("\u5B89\u88C5\u5B8C\u6210\uFF0C\u6B63\u5728\u91CD\u542F\u670D\u52A1\u2026");
|
|
@@ -862,7 +796,7 @@ var USAGE = `octopus-proxy \u2014 Octopus \u4E3B\u673A\u4EE3\u7406
|
|
|
862
796
|
projects \u5217\u51FA\u6388\u6743\u9879\u76EE\uFF1B\u53EF\u6309\u5E8F\u53F7\u5220\u9664\u9519\u8BEF\u914D\u7F6E\u9879
|
|
863
797
|
install \u6821\u9A8C\u914D\u7F6E\u540E\u5B89\u88C5/\u66F4\u65B0\u7CFB\u7EDF\u670D\u52A1\uFF08\u5E42\u7B49\uFF0C\u81EA\u52A8\u542F\u52A8\uFF09
|
|
864
798
|
uninstall \u505C\u6B62\u5E76\u79FB\u9664\u7CFB\u7EDF\u670D\u52A1\uFF08\u4FDD\u7559\u914D\u7F6E\u4E0E\u72B6\u6001\u6570\u636E\uFF09
|
|
865
|
-
update \u68C0\u67E5 npm \u6700\u65B0\u7248\u672C\uFF0C\u786E\u8BA4\u540E\
|
|
799
|
+
update \u68C0\u67E5 npm \u6700\u65B0\u7248\u672C\uFF0C\u786E\u8BA4\u540E\u8986\u76D6\u5B89\u88C5\uFF0C\u6821\u9A8C\u6210\u529F\u540E\u91CD\u542F
|
|
866
800
|
start \u542F\u52A8\u670D\u52A1
|
|
867
801
|
stop \u505C\u6B62\u670D\u52A1
|
|
868
802
|
restart \u91CD\u542F\u670D\u52A1
|
package/dist/main.js
CHANGED