@skylandnpm/octopus-proxy 0.1.64 → 0.1.66
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 +25 -1
- package/dist/{chunk-J3ZMMSW2.js → chunk-IV5ZGRAM.js} +85 -43
- package/dist/{chunk-GS3Q6EU5.js → chunk-UFLXQ4HQ.js} +10 -4
- package/dist/cli.js +23 -15
- package/dist/main.js +2 -2
- package/dist/remote-worker.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -60,7 +60,7 @@ octopus-proxy restart
|
|
|
60
60
|
|
|
61
61
|
Claw 完成主机信任和账号认证后,通过端到端会话向 Proxy 发送只读 `runtime.snapshot` 命令。响应包含当前运行状态、Hub 和工具设置、授权项目路径,以及工具可提供的模型信息;机器人 Secret、工具密码和提供商凭据不会返回。工作区的“Proxy 信息”按钮可查看这些内容。
|
|
62
62
|
|
|
63
|
-
Codex 和 ZCode 由 Proxy 按授权项目启动各自的 app-server,默认随 Proxy 自动启动;服务未就绪时也可在 Claw
|
|
63
|
+
Codex 和 ZCode 由 Proxy 按授权项目启动各自的 app-server,默认随 Proxy 自动启动;服务未就绪时也可在 Claw 点击“启动服务”。若命令不可执行或某个项目启动失败,工作区会显示失败项目及原因。通过 `onboard` 或配置文件修改授权项目后,Claw 打开、切换或刷新工作区项目时会读取最新项目配置;目录变化会释放该项目的旧运行资源和终端,并刷新文件面板。Proxy 不轮询配置文件,也不因主机配置变化主动推送刷新通知。Claw 也可在授权项目菜单中点击“修改项目”,修改“Proxy 主机上的目录”并保存;Proxy 校验目录存在且未重复授权后写入主机配置,Claw 随即刷新工作区,无需重启 Proxy。修改工具命令、机器人凭据等其他设置后,仍需运行 `octopus-proxy restart`。
|
|
64
64
|
|
|
65
65
|
## Claw 设备配对与撤销
|
|
66
66
|
|
|
@@ -123,6 +123,30 @@ octopus-proxy update
|
|
|
123
123
|
|
|
124
124
|
安装或版本校验失败时不会触发服务重启;包安装失败不保证旧版本文件完整保留。已安装新版本但重启失败时会单独提示,可运行 `octopus-proxy doctor` 和 `octopus-proxy logs` 排查。此命令用于 npm 全局安装,覆盖安装前会检查 npm 安装目录是否与当前命令一致;源码、本地安装或 Node.js 环境不一致时会中止并给出提示。
|
|
125
125
|
|
|
126
|
+
## Linux 服务在线,但终端显示“未运行、未启用”
|
|
127
|
+
|
|
128
|
+
后台服务创建的终端可能没有登录会话中的 `XDG_RUNTIME_DIR`。此时 `systemctl --user` 会报 `Failed to connect to bus: No medium found`,不能据此判断服务已停止。CLI 会在未显式配置用户会话环境时,检查 `/run/user/<当前 UID>` 是否存在且属于当前用户,并用于服务查询和控制;查询失败会报告错误。此检查也支持只有 `systemd/private`、没有会话 `bus` socket 的环境。
|
|
129
|
+
|
|
130
|
+
旧版可先验证:
|
|
131
|
+
|
|
132
|
+
```sh
|
|
133
|
+
XDG_RUNTIME_DIR=/run/user/$(id -u) octopus-proxy status
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
如果显示正常,可在该服务账号的 `~/.bashrc` 中补上以下设置,使 Claw 新建的 Bash 终端也能查询用户服务:
|
|
137
|
+
|
|
138
|
+
```sh
|
|
139
|
+
if [ -z "${XDG_RUNTIME_DIR:-}" ] && [ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ]; then
|
|
140
|
+
octopus_runtime_dir="/run/user/$(id -u)"
|
|
141
|
+
if [ -d "$octopus_runtime_dir" ] && [ -O "$octopus_runtime_dir" ]; then
|
|
142
|
+
export XDG_RUNTIME_DIR="$octopus_runtime_dir"
|
|
143
|
+
fi
|
|
144
|
+
unset octopus_runtime_dir
|
|
145
|
+
fi
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
重新打开终端或运行 `source ~/.bashrc` 后验证 `octopus-proxy status`。如果对应用户运行目录或用户服务管理器不存在,仍需检查主机的登录会话和 linger 配置。
|
|
149
|
+
|
|
126
150
|
## 服务启动时报 `Unexpected token '??='`
|
|
127
151
|
|
|
128
152
|
这通常表示服务实际使用了旧版 Node.js。错误栈中 `.nvm/versions/node/v22.../lib/node_modules` 是包的安装位置,不能说明运行它的 Node.js 版本。安装器只会选择 Node.js 22 或更高版本;旧版安装器可能优先选中能执行 `--version` 的旧系统 Node。
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
probeRemote,
|
|
13
13
|
remoteSessionBudget,
|
|
14
14
|
z
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-UFLXQ4HQ.js";
|
|
16
16
|
|
|
17
17
|
// ../../node_modules/.pnpm/ajv@8.17.1/node_modules/ajv/dist/compile/codegen/code.js
|
|
18
18
|
var require_code = __commonJS({
|
|
@@ -11616,7 +11616,13 @@ function standaloneZcodeSource(source) {
|
|
|
11616
11616
|
throw new Error("Unsupported ZCode account bootstrap; native compatibility check failed");
|
|
11617
11617
|
}
|
|
11618
11618
|
body = body.replace(registryCall, `${registry}($1,{standalone:{}})`).replace(nativePorts, "$&providerRuntimeHeadersPort:$1.providerRuntimeHeadersPort,");
|
|
11619
|
-
|
|
11619
|
+
source = source.slice(0, start) + body + source.slice(end);
|
|
11620
|
+
const close = /\br\(([$\w]+),"closeSession"\)/.exec(source)?.[1];
|
|
11621
|
+
if (!close) throw new Error("Unsupported ZCode bundle: session close is unavailable");
|
|
11622
|
+
const escapedClose = close.replace(/[$]/g, "\\$&");
|
|
11623
|
+
const closeBody = new RegExp(`async function ${escapedClose}\\(([$\\w]+),([$\\w]+)\\)\\{[^}]*?await ([$\\w]+)\\.eventStore\\.deleteSession\\(([$\\w]+)\\.sessionId\\),\\{closed:!0\\}`, "g");
|
|
11624
|
+
if ([...source.matchAll(closeBody)].length !== 1) throw new Error("Unsupported ZCode session close; native compatibility check failed");
|
|
11625
|
+
return source.replace(closeBody, (match, context, _params, record2, parsed) => match.replace(",{closed:!0}", `,${record2}.persistence!=="deferred"&&await ${context}.deps.sessionStore.updateSession({id:${parsed}.sessionId,timeArchived:Date.now()}),{closed:!0}`));
|
|
11620
11626
|
}
|
|
11621
11627
|
function zcodeServerLaunch(command, args) {
|
|
11622
11628
|
if (basename4(command) !== "zcode.cjs") return { command, args: [...args] };
|
|
@@ -13613,6 +13619,7 @@ function createAdapter(runtime, workspace, uploads, emit) {
|
|
|
13613
13619
|
// src/project-registry.ts
|
|
13614
13620
|
import { realpath as realpath6, readFile as readFile9, stat as stat7 } from "node:fs/promises";
|
|
13615
13621
|
import { isAbsolute as isAbsolute4 } from "node:path";
|
|
13622
|
+
import { isDeepStrictEqual } from "node:util";
|
|
13616
13623
|
var ProjectMutationSchema = ProjectSchema.extend({
|
|
13617
13624
|
name: ProjectSchema.shape.name.trim().max(120),
|
|
13618
13625
|
specDirectories: ProjectSchema.shape.specDirectories.removeDefault().element.min(1).array().max(20).default(["specs", "docs"])
|
|
@@ -13628,13 +13635,21 @@ function rawProjectId(value) {
|
|
|
13628
13635
|
return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.id === "string" ? value.id : void 0;
|
|
13629
13636
|
}
|
|
13630
13637
|
var ProjectRegistry = class {
|
|
13631
|
-
constructor(configPath, config, runtime, adapter) {
|
|
13638
|
+
constructor(configPath, config, runtime, adapter, onChanged = () => {
|
|
13639
|
+
}) {
|
|
13632
13640
|
this.configPath = configPath;
|
|
13633
13641
|
this.config = config;
|
|
13634
13642
|
this.runtime = runtime;
|
|
13635
13643
|
this.adapter = adapter;
|
|
13644
|
+
this.onChanged = onChanged;
|
|
13636
13645
|
}
|
|
13637
13646
|
pending = Promise.resolve();
|
|
13647
|
+
// The daemon runs this under the same lock as tool selection and project mutations.
|
|
13648
|
+
reload() {
|
|
13649
|
+
return this.serial(async () => {
|
|
13650
|
+
await this.readCurrent();
|
|
13651
|
+
});
|
|
13652
|
+
}
|
|
13638
13653
|
add(payload) {
|
|
13639
13654
|
return this.serial(() => this.addUnlocked(payload));
|
|
13640
13655
|
}
|
|
@@ -13658,17 +13673,36 @@ var ProjectRegistry = class {
|
|
|
13658
13673
|
}
|
|
13659
13674
|
async readCurrent() {
|
|
13660
13675
|
const current = rawConfig(JSON.parse(await readFile9(this.configPath, "utf8")));
|
|
13661
|
-
const diskProjects = current.projects.map((
|
|
13662
|
-
|
|
13663
|
-
|
|
13664
|
-
|
|
13665
|
-
if (JSON.stringify(diskProjects) !== JSON.stringify(this.config.projects)) {
|
|
13666
|
-
throw new Error("Proxy \u914D\u7F6E\u5DF2\u5728\u4E3B\u673A\u4E0A\u88AB\u4FEE\u6539\uFF1B\u8BF7\u91CD\u542F Proxy \u540E\u518D\u7BA1\u7406\u9879\u76EE");
|
|
13667
|
-
}
|
|
13676
|
+
const diskProjects = ConfigSchema.shape.projects.parse(current.projects).map((project) => ({ ...project, directory: expandHome(project.directory) }));
|
|
13677
|
+
if (new Set(diskProjects.map((project) => project.id)).size !== diskProjects.length) throw new Error("\u9879\u76EE ID \u91CD\u590D");
|
|
13678
|
+
if (diskProjects.some((project) => !isAbsolute4(project.directory))) throw new Error("\u9879\u76EE\u76EE\u5F55\u5FC5\u987B\u662F Proxy \u4E3B\u673A\u4E0A\u7684\u7EDD\u5BF9\u8DEF\u5F84");
|
|
13679
|
+
await this.applyProjects(diskProjects);
|
|
13668
13680
|
return current;
|
|
13669
13681
|
}
|
|
13682
|
+
async persist(expected, projects) {
|
|
13683
|
+
const latest = rawConfig(JSON.parse(await readFile9(this.configPath, "utf8")));
|
|
13684
|
+
if (!isDeepStrictEqual(latest.projects, expected.projects)) {
|
|
13685
|
+
throw new Error("\u4E3B\u673A\u4E0A\u7684\u9879\u76EE\u914D\u7F6E\u5728\u4FDD\u5B58\u671F\u95F4\u53D1\u751F\u53D8\u5316\uFF0C\u8BF7\u5237\u65B0\u540E\u91CD\u8BD5");
|
|
13686
|
+
}
|
|
13687
|
+
await atomicJson(this.configPath, { ...latest, projects });
|
|
13688
|
+
}
|
|
13689
|
+
async applyProjects(projects) {
|
|
13690
|
+
if (isDeepStrictEqual(projects, this.config.projects)) return;
|
|
13691
|
+
const reset = this.config.projects.filter((previous) => !projects.some((project) => project.id === previous.id && project.directory === previous.directory));
|
|
13692
|
+
const added = projects.filter((project) => !this.config.projects.some((previous) => previous.id === project.id && previous.directory === project.directory));
|
|
13693
|
+
for (const project of reset) {
|
|
13694
|
+
await Promise.all([
|
|
13695
|
+
this.bestEffort(() => this.adapter.removeProject?.(project.id)),
|
|
13696
|
+
this.bestEffort(() => this.runtime.removeProject?.(project.id))
|
|
13697
|
+
]);
|
|
13698
|
+
}
|
|
13699
|
+
this.config.projects.splice(0, this.config.projects.length, ...projects);
|
|
13700
|
+
for (const project of added) await this.bestEffort(() => this.runtime.addProject?.(project.id));
|
|
13701
|
+
this.onChanged(reset.map((project) => project.id));
|
|
13702
|
+
}
|
|
13670
13703
|
async addUnlocked(payload) {
|
|
13671
13704
|
const parsed = ProjectMutationSchema.parse(payload);
|
|
13705
|
+
const raw = await this.readCurrent();
|
|
13672
13706
|
const directory = expandHome(parsed.directory);
|
|
13673
13707
|
if (!isAbsolute4(directory)) throw new Error("\u9879\u76EE\u76EE\u5F55\u5FC5\u987B\u662F Proxy \u4E3B\u673A\u4E0A\u7684\u7EDD\u5BF9\u8DEF\u5F84");
|
|
13674
13708
|
const info = await stat7(directory).catch(() => void 0);
|
|
@@ -13679,14 +13713,13 @@ var ProjectRegistry = class {
|
|
|
13679
13713
|
if (await realpath6(project2.directory).catch(() => project2.directory) === canonical) throw new Error("\u8BE5\u76EE\u5F55\u5DF2\u7ECF\u662F\u6388\u6743\u9879\u76EE");
|
|
13680
13714
|
}
|
|
13681
13715
|
const project = { ...parsed, directory: canonical };
|
|
13682
|
-
|
|
13683
|
-
await
|
|
13684
|
-
this.config.projects.push(project);
|
|
13685
|
-
await this.bestEffort(() => this.runtime.addProject?.(project.id));
|
|
13716
|
+
await this.persist(raw, [...raw.projects, project]);
|
|
13717
|
+
await this.applyProjects([...this.config.projects, project]);
|
|
13686
13718
|
return publicProjects(this.config);
|
|
13687
13719
|
}
|
|
13688
13720
|
async updateUnlocked(payload) {
|
|
13689
13721
|
const parsed = ProjectUpdateSchema.parse(payload);
|
|
13722
|
+
const raw = await this.readCurrent();
|
|
13690
13723
|
const index = this.config.projects.findIndex((project) => project.id === parsed.id);
|
|
13691
13724
|
if (index < 0) throw new Error(`\u672A\u77E5\u6388\u6743\u9879\u76EE\uFF1A${parsed.id}`);
|
|
13692
13725
|
const directory = expandHome(parsed.directory);
|
|
@@ -13700,21 +13733,8 @@ var ProjectRegistry = class {
|
|
|
13700
13733
|
}
|
|
13701
13734
|
}
|
|
13702
13735
|
const current = this.config.projects[index];
|
|
13703
|
-
|
|
13704
|
-
|
|
13705
|
-
const raw = await this.readCurrent();
|
|
13706
|
-
await atomicJson(this.configPath, {
|
|
13707
|
-
...raw,
|
|
13708
|
-
projects: raw.projects.map((project) => rawProjectId(project) === parsed.id ? { ...project, name: parsed.name, directory: canonical } : project)
|
|
13709
|
-
});
|
|
13710
|
-
if (directoryChanged) {
|
|
13711
|
-
await Promise.all([
|
|
13712
|
-
this.bestEffort(() => this.adapter.removeProject?.(parsed.id)),
|
|
13713
|
-
this.bestEffort(() => this.runtime.removeProject?.(parsed.id))
|
|
13714
|
-
]);
|
|
13715
|
-
}
|
|
13716
|
-
this.config.projects[index] = { ...current, name: parsed.name, directory: canonical };
|
|
13717
|
-
if (directoryChanged) await this.bestEffort(() => this.runtime.addProject?.(parsed.id));
|
|
13736
|
+
await this.persist(raw, raw.projects.map((project) => rawProjectId(project) === parsed.id ? { ...project, name: parsed.name, directory: canonical } : project));
|
|
13737
|
+
await this.applyProjects(this.config.projects.map((project) => project.id === parsed.id ? { ...current, name: parsed.name, directory: canonical } : project));
|
|
13718
13738
|
return publicProjects(this.config);
|
|
13719
13739
|
}
|
|
13720
13740
|
async removeUnlocked(payload) {
|
|
@@ -13723,16 +13743,12 @@ var ProjectRegistry = class {
|
|
|
13723
13743
|
throw new Error("\u5220\u9664\u9879\u76EE\u9700\u8981\u5408\u6CD5\u7684\u9879\u76EE ID");
|
|
13724
13744
|
}
|
|
13725
13745
|
const id = payload.id;
|
|
13746
|
+
const raw = await this.readCurrent();
|
|
13726
13747
|
if (this.config.projects.length === 1) throw new Error("\u81F3\u5C11\u9700\u8981\u4FDD\u7559\u4E00\u4E2A\u6388\u6743\u9879\u76EE");
|
|
13727
13748
|
const index = this.config.projects.findIndex((project) => project.id === id);
|
|
13728
13749
|
if (index < 0) throw new Error(`\u672A\u77E5\u6388\u6743\u9879\u76EE\uFF1A${id}`);
|
|
13729
|
-
|
|
13730
|
-
await
|
|
13731
|
-
await Promise.all([
|
|
13732
|
-
this.bestEffort(() => this.adapter.removeProject?.(id)),
|
|
13733
|
-
this.bestEffort(() => this.runtime.removeProject?.(id))
|
|
13734
|
-
]);
|
|
13735
|
-
this.config.projects.splice(index, 1);
|
|
13750
|
+
await this.persist(raw, raw.projects.filter((project) => rawProjectId(project) !== id));
|
|
13751
|
+
await this.applyProjects(this.config.projects.filter((project) => project.id !== id));
|
|
13736
13752
|
return publicProjects(this.config);
|
|
13737
13753
|
}
|
|
13738
13754
|
};
|
|
@@ -13889,6 +13905,13 @@ var TerminalManager = class {
|
|
|
13889
13905
|
terminal.process.kill();
|
|
13890
13906
|
}
|
|
13891
13907
|
}
|
|
13908
|
+
closeProject(project) {
|
|
13909
|
+
for (const terminal of this.terminals.values()) if (terminal.project === project) {
|
|
13910
|
+
this.terminals.delete(terminal.id);
|
|
13911
|
+
terminal.process.kill();
|
|
13912
|
+
this.emit(terminal.owner, project, "terminal.closed", { id: terminal.id });
|
|
13913
|
+
}
|
|
13914
|
+
}
|
|
13892
13915
|
close() {
|
|
13893
13916
|
for (const owner of new Set([...this.terminals.values()].map((t) => t.owner))) this.closeOwned(owner);
|
|
13894
13917
|
}
|
|
@@ -13898,17 +13921,30 @@ var TerminalManager = class {
|
|
|
13898
13921
|
import { execFile as execFile6 } from "node:child_process";
|
|
13899
13922
|
import { promisify as promisify6 } from "node:util";
|
|
13900
13923
|
import { userInfo as userInfo4 } from "node:os";
|
|
13924
|
+
import { lstat as lstat3 } from "node:fs/promises";
|
|
13901
13925
|
var exec = promisify6(execFile6);
|
|
13902
13926
|
var PlatformDesktopSession = class {
|
|
13903
|
-
constructor(system = process.platform, environment = process.env, execute3 = async (args) => (await exec("loginctl", ["--no-ask-password", ...args], { timeout: 5e3, maxBuffer: 16384 })).stdout, account = userInfo4()) {
|
|
13927
|
+
constructor(x11Session = "logind", system = process.platform, environment = process.env, execute3 = async (args) => (await exec("loginctl", ["--no-ask-password", ...args], { timeout: 5e3, maxBuffer: 16384 })).stdout, account = userInfo4(), inspectSocket = lstat3) {
|
|
13928
|
+
this.x11Session = x11Session;
|
|
13904
13929
|
this.system = system;
|
|
13905
13930
|
this.environment = environment;
|
|
13906
13931
|
this.execute = execute3;
|
|
13907
13932
|
this.account = account;
|
|
13933
|
+
this.inspectSocket = inspectSocket;
|
|
13908
13934
|
}
|
|
13909
13935
|
async sessionIdentity() {
|
|
13910
13936
|
if (this.system === "darwin") return String(this.account.uid);
|
|
13911
13937
|
if (this.system !== "linux") throw new Error("\u6B64\u5E73\u53F0\u4E0D\u652F\u6301\u8FDC\u7A0B\u684C\u9762\u4F1A\u8BDD");
|
|
13938
|
+
if (this.x11Session === "standalone") {
|
|
13939
|
+
const display = /^(?:unix)?:([0-9]+)(?:\.([0-9]+))?$/.exec(this.environment.DISPLAY ?? "");
|
|
13940
|
+
if (!display || this.account.uid === 0 || this.environment.WAYLAND_DISPLAY || this.environment.XDG_SESSION_TYPE === "wayland") throw new Error("\u72EC\u7ACB X11 \u8FDC\u63A7\u9700\u8981\u975E root \u7528\u6237\u548C\u672C\u5730 DISPLAY\uFF08\u5982 :99\uFF09\uFF0C\u4E0D\u652F\u6301 Wayland \u6216\u8FDC\u7A0B X11");
|
|
13941
|
+
const path = `/tmp/.X11-unix/X${Number(display[1])}`;
|
|
13942
|
+
const socket = await this.inspectSocket(path).catch(() => {
|
|
13943
|
+
throw new Error(`\u72EC\u7ACB X11 socket \u4E0D\u53EF\u7528\uFF1A${path}\uFF1B\u8BF7\u5148\u542F\u52A8 Xvfb/\u684C\u9762\u670D\u52A1`);
|
|
13944
|
+
});
|
|
13945
|
+
if (!socket.isSocket()) throw new Error(`\u72EC\u7ACB X11 \u8DEF\u5F84\u4E0D\u662F socket\uFF1A${path}`);
|
|
13946
|
+
return `x11:${this.account.uid}:${Number(display[1])}.${Number(display[2] ?? 0)}:${socket.dev}:${socket.ino}:${socket.ctimeMs}`;
|
|
13947
|
+
}
|
|
13912
13948
|
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);
|
|
13913
13949
|
const sessions = [];
|
|
13914
13950
|
for (const id of ids) {
|
|
@@ -14021,6 +14057,7 @@ var RemoteManager = class {
|
|
|
14021
14057
|
const permissions = await this.probe(this.host.stateDirectory, this.config);
|
|
14022
14058
|
const lockUnsupported = permissions.locked === true && permissions.lockCapture === false;
|
|
14023
14059
|
const available = permissions.session && permissions.capture && permissions.input && !lockUnsupported;
|
|
14060
|
+
if (available) await this.desktopSession.sessionIdentity();
|
|
14024
14061
|
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" } : {} };
|
|
14025
14062
|
} catch (error) {
|
|
14026
14063
|
return { ...base, available: false, message: error instanceof Error ? error.message : "\u8FDC\u63A7\u4F9D\u8D56\u4E0D\u53EF\u7528" };
|
|
@@ -14139,7 +14176,7 @@ var RemoteManager = class {
|
|
|
14139
14176
|
import { randomBytes as randomBytes3, randomUUID as randomUUID19 } from "node:crypto";
|
|
14140
14177
|
import { access as access3, constants as constants6, readFile as readFile10 } from "node:fs/promises";
|
|
14141
14178
|
import { platform as platform5 } from "node:os";
|
|
14142
|
-
import { isDeepStrictEqual } from "node:util";
|
|
14179
|
+
import { isDeepStrictEqual as isDeepStrictEqual2 } from "node:util";
|
|
14143
14180
|
|
|
14144
14181
|
// src/node-resolution.ts
|
|
14145
14182
|
import { realpath as realpath7, stat as stat10 } from "node:fs/promises";
|
|
@@ -14319,7 +14356,7 @@ var ToolSelection = class {
|
|
|
14319
14356
|
}
|
|
14320
14357
|
async readCurrent() {
|
|
14321
14358
|
const disk = await loadConfig(this.configPath);
|
|
14322
|
-
if (!
|
|
14359
|
+
if (!isDeepStrictEqual2(JSON.parse(JSON.stringify(disk.tool)), JSON.parse(JSON.stringify(this.config.tool))) || !isDeepStrictEqual2(disk.projects, this.config.projects) || disk.botId !== this.config.botId || disk.secretKey !== this.config.secretKey) {
|
|
14323
14360
|
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");
|
|
14324
14361
|
}
|
|
14325
14362
|
return JSON.parse(await readFile10(this.configPath, "utf8"));
|
|
@@ -14346,7 +14383,7 @@ async function runDaemon(configPath, authenticate = verifyHostPassword) {
|
|
|
14346
14383
|
secureChat?.publish(projectId, event, data);
|
|
14347
14384
|
}
|
|
14348
14385
|
const terminals = new TerminalManager(workspace, (owner, project, event, data) => secureChat?.sendTo(owner, project, event, data));
|
|
14349
|
-
const desktopBackend = new PlatformDesktop(config.stateDirectory), desktopSession = new PlatformDesktopSession();
|
|
14386
|
+
const desktopBackend = new PlatformDesktop(config.stateDirectory), desktopSession = new PlatformDesktopSession(config.desktopRemote?.x11Session);
|
|
14350
14387
|
const desktop = new DesktopManager(desktopBackend);
|
|
14351
14388
|
const remote = new RemoteManager(
|
|
14352
14389
|
config,
|
|
@@ -14363,7 +14400,12 @@ async function runDaemon(configPath, authenticate = verifyHostPassword) {
|
|
|
14363
14400
|
return { runtime, adapter };
|
|
14364
14401
|
});
|
|
14365
14402
|
const artifacts = new Artifacts(workspace, join15(config.stateDirectory, "artifacts"), emit);
|
|
14403
|
+
const projectRegistry = () => new ProjectRegistry(configPath, config, tools.current.runtime, tools.current.adapter, (resetProjects) => {
|
|
14404
|
+
for (const id of resetProjects) terminals.closeProject(id);
|
|
14405
|
+
});
|
|
14406
|
+
const reloadProjects = () => tools.exclusive(() => projectRegistry().reload());
|
|
14366
14407
|
async function dispatch(c, secureSessionId) {
|
|
14408
|
+
if (["runtime.snapshot", "workspace.projects", "runtime.models", "runtime.switchTool"].includes(c.operation)) await reloadProjects();
|
|
14367
14409
|
if (c.operation.startsWith("desktop.remote.")) return remote.dispatch(c, secureSessionId);
|
|
14368
14410
|
if (c.operation.startsWith("desktop.")) return desktop.dispatch(c, secureSessionId);
|
|
14369
14411
|
if (c.operation.startsWith("terminal.")) return terminals.dispatch(c, secureSessionId);
|
|
@@ -14383,17 +14425,17 @@ async function runDaemon(configPath, authenticate = verifyHostPassword) {
|
|
|
14383
14425
|
if (c.operation === "runtime.restart") return runtime.restart();
|
|
14384
14426
|
if (c.operation === "workspace.projects") return publicProjects(config);
|
|
14385
14427
|
if (c.operation === "workspace.projects.add") {
|
|
14386
|
-
const result2 = await tools.exclusive(() =>
|
|
14428
|
+
const result2 = await tools.exclusive(() => projectRegistry().add(p));
|
|
14387
14429
|
emit(String(p.id), "octopus.resync", {});
|
|
14388
14430
|
return result2;
|
|
14389
14431
|
}
|
|
14390
14432
|
if (c.operation === "workspace.projects.update") {
|
|
14391
|
-
const result2 = await tools.exclusive(() =>
|
|
14433
|
+
const result2 = await tools.exclusive(() => projectRegistry().update(p));
|
|
14392
14434
|
emit(String(p.id), "octopus.resync", {});
|
|
14393
14435
|
return result2;
|
|
14394
14436
|
}
|
|
14395
14437
|
if (c.operation === "workspace.projects.remove") {
|
|
14396
|
-
const result2 = await tools.exclusive(() =>
|
|
14438
|
+
const result2 = await tools.exclusive(() => projectRegistry().remove(p));
|
|
14397
14439
|
emit(String(p.id), "octopus.resync", {});
|
|
14398
14440
|
return result2;
|
|
14399
14441
|
}
|
|
@@ -4104,7 +4104,8 @@ var RemoteConfigSchema = z.object({
|
|
|
4104
4104
|
stunUrls: z.array(stunUrl).min(1).max(4).optional(),
|
|
4105
4105
|
totalBitrate: z.number().int().min(1e5).max(REMOTE_DEFAULTS.totalBitrate).default(REMOTE_DEFAULTS.totalBitrate),
|
|
4106
4106
|
videoMaxBitrate: z.number().int().min(5e4).max(REMOTE_DEFAULTS.videoMaxBitrate).default(REMOTE_DEFAULTS.videoMaxBitrate),
|
|
4107
|
-
ffmpeg: z.string().min(1).default("ffmpeg")
|
|
4107
|
+
ffmpeg: z.string().min(1).default("ffmpeg"),
|
|
4108
|
+
x11Session: z.enum(["logind", "standalone"]).default("logind")
|
|
4108
4109
|
}).strict().refine((c) => c.videoMaxBitrate <= c.totalBitrate * 0.8, "\u89C6\u9891\u989D\u5EA6\u4E0D\u5F97\u8D85\u8FC7\u603B\u9884\u7B97\u7684 80%");
|
|
4109
4110
|
function remoteSessionBudget(config, quality, legacy = false) {
|
|
4110
4111
|
const profile = legacy ? REMOTE_LEGACY_PROFILE : REMOTE_QUALITY_PROFILES[quality];
|
|
@@ -4456,8 +4457,12 @@ import { join as join2 } from "node:path";
|
|
|
4456
4457
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
4457
4458
|
import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
|
|
4458
4459
|
var exec = promisify2(execFile2);
|
|
4459
|
-
function
|
|
4460
|
-
|
|
4460
|
+
function remotePlatformError(system = process.platform, arch = process.arch, env = process.env) {
|
|
4461
|
+
if (!["darwin", "linux"].includes(system) || !["arm64", "x64"].includes(arch)) return `\u8FDC\u63A7\u4E0D\u652F\u6301\u5F53\u524D\u5E73\u53F0 ${system}/${arch}\uFF1B\u9700\u8981 macOS 12.3+ \u6216 Linux X11\uFF08x64/ARM64\uFF09`;
|
|
4462
|
+
if (system === "linux") {
|
|
4463
|
+
if (env.WAYLAND_DISPLAY || env.XDG_SESSION_TYPE === "wayland") return "\u8FDC\u63A7\u6682\u4E0D\u652F\u6301 Wayland\uFF0C\u8BF7\u4F7F\u7528 X11 \u684C\u9762\u4F1A\u8BDD";
|
|
4464
|
+
if (!env.DISPLAY) return "Proxy \u672A\u8FDE\u63A5 X11 \u56FE\u5F62\u4F1A\u8BDD\uFF1B\u8BF7\u4E3A\u5B9E\u9645\u8FD0\u884C Proxy \u7684\u670D\u52A1\u8BBE\u7F6E\u684C\u9762\u7528\u6237\u7684 DISPLAY \u548C XAUTHORITY\uFF0C\u5BB9\u5668\u4E2D\u8BF7\u5148\u542F\u52A8 Xvfb/\u684C\u9762\u670D\u52A1";
|
|
4465
|
+
}
|
|
4461
4466
|
}
|
|
4462
4467
|
async function ensureRemoteHelper(stateDirectory) {
|
|
4463
4468
|
await mkdir2(stateDirectory, { recursive: true, mode: 448 });
|
|
@@ -4485,7 +4490,8 @@ async function ensureRemoteHelper(stateDirectory) {
|
|
|
4485
4490
|
}
|
|
4486
4491
|
}
|
|
4487
4492
|
async function probeRemote(stateDirectory, config) {
|
|
4488
|
-
|
|
4493
|
+
const platformError = remotePlatformError();
|
|
4494
|
+
if (platformError) throw new Error(platformError);
|
|
4489
4495
|
await import("@roamhq/wrtc").catch(() => {
|
|
4490
4496
|
throw new Error("WebRTC \u539F\u751F\u4F9D\u8D56\u4E0D\u53EF\u7528\uFF0C\u8BF7\u91CD\u65B0\u5B89\u88C5\u65B0\u7248 Proxy");
|
|
4491
4497
|
});
|
package/dist/cli.js
CHANGED
|
@@ -35,20 +35,20 @@ import {
|
|
|
35
35
|
supportedPlatform,
|
|
36
36
|
systemdUnitPath,
|
|
37
37
|
toolSearchDirs
|
|
38
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-IV5ZGRAM.js";
|
|
39
39
|
import {
|
|
40
40
|
DESKTOP_PERMISSION_GUIDANCE,
|
|
41
41
|
PlatformDesktop,
|
|
42
42
|
checkDesktop,
|
|
43
43
|
requestDesktopPermission
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-UFLXQ4HQ.js";
|
|
45
45
|
|
|
46
46
|
// src/cli.ts
|
|
47
47
|
import { spawn as spawn2 } from "node:child_process";
|
|
48
48
|
import { access as access4 } from "node:fs/promises";
|
|
49
49
|
|
|
50
50
|
// src/doctor.ts
|
|
51
|
-
import { access as access2, chmod, constants, mkdir as mkdir3, stat } from "node:fs/promises";
|
|
51
|
+
import { access as access2, chmod, constants, mkdir as mkdir3, stat as stat2 } from "node:fs/promises";
|
|
52
52
|
import { isAbsolute as isAbsolute3, join } from "node:path";
|
|
53
53
|
import { execFile as execFile3 } from "node:child_process";
|
|
54
54
|
import { promisify as promisify3 } from "node:util";
|
|
@@ -130,8 +130,9 @@ async function launchdStatus() {
|
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
// src/systemd.ts
|
|
133
|
-
import { mkdir as mkdir2, rm as rm2, writeFile as writeFile2 } from "node:fs/promises";
|
|
133
|
+
import { mkdir as mkdir2, rm as rm2, stat, writeFile as writeFile2 } from "node:fs/promises";
|
|
134
134
|
import { dirname as dirname2 } from "node:path";
|
|
135
|
+
import { userInfo as userInfo2 } from "node:os";
|
|
135
136
|
import { execFile as execFile2 } from "node:child_process";
|
|
136
137
|
import { promisify as promisify2 } from "node:util";
|
|
137
138
|
var run2 = promisify2(execFile2);
|
|
@@ -170,8 +171,15 @@ async function writeUnitFile(text) {
|
|
|
170
171
|
return path;
|
|
171
172
|
}
|
|
172
173
|
async function systemctl(args) {
|
|
174
|
+
const env = { ...process.env };
|
|
175
|
+
if (!env.XDG_RUNTIME_DIR && !env.DBUS_SESSION_BUS_ADDRESS) {
|
|
176
|
+
const uid = userInfo2().uid;
|
|
177
|
+
const runtimeDirectory = `/run/user/${uid}`;
|
|
178
|
+
const info = await stat(runtimeDirectory).catch(() => void 0);
|
|
179
|
+
if (info?.isDirectory() && info.uid === uid) env.XDG_RUNTIME_DIR = runtimeDirectory;
|
|
180
|
+
}
|
|
173
181
|
try {
|
|
174
|
-
const { stdout: stdout3 } = await run2("systemctl", ["--user", ...args]);
|
|
182
|
+
const { stdout: stdout3 } = await run2("systemctl", ["--user", ...args], { env });
|
|
175
183
|
return stdout3;
|
|
176
184
|
} catch (error) {
|
|
177
185
|
const stderr = error instanceof Error ? error.stderr ?? error.message : String(error);
|
|
@@ -214,9 +222,9 @@ async function systemdUninstall() {
|
|
|
214
222
|
await systemdReload();
|
|
215
223
|
}
|
|
216
224
|
async function systemdStatus() {
|
|
217
|
-
const
|
|
218
|
-
const
|
|
219
|
-
return { active:
|
|
225
|
+
const output = await systemctl(["show", SYSTEMD_UNIT, "--property=ActiveState", "--property=UnitFileState"]);
|
|
226
|
+
const properties = Object.fromEntries(output.trim().split("\n").map((line) => line.split("=")));
|
|
227
|
+
return { active: properties.ActiveState === "active", enabled: properties.UnitFileState === "enabled" };
|
|
220
228
|
}
|
|
221
229
|
|
|
222
230
|
// src/service.ts
|
|
@@ -339,7 +347,7 @@ async function runDoctor(opts) {
|
|
|
339
347
|
results.push(await checkTerminalSupport(opts.fix));
|
|
340
348
|
let config;
|
|
341
349
|
try {
|
|
342
|
-
const info = await
|
|
350
|
+
const info = await stat2(opts.configPath).catch((error) => {
|
|
343
351
|
if (error.code === "ENOENT") throw new Error(`\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${opts.configPath}\uFF1B\u5148\u8FD0\u884C octopus-proxy onboard`);
|
|
344
352
|
throw error;
|
|
345
353
|
});
|
|
@@ -356,7 +364,7 @@ async function runDoctor(opts) {
|
|
|
356
364
|
}
|
|
357
365
|
if (config) {
|
|
358
366
|
results.push(...await checkDesktop(config.stateDirectory));
|
|
359
|
-
const remote = new RemoteManager(config, new PlatformDesktop(config.stateDirectory), new PlatformDesktopSession(), () => {
|
|
367
|
+
const remote = new RemoteManager(config, new PlatformDesktop(config.stateDirectory), new PlatformDesktopSession(config.desktopRemote?.x11Session), () => {
|
|
360
368
|
}, () => false);
|
|
361
369
|
try {
|
|
362
370
|
const status = await remote.status();
|
|
@@ -457,7 +465,7 @@ async function runDoctor(opts) {
|
|
|
457
465
|
import { createInterface } from "node:readline/promises";
|
|
458
466
|
import { randomBytes } from "node:crypto";
|
|
459
467
|
import { stdin, stdout } from "node:process";
|
|
460
|
-
import { access as access3, constants as constants2, mkdir as mkdir4, stat as
|
|
468
|
+
import { access as access3, constants as constants2, mkdir as mkdir4, stat as stat3 } from "node:fs/promises";
|
|
461
469
|
import { execFile as execFile4 } from "node:child_process";
|
|
462
470
|
import { platform } from "node:os";
|
|
463
471
|
import { isAbsolute as isAbsolute4, join as join2 } from "node:path";
|
|
@@ -501,7 +509,7 @@ async function validateToolCommand(value, kind) {
|
|
|
501
509
|
const command2 = expandHome(value);
|
|
502
510
|
if (!isAbsolute4(command2)) return "\u670D\u52A1\u73AF\u5883\u9700\u8981\u7EDD\u5BF9\u8DEF\u5F84";
|
|
503
511
|
try {
|
|
504
|
-
if (!(await
|
|
512
|
+
if (!(await stat3(command2)).isFile()) return "\u547D\u4EE4\u8DEF\u5F84\u5FC5\u987B\u6307\u5411\u53EF\u6267\u884C\u6587\u4EF6";
|
|
505
513
|
await access3(command2, constants2.X_OK);
|
|
506
514
|
} catch {
|
|
507
515
|
return `\u547D\u4EE4\u4E0D\u5B58\u5728\u6216\u4E0D\u53EF\u6267\u884C\uFF1A${command2}`;
|
|
@@ -514,7 +522,7 @@ async function validateToolCommand(value, kind) {
|
|
|
514
522
|
return void 0;
|
|
515
523
|
}
|
|
516
524
|
async function runOnboard(opts) {
|
|
517
|
-
const configExists = await
|
|
525
|
+
const configExists = await stat3(opts.configPath).then(() => true, (error) => {
|
|
518
526
|
if (error.code === "ENOENT") return false;
|
|
519
527
|
throw error;
|
|
520
528
|
});
|
|
@@ -564,7 +572,7 @@ async function runOnboard(opts) {
|
|
|
564
572
|
console.log(" \u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84");
|
|
565
573
|
continue;
|
|
566
574
|
}
|
|
567
|
-
if (!await
|
|
575
|
+
if (!await stat3(project.directory).then(() => true, () => false)) {
|
|
568
576
|
console.log(" \u76EE\u5F55\u4E0D\u5B58\u5728");
|
|
569
577
|
continue;
|
|
570
578
|
}
|
|
@@ -593,7 +601,7 @@ async function runOnboard(opts) {
|
|
|
593
601
|
console.log(" \u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84");
|
|
594
602
|
continue;
|
|
595
603
|
}
|
|
596
|
-
if (!await
|
|
604
|
+
if (!await stat3(directory).then(() => true, () => false)) {
|
|
597
605
|
console.log(" \u76EE\u5F55\u4E0D\u5B58\u5728");
|
|
598
606
|
continue;
|
|
599
607
|
}
|
package/dist/main.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import {
|
|
3
3
|
ConfigError,
|
|
4
4
|
runDaemon
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-IV5ZGRAM.js";
|
|
6
|
+
import "./chunk-UFLXQ4HQ.js";
|
|
7
7
|
|
|
8
8
|
// src/main.ts
|
|
9
9
|
runDaemon(process.env.OCTOPUS_PROXY_CONFIG ?? "proxy.json").catch((error) => {
|
package/dist/remote-worker.js
CHANGED