@skylandnpm/octopus-proxy 0.1.12 → 0.1.14
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 +3 -1
- package/dist/{chunk-AGWRSSBQ.js → chunk-JHJQMGCD.js} +170 -52
- package/dist/cli.js +9 -11
- package/dist/main.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,6 +28,8 @@ octopus-proxy restart
|
|
|
28
28
|
|
|
29
29
|
Claw 打开机器人工作区时会通过 Hub 向 Proxy 发送只读 `runtime.snapshot` 命令。响应包含当前运行状态、Hub 和工具设置、授权项目路径,以及工具可提供的模型信息;机器人 Secret、工具密码和提供商凭据不会返回。工作区的“Proxy 信息”按钮可查看这些内容。
|
|
30
30
|
|
|
31
|
+
Codex 和 ZCode 由 Proxy 按授权项目启动各自的 app-server,默认随 Proxy 自动启动;服务未就绪时也可在 Claw 点击“启动服务”。若命令不可执行或某个项目启动失败,工作区会显示失败项目及原因。修改命令路径或授权项目后,运行 `octopus-proxy restart` 使配置生效。
|
|
32
|
+
|
|
31
33
|
## Pi agent
|
|
32
34
|
|
|
33
35
|
支持 Pi coding agent 的 RPC 模式,验证基线为 `@earendil-works/pi-coding-agent` **0.87.1**(旧 npm 包名为 `@mariozechner/pi-coding-agent`)。先安装 Pi,在代理运行用户下启动 `pi`,用 `/login` 配置提供商;也可按 Pi 文档配置 API key 环境变量或 `~/.pi/agent/models.json`。系统服务需要能读取相同的凭据和环境。
|
|
@@ -70,6 +72,6 @@ npm run publish
|
|
|
70
72
|
|
|
71
73
|
## 编码工具账号与 OpenCode 服务
|
|
72
74
|
|
|
73
|
-
OpenCode
|
|
75
|
+
OpenCode 使用 external HTTP 模式。可自行运行 `opencode serve --hostname 127.0.0.1 --port 4096`,也可在 Claw 工作区点击“启动服务”,由 Proxy 在目标主机使用 `tool.command` 拉起服务;配置 `tool.baseUrl` 与端口一致。启动日志保存在 `stateDirectory/opencode-serve.log`,`autoStart: true` 可让 Proxy 启动时自动尝试拉起。旧 managed 配置必须迁移;Proxy 不停止或重启已有 OpenCode 服务。
|
|
74
76
|
|
|
75
77
|
Claw 的“编码工具账号”窗口支持 Codex API Key/设备码、ZCode 原生 Z.AI OAuth、OpenCode 原生 API Key/OAuth。凭据由编码工具保存,模型列表从原生接口读取。详细行为与验证边界见 [编码工具账号](../../docs/tool-authentication.md)。
|
|
@@ -11606,8 +11606,8 @@ var ToolSchema = z.object({
|
|
|
11606
11606
|
autoStart: z.boolean().optional(),
|
|
11607
11607
|
password: z.string().optional()
|
|
11608
11608
|
}).superRefine((tool, context) => {
|
|
11609
|
-
if (tool.kind === "opencode" && tool.mode === "managed") context.addIssue({ code: z.ZodIssueCode.custom, path: ["mode"], message: "OpenCode \u4EC5\u652F\u6301 external \u6A21\u5F0F\uFF1B\u8BF7\
|
|
11610
|
-
}).transform((tool) => ({ ...tool, mode: tool.kind === "opencode" ? "external" : tool.mode ?? "managed", autoStart: tool.kind
|
|
11609
|
+
if (tool.kind === "opencode" && tool.mode === "managed") context.addIssue({ code: z.ZodIssueCode.custom, path: ["mode"], message: "OpenCode \u4EC5\u652F\u6301 external \u6A21\u5F0F\uFF1B\u8BF7\u5C06 tool.mode \u6539\u4E3A external\uFF0C\u7136\u540E\u53EF\u4ECE Claw \u8FDC\u7A0B\u542F\u52A8\u670D\u52A1" });
|
|
11610
|
+
}).transform((tool) => ({ ...tool, mode: tool.kind === "opencode" ? "external" : tool.mode ?? "managed", autoStart: tool.autoStart ?? tool.kind !== "opencode" }));
|
|
11611
11611
|
var ConfigSchema = z.object({
|
|
11612
11612
|
hubUrl: z.string().url(),
|
|
11613
11613
|
botId: z.string().uuid(),
|
|
@@ -11656,10 +11656,14 @@ async function loadConfig(path) {
|
|
|
11656
11656
|
}
|
|
11657
11657
|
}
|
|
11658
11658
|
|
|
11659
|
+
// src/version.ts
|
|
11660
|
+
import { readFileSync } from "node:fs";
|
|
11661
|
+
var proxyVersion = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
|
|
11662
|
+
|
|
11659
11663
|
// src/daemon.ts
|
|
11660
11664
|
import WebSocket2 from "ws";
|
|
11661
|
-
import { mkdir as
|
|
11662
|
-
import { join as
|
|
11665
|
+
import { mkdir as mkdir7 } from "node:fs/promises";
|
|
11666
|
+
import { join as join10 } from "node:path";
|
|
11663
11667
|
import { randomUUID as randomUUID12 } from "node:crypto";
|
|
11664
11668
|
|
|
11665
11669
|
// ../../packages/protocol/src/index.ts
|
|
@@ -14226,7 +14230,7 @@ var OpenCodeAdapter = class {
|
|
|
14226
14230
|
this.media = new RemoteMedia(workspace, () => uploads.limits.imageBytes);
|
|
14227
14231
|
}
|
|
14228
14232
|
supportedOperations() {
|
|
14229
|
-
return Operations.filter((op) => !["runtime.
|
|
14233
|
+
return Operations.filter((op) => !["runtime.stop", "runtime.restart", "runtime.logs"].includes(op));
|
|
14230
14234
|
}
|
|
14231
14235
|
auth = new AuthFlows();
|
|
14232
14236
|
authDirty = /* @__PURE__ */ new Set();
|
|
@@ -14541,6 +14545,9 @@ function redact(value) {
|
|
|
14541
14545
|
}
|
|
14542
14546
|
|
|
14543
14547
|
// src/runtime.ts
|
|
14548
|
+
import { spawn } from "node:child_process";
|
|
14549
|
+
import { mkdir as mkdir4, open as open4 } from "node:fs/promises";
|
|
14550
|
+
import { join as join5 } from "node:path";
|
|
14544
14551
|
var Runtime = class {
|
|
14545
14552
|
constructor(config) {
|
|
14546
14553
|
this.config = config;
|
|
@@ -14548,8 +14555,10 @@ var Runtime = class {
|
|
|
14548
14555
|
kind = "opencode";
|
|
14549
14556
|
version = "";
|
|
14550
14557
|
state = "external";
|
|
14558
|
+
starting;
|
|
14551
14559
|
async init() {
|
|
14552
|
-
await this.
|
|
14560
|
+
if (this.config.tool.autoStart) await this.start();
|
|
14561
|
+
else await this.health();
|
|
14553
14562
|
}
|
|
14554
14563
|
headers() {
|
|
14555
14564
|
const password = this.config.tool.password;
|
|
@@ -14557,7 +14566,7 @@ var Runtime = class {
|
|
|
14557
14566
|
}
|
|
14558
14567
|
async health() {
|
|
14559
14568
|
try {
|
|
14560
|
-
const response = await fetch(
|
|
14569
|
+
const response = await fetch(new URL("/global/health", this.config.tool.baseUrl), { headers: this.headers(), signal: AbortSignal.timeout(2e3) });
|
|
14561
14570
|
if (!response.ok) return false;
|
|
14562
14571
|
const data = await response.json();
|
|
14563
14572
|
this.version = data.version ?? this.version;
|
|
@@ -14574,11 +14583,66 @@ var Runtime = class {
|
|
|
14574
14583
|
state: this.state,
|
|
14575
14584
|
healthy,
|
|
14576
14585
|
version: this.version,
|
|
14577
|
-
...healthy ? {} : { error: `\u65E0\u6CD5\u8FDE\u63A5 OpenCode\uFF1A${this.config.tool.baseUrl}\u3002\
|
|
14586
|
+
...healthy ? {} : { error: `\u65E0\u6CD5\u8FDE\u63A5 OpenCode\uFF1A${this.config.tool.baseUrl}\u3002\u53EF\u5728 Claw \u4E2D\u70B9\u51FB\u201C\u542F\u52A8\u670D\u52A1\u201D\uFF0C\u6216\u5728\u8BE5\u4E3B\u673A\u542F\u52A8 opencode serve\uFF1B\u8BF7\u68C0\u67E5\u670D\u52A1\u5730\u5740\u548C\u5BC6\u7801\u3002` }
|
|
14578
14587
|
};
|
|
14579
14588
|
}
|
|
14580
14589
|
async start() {
|
|
14581
|
-
|
|
14590
|
+
return this.starting ??= this.launch().finally(() => {
|
|
14591
|
+
this.starting = void 0;
|
|
14592
|
+
});
|
|
14593
|
+
}
|
|
14594
|
+
async launch() {
|
|
14595
|
+
const url = new URL(this.config.tool.baseUrl);
|
|
14596
|
+
const endpoint = new URL("/global/health", url);
|
|
14597
|
+
let response;
|
|
14598
|
+
try {
|
|
14599
|
+
response = await fetch(endpoint, { headers: this.headers(), signal: AbortSignal.timeout(2e3) });
|
|
14600
|
+
} catch (error) {
|
|
14601
|
+
if (error instanceof Error && error.name === "TimeoutError") throw new Error(`OpenCode \u5730\u5740 ${url.origin} \u54CD\u5E94\u8D85\u65F6\uFF1B\u8BF7\u68C0\u67E5\u670D\u52A1\u540E\u91CD\u8BD5\uFF0CProxy \u4E0D\u4F1A\u91CD\u590D\u542F\u52A8`);
|
|
14602
|
+
}
|
|
14603
|
+
if (response) {
|
|
14604
|
+
const health = await response.json().catch(() => null);
|
|
14605
|
+
if (response.ok && health?.healthy === true) return this.status();
|
|
14606
|
+
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`);
|
|
14607
|
+
}
|
|
14608
|
+
await mkdir4(this.config.stateDirectory, { recursive: true, mode: 448 });
|
|
14609
|
+
const logPath = join5(this.config.stateDirectory, "opencode-serve.log");
|
|
14610
|
+
const log = await open4(logPath, "a", 384);
|
|
14611
|
+
const env = { ...process.env };
|
|
14612
|
+
delete env.OPENCODE_SERVER_PASSWORD;
|
|
14613
|
+
delete env.OPENCODE_SERVER_USERNAME;
|
|
14614
|
+
if (this.config.tool.password) {
|
|
14615
|
+
env.OPENCODE_SERVER_PASSWORD = this.config.tool.password;
|
|
14616
|
+
env.OPENCODE_SERVER_USERNAME = "opencode";
|
|
14617
|
+
}
|
|
14618
|
+
let child;
|
|
14619
|
+
try {
|
|
14620
|
+
child = spawn(this.config.tool.command, ["serve", "--hostname", url.hostname === "[::1]" ? "::1" : url.hostname, "--port", url.port || "80"], {
|
|
14621
|
+
cwd: this.config.projects[0].directory,
|
|
14622
|
+
env,
|
|
14623
|
+
detached: true,
|
|
14624
|
+
stdio: ["ignore", log.fd, log.fd]
|
|
14625
|
+
});
|
|
14626
|
+
await new Promise((resolve4, reject) => {
|
|
14627
|
+
child.once("spawn", resolve4);
|
|
14628
|
+
child.once("error", reject);
|
|
14629
|
+
});
|
|
14630
|
+
child.on("error", () => {
|
|
14631
|
+
});
|
|
14632
|
+
child.unref();
|
|
14633
|
+
} catch (error) {
|
|
14634
|
+
throw new Error(`\u65E0\u6CD5\u6267\u884C OpenCode \u547D\u4EE4 ${this.config.tool.command}\uFF1A${error instanceof Error ? error.message : String(error)}`);
|
|
14635
|
+
} finally {
|
|
14636
|
+
await log.close();
|
|
14637
|
+
}
|
|
14638
|
+
const deadline = Date.now() + 2e4;
|
|
14639
|
+
while (Date.now() < deadline) {
|
|
14640
|
+
if (child.exitCode !== null || child.signalCode !== null) break;
|
|
14641
|
+
if (await this.health()) return this.status();
|
|
14642
|
+
await new Promise((resolve4) => setTimeout(resolve4, 250));
|
|
14643
|
+
}
|
|
14644
|
+
if (child.exitCode === null && child.signalCode === null) child.kill("SIGTERM");
|
|
14645
|
+
throw new Error(`OpenCode \u542F\u52A8\u5931\u8D25\u6216\u5065\u5EB7\u68C0\u67E5\u8D85\u65F6\uFF1B\u8BF7\u68C0\u67E5 ${logPath}\u3001\u670D\u52A1\u5730\u5740\u548C\u5BC6\u7801`);
|
|
14582
14646
|
}
|
|
14583
14647
|
async stop() {
|
|
14584
14648
|
throw new Error("OpenCode \u670D\u52A1\u7531\u5916\u90E8\u7BA1\u7406\uFF0CProxy \u4E0D\u652F\u6301\u505C\u6B62");
|
|
@@ -14595,21 +14659,21 @@ var Runtime = class {
|
|
|
14595
14659
|
|
|
14596
14660
|
// src/tools/zcode-env.ts
|
|
14597
14661
|
import { existsSync } from "node:fs";
|
|
14598
|
-
import { dirname as dirname4, join as
|
|
14662
|
+
import { dirname as dirname4, join as join6 } from "node:path";
|
|
14599
14663
|
function zcodeEnvironment(command) {
|
|
14600
14664
|
const env = { ...process.env };
|
|
14601
14665
|
if (!env.ZCODE_BUILTIN_PROVIDER_CONFIG_FILE) {
|
|
14602
|
-
const bundled =
|
|
14666
|
+
const bundled = join6(dirname4(command), "..", "config", "provider", "zcode-builtin.json");
|
|
14603
14667
|
if (existsSync(bundled)) env.ZCODE_BUILTIN_PROVIDER_CONFIG_FILE = bundled;
|
|
14604
14668
|
}
|
|
14605
14669
|
return env;
|
|
14606
14670
|
}
|
|
14607
14671
|
|
|
14608
14672
|
// src/tools/stdio-runtime.ts
|
|
14609
|
-
import { spawn, execFile as execFile2 } from "node:child_process";
|
|
14673
|
+
import { spawn as spawn2, execFile as execFile2 } from "node:child_process";
|
|
14610
14674
|
import { promisify as promisify2 } from "node:util";
|
|
14611
|
-
import { open as
|
|
14612
|
-
import { join as
|
|
14675
|
+
import { mkdir as mkdir5, open as open5 } from "node:fs/promises";
|
|
14676
|
+
import { join as join7 } from "node:path";
|
|
14613
14677
|
|
|
14614
14678
|
// src/tools/line-rpc.ts
|
|
14615
14679
|
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
@@ -14820,6 +14884,7 @@ var StdioRuntime = class {
|
|
|
14820
14884
|
attempts = /* @__PURE__ */ new Map();
|
|
14821
14885
|
timers = /* @__PURE__ */ new Map();
|
|
14822
14886
|
refreshing = /* @__PURE__ */ new Set();
|
|
14887
|
+
failures = /* @__PURE__ */ new Map();
|
|
14823
14888
|
wanted = false;
|
|
14824
14889
|
detached = false;
|
|
14825
14890
|
async init() {
|
|
@@ -14827,7 +14892,10 @@ var StdioRuntime = class {
|
|
|
14827
14892
|
const { stdout } = await run(this.config.tool.command, [...this.descriptor.versionArgs], { timeout: 15e3 });
|
|
14828
14893
|
this.version = stdout.trim().split("\n")[0].trim();
|
|
14829
14894
|
} catch (error) {
|
|
14830
|
-
|
|
14895
|
+
const reason = `\u65E0\u6CD5\u6267\u884C ${this.config.tool.command}\uFF1A${error instanceof Error ? error.message : String(error)}`;
|
|
14896
|
+
this.wanted = this.config.tool.autoStart;
|
|
14897
|
+
for (const project of this.config.projects) this.failures.set(project.id, reason);
|
|
14898
|
+
throw new Error(reason);
|
|
14831
14899
|
}
|
|
14832
14900
|
if (this.descriptor.versionPolicy === "warn" && this.version !== this.descriptor.baseline) {
|
|
14833
14901
|
console.warn(`Warning: ${this.descriptor.label} ${this.version} differs from the verified baseline ${this.descriptor.baseline}; the app-server protocol may have drifted`);
|
|
@@ -14837,19 +14905,27 @@ var StdioRuntime = class {
|
|
|
14837
14905
|
}
|
|
14838
14906
|
async start() {
|
|
14839
14907
|
this.wanted = true;
|
|
14908
|
+
for (const timer of this.timers.values()) clearTimeout(timer);
|
|
14909
|
+
this.timers.clear();
|
|
14910
|
+
this.attempts.clear();
|
|
14840
14911
|
await Promise.allSettled(this.config.projects.map((project) => this.track(project.id, project.directory)));
|
|
14841
|
-
|
|
14912
|
+
const status = await this.status();
|
|
14913
|
+
if (!status.healthy) throw new Error(`${this.descriptor.label} \u542F\u52A8\u5931\u8D25\uFF1A${status.error ?? "\u8BF7\u68C0\u67E5 Proxy \u65E5\u5FD7"}`);
|
|
14914
|
+
return status;
|
|
14842
14915
|
}
|
|
14843
14916
|
async stop() {
|
|
14844
14917
|
this.wanted = false;
|
|
14918
|
+
for (const timer of this.timers.values()) clearTimeout(timer);
|
|
14919
|
+
this.timers.clear();
|
|
14920
|
+
this.attempts.clear();
|
|
14921
|
+
await Promise.allSettled([...this.tracking.values()]);
|
|
14845
14922
|
for (const projectId of [...this.children.keys()]) this.kill(projectId);
|
|
14923
|
+
this.failures.clear();
|
|
14846
14924
|
return this.status();
|
|
14847
14925
|
}
|
|
14848
14926
|
async restart() {
|
|
14849
|
-
this.
|
|
14850
|
-
|
|
14851
|
-
await Promise.allSettled(this.config.projects.map((project) => this.track(project.id, project.directory)));
|
|
14852
|
-
return this.status();
|
|
14927
|
+
await this.stop();
|
|
14928
|
+
return this.start();
|
|
14853
14929
|
}
|
|
14854
14930
|
async reconnectProject(projectId) {
|
|
14855
14931
|
const child = this.children.get(projectId);
|
|
@@ -14866,7 +14942,7 @@ var StdioRuntime = class {
|
|
|
14866
14942
|
async logs() {
|
|
14867
14943
|
const chunks = [];
|
|
14868
14944
|
for (const [projectId, logPath] of this.logPaths) {
|
|
14869
|
-
const file = await
|
|
14945
|
+
const file = await open5(logPath, "r").catch(() => void 0);
|
|
14870
14946
|
if (!file) continue;
|
|
14871
14947
|
try {
|
|
14872
14948
|
const size = (await file.stat()).size, bytes = Buffer.alloc(Math.min(size, 3e4));
|
|
@@ -14883,13 +14959,24 @@ ${bytes.toString("utf8")}`);
|
|
|
14883
14959
|
const children = [...this.children].map(([projectId, child]) => ({ projectId, pid: child.pid, state: child.exitCode === null && child.signalCode === null ? "running" : "stopped" }));
|
|
14884
14960
|
const alive = children.filter((child) => child.state === "running");
|
|
14885
14961
|
this.state = alive.length === 0 ? this.wanted ? "failed" : "stopped" : "running";
|
|
14886
|
-
|
|
14962
|
+
const missing = this.config.projects.filter((project) => !alive.some((child) => child.projectId === project.id));
|
|
14963
|
+
return {
|
|
14964
|
+
tool: this.kind,
|
|
14965
|
+
mode: "managed",
|
|
14966
|
+
state: this.state,
|
|
14967
|
+
healthy: missing.length === 0,
|
|
14968
|
+
version: this.version,
|
|
14969
|
+
children,
|
|
14970
|
+
...missing.length && (this.wanted || missing.some((project) => this.failures.has(project.id))) ? { error: missing.map((project) => `${project.name}\uFF1A${this.failures.get(project.id) ?? "\u8FDB\u7A0B\u672A\u8FD0\u884C"}`).join("\uFF1B") } : {}
|
|
14971
|
+
};
|
|
14887
14972
|
}
|
|
14888
14973
|
headers() {
|
|
14889
14974
|
return {};
|
|
14890
14975
|
}
|
|
14891
14976
|
detach() {
|
|
14892
14977
|
this.detached = true;
|
|
14978
|
+
for (const timer of this.timers.values()) clearTimeout(timer);
|
|
14979
|
+
this.timers.clear();
|
|
14893
14980
|
}
|
|
14894
14981
|
// Get (or lazily spawn) the RPC connection for a project. `onConnect` runs after every
|
|
14895
14982
|
// (re)spawn, e.g. Codex sends its initialize handshake there. Concurrent callers share one launch.
|
|
@@ -14912,7 +14999,7 @@ ${bytes.toString("utf8")}`);
|
|
|
14912
14999
|
const child = this.children.get(projectId);
|
|
14913
15000
|
const rpc = new LineRpc(child, { ...handlers, onClose: (reason) => {
|
|
14914
15001
|
this.connections.delete(projectId);
|
|
14915
|
-
this.onExited(projectId, reason);
|
|
15002
|
+
if (this.children.get(projectId) === child) this.onExited(projectId, reason);
|
|
14916
15003
|
handlers.onClose?.(reason);
|
|
14917
15004
|
} });
|
|
14918
15005
|
try {
|
|
@@ -14928,29 +15015,58 @@ ${bytes.toString("utf8")}`);
|
|
|
14928
15015
|
}
|
|
14929
15016
|
// Spawn the supervised child if absent (concurrent callers share one spawn); resolves once the process exists.
|
|
14930
15017
|
async track(projectId, directory) {
|
|
14931
|
-
if (this.children.has(projectId)) return;
|
|
14932
15018
|
const inFlight = this.tracking.get(projectId);
|
|
14933
15019
|
if (inFlight) return inFlight;
|
|
15020
|
+
if (this.children.has(projectId)) return;
|
|
14934
15021
|
const spawnOnce = (async () => {
|
|
14935
|
-
const logPath =
|
|
14936
|
-
|
|
14937
|
-
const
|
|
14938
|
-
|
|
15022
|
+
const logPath = join7(this.config.stateDirectory, `${this.kind}-${projectId}.log`);
|
|
15023
|
+
await mkdir5(this.config.stateDirectory, { recursive: true, mode: 448 });
|
|
15024
|
+
const log = await open5(logPath, "a", 384);
|
|
15025
|
+
let child;
|
|
15026
|
+
try {
|
|
15027
|
+
child = spawn2(this.config.tool.command, [...this.descriptor.serverArgs], { cwd: directory, env: this.kind === "zcode" ? zcodeEnvironment(this.config.tool.command) : { ...process.env }, stdio: ["pipe", "pipe", log.fd] });
|
|
15028
|
+
} catch (error) {
|
|
15029
|
+
await log.close();
|
|
15030
|
+
throw error;
|
|
15031
|
+
}
|
|
15032
|
+
child.once("close", () => {
|
|
14939
15033
|
void log.close();
|
|
14940
15034
|
});
|
|
14941
15035
|
this.children.set(projectId, child);
|
|
14942
15036
|
this.logPaths.set(projectId, logPath);
|
|
14943
|
-
child.
|
|
14944
|
-
|
|
15037
|
+
child.on("error", (error) => {
|
|
15038
|
+
this.failures.set(projectId, error.message);
|
|
14945
15039
|
});
|
|
14946
|
-
|
|
15040
|
+
child.once("exit", (code, signal) => {
|
|
15041
|
+
if (this.children.get(projectId) === child) this.onExited(projectId, `\u8FDB\u7A0B\u9000\u51FA\uFF08${code ?? signal}\uFF09`);
|
|
15042
|
+
});
|
|
15043
|
+
try {
|
|
15044
|
+
await new Promise((resolve4, reject) => {
|
|
15045
|
+
child.once("spawn", resolve4);
|
|
15046
|
+
child.once("error", reject);
|
|
15047
|
+
});
|
|
15048
|
+
this.failures.delete(projectId);
|
|
15049
|
+
} catch (error) {
|
|
15050
|
+
if (this.children.get(projectId) === child) this.children.delete(projectId);
|
|
15051
|
+
this.failures.set(projectId, error instanceof Error ? error.message : String(error));
|
|
15052
|
+
throw error;
|
|
15053
|
+
}
|
|
15054
|
+
})().catch((error) => {
|
|
15055
|
+
this.failures.set(projectId, error instanceof Error ? error.message : String(error));
|
|
15056
|
+
throw error;
|
|
15057
|
+
});
|
|
14947
15058
|
this.tracking.set(projectId, spawnOnce);
|
|
14948
15059
|
spawnOnce.finally(() => this.tracking.delete(projectId)).catch(() => {
|
|
14949
15060
|
});
|
|
14950
15061
|
return spawnOnce;
|
|
14951
15062
|
}
|
|
14952
|
-
onExited(projectId,
|
|
15063
|
+
onExited(projectId, reason) {
|
|
15064
|
+
const child = this.children.get(projectId);
|
|
15065
|
+
this.children.delete(projectId);
|
|
15066
|
+
if (child && child.exitCode === null && child.signalCode === null) child.kill("SIGTERM");
|
|
15067
|
+
this.failures.set(projectId, reason);
|
|
14953
15068
|
if (this.detached || !this.wanted || this.refreshing.has(projectId)) return;
|
|
15069
|
+
if (this.timers.has(projectId)) return;
|
|
14954
15070
|
const attempts = (this.attempts.get(projectId) ?? 0) + 1;
|
|
14955
15071
|
if (attempts > 5) {
|
|
14956
15072
|
console.error(`${projectId}: app server exited; retry budget exhausted`);
|
|
@@ -14960,6 +15076,7 @@ ${bytes.toString("utf8")}`);
|
|
|
14960
15076
|
const delay = Math.min(3e4, 1e3 * 2 ** attempts);
|
|
14961
15077
|
this.timers.set(projectId, setTimeout(() => {
|
|
14962
15078
|
this.timers.delete(projectId);
|
|
15079
|
+
if (!this.wanted || this.detached) return;
|
|
14963
15080
|
const project = this.config.projects.find((entry) => entry.id === projectId);
|
|
14964
15081
|
if (project) void this.track(projectId, project.directory).catch(() => {
|
|
14965
15082
|
});
|
|
@@ -14982,7 +15099,7 @@ ${bytes.toString("utf8")}`);
|
|
|
14982
15099
|
};
|
|
14983
15100
|
|
|
14984
15101
|
// src/tools/codex.ts
|
|
14985
|
-
import { join as
|
|
15102
|
+
import { join as join8 } from "node:path";
|
|
14986
15103
|
|
|
14987
15104
|
// src/adapter.ts
|
|
14988
15105
|
var DAEMON_OPERATIONS = [
|
|
@@ -15123,21 +15240,21 @@ var CodexAdapter = class {
|
|
|
15123
15240
|
respond({ decision: "decline" });
|
|
15124
15241
|
}
|
|
15125
15242
|
async index(project) {
|
|
15126
|
-
const stored = await readJson(
|
|
15243
|
+
const stored = await readJson(join8(this.runtime.config.stateDirectory, `codex-${project}.json`));
|
|
15127
15244
|
return { threadIds: stored?.threadIds ?? [] };
|
|
15128
15245
|
}
|
|
15129
15246
|
async remember(project, threadId) {
|
|
15130
15247
|
const index = await this.index(project);
|
|
15131
15248
|
if (!index.threadIds.includes(threadId)) {
|
|
15132
15249
|
index.threadIds.push(threadId);
|
|
15133
|
-
await atomicJson(
|
|
15250
|
+
await atomicJson(join8(this.runtime.config.stateDirectory, `codex-${project}.json`), index);
|
|
15134
15251
|
}
|
|
15135
15252
|
}
|
|
15136
15253
|
async forget(project, threadId) {
|
|
15137
15254
|
const index = await this.index(project);
|
|
15138
15255
|
if (index.threadIds.includes(threadId)) {
|
|
15139
15256
|
index.threadIds = index.threadIds.filter((entry) => entry !== threadId);
|
|
15140
|
-
await atomicJson(
|
|
15257
|
+
await atomicJson(join8(this.runtime.config.stateDirectory, `codex-${project}.json`), index);
|
|
15141
15258
|
}
|
|
15142
15259
|
}
|
|
15143
15260
|
async resume(project, threadId, rpc) {
|
|
@@ -15477,7 +15594,7 @@ var CodexAdapter = class {
|
|
|
15477
15594
|
};
|
|
15478
15595
|
|
|
15479
15596
|
// src/tools/zcode.ts
|
|
15480
|
-
import { spawn as
|
|
15597
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
15481
15598
|
import { randomUUID as randomUUID10 } from "node:crypto";
|
|
15482
15599
|
import { realpath as realpath5 } from "node:fs/promises";
|
|
15483
15600
|
var OPERATIONS2 = [
|
|
@@ -15670,7 +15787,7 @@ var ZcodeAdapter = class {
|
|
|
15670
15787
|
const flow = this.auth.create(project, () => {
|
|
15671
15788
|
child?.kill("SIGTERM");
|
|
15672
15789
|
});
|
|
15673
|
-
child =
|
|
15790
|
+
child = spawn3(this.runtime.config.tool.command, ["login", "--no-browser", "--json"], { cwd: await this.workspace.root(project), env: zcodeEnvironment(this.runtime.config.tool.command), stdio: ["ignore", "pipe", "pipe"] });
|
|
15674
15791
|
let output = "", diagnostic = "";
|
|
15675
15792
|
child.stdout?.on("data", (data) => {
|
|
15676
15793
|
output = (output + data.toString()).slice(-65536);
|
|
@@ -16106,9 +16223,9 @@ var PiAdapter = class {
|
|
|
16106
16223
|
};
|
|
16107
16224
|
|
|
16108
16225
|
// src/tools/pi-runtime.ts
|
|
16109
|
-
import { execFile as execFile3, spawn as
|
|
16110
|
-
import { mkdir as
|
|
16111
|
-
import { join as
|
|
16226
|
+
import { execFile as execFile3, spawn as spawn4 } from "node:child_process";
|
|
16227
|
+
import { mkdir as mkdir6, open as open6 } from "node:fs/promises";
|
|
16228
|
+
import { join as join9 } from "node:path";
|
|
16112
16229
|
import { promisify as promisify3 } from "node:util";
|
|
16113
16230
|
var run2 = promisify3(execFile3);
|
|
16114
16231
|
var PiRuntime = class {
|
|
@@ -16170,7 +16287,7 @@ var PiRuntime = class {
|
|
|
16170
16287
|
async logs() {
|
|
16171
16288
|
const chunks = [];
|
|
16172
16289
|
for (const path of this.logPaths) {
|
|
16173
|
-
const file = await
|
|
16290
|
+
const file = await open6(path, "r").catch(() => void 0);
|
|
16174
16291
|
if (!file) continue;
|
|
16175
16292
|
try {
|
|
16176
16293
|
const size = (await file.stat()).size, bytes = Buffer.alloc(Math.min(size, 3e4));
|
|
@@ -16186,7 +16303,7 @@ ${bytes.toString("utf8")}`);
|
|
|
16186
16303
|
sessionFile(projectId, sessionId) {
|
|
16187
16304
|
this.project(projectId);
|
|
16188
16305
|
if (!describeTool("pi").sessionPattern.test(sessionId)) throw new Error("Invalid session id");
|
|
16189
|
-
return
|
|
16306
|
+
return join9(this.config.stateDirectory, "pi-sessions", projectId, `${sessionId}.jsonl`);
|
|
16190
16307
|
}
|
|
16191
16308
|
project(id) {
|
|
16192
16309
|
const project = this.config.projects.find((entry) => entry.id === id);
|
|
@@ -16217,15 +16334,15 @@ ${bytes.toString("utf8")}`);
|
|
|
16217
16334
|
async launch(key, projectId, sessionId, handlers, generation) {
|
|
16218
16335
|
await this.kill(key);
|
|
16219
16336
|
const directory = this.project(projectId).directory;
|
|
16220
|
-
await
|
|
16221
|
-
const logPath =
|
|
16222
|
-
const log = await
|
|
16337
|
+
await mkdir6(join9(this.config.stateDirectory, "pi-sessions", projectId), { recursive: true, mode: 448 });
|
|
16338
|
+
const logPath = join9(this.config.stateDirectory, `pi-${key}.log`);
|
|
16339
|
+
const log = await open6(logPath, "a", 384);
|
|
16223
16340
|
if (generation !== this.generation) {
|
|
16224
16341
|
await log.close();
|
|
16225
16342
|
throw new Error("Pi runtime stopped during startup");
|
|
16226
16343
|
}
|
|
16227
16344
|
const args = [...describeTool("pi").serverArgs, ...sessionId ? ["--session", this.sessionFile(projectId, sessionId)] : ["--no-session"]];
|
|
16228
|
-
const child =
|
|
16345
|
+
const child = spawn4(this.config.tool.command, args, { cwd: directory, env: { ...process.env }, stdio: ["pipe", "pipe", log.fd] });
|
|
16229
16346
|
child.once("close", () => {
|
|
16230
16347
|
void log.close();
|
|
16231
16348
|
});
|
|
@@ -16282,8 +16399,8 @@ function createAdapter(runtime, workspace, uploads, emit) {
|
|
|
16282
16399
|
// src/daemon.ts
|
|
16283
16400
|
async function runDaemon(configPath) {
|
|
16284
16401
|
const config = await loadConfig(configPath);
|
|
16285
|
-
await
|
|
16286
|
-
const workspace = new Workspace(config.projects), runtime = createRuntime(config), uploads = new Uploads(workspace,
|
|
16402
|
+
await mkdir7(config.stateDirectory, { recursive: true, mode: 448 });
|
|
16403
|
+
const workspace = new Workspace(config.projects), runtime = createRuntime(config), uploads = new Uploads(workspace, join10(config.stateDirectory, "uploads")), journal = new Journal(join10(config.stateDirectory, "journal"));
|
|
16287
16404
|
const epoch = randomUUID12();
|
|
16288
16405
|
let stopping = false, botId = "";
|
|
16289
16406
|
const sockets = /* @__PURE__ */ new Set();
|
|
@@ -16293,7 +16410,7 @@ async function runDaemon(configPath) {
|
|
|
16293
16410
|
function emit(projectId, event, data) {
|
|
16294
16411
|
events.publish({ type: "event", botId, projectId, event, data });
|
|
16295
16412
|
}
|
|
16296
|
-
const adapter = createAdapter(runtime, workspace, uploads, emit), artifacts = new Artifacts(workspace,
|
|
16413
|
+
const adapter = createAdapter(runtime, workspace, uploads, emit), artifacts = new Artifacts(workspace, join10(config.stateDirectory, "artifacts"), emit);
|
|
16297
16414
|
async function dispatch(c) {
|
|
16298
16415
|
if (!adapter.supportedOperations().includes(c.operation)) throw new Error(`Unsupported ${runtime.kind} operation: ${c.operation}`);
|
|
16299
16416
|
const p = c.payload;
|
|
@@ -16343,11 +16460,11 @@ async function runDaemon(configPath) {
|
|
|
16343
16460
|
let lastPong = Date.now();
|
|
16344
16461
|
const heartbeat = setInterval(() => {
|
|
16345
16462
|
if (Date.now() - lastPong > 45e3) socket.terminate();
|
|
16346
|
-
else if (socket.readyState === WebSocket2.OPEN) socket.send(JSON.stringify({ type: "ping" }));
|
|
16463
|
+
else if (socket.readyState === WebSocket2.OPEN) socket.send(JSON.stringify({ type: "ping", proxyVersion }));
|
|
16347
16464
|
}, 15e3);
|
|
16348
16465
|
socket.on("open", () => {
|
|
16349
16466
|
attempt = 0;
|
|
16350
|
-
socket.send(JSON.stringify({ type: "hello", protocol: PROTOCOL_VERSION, version: runtime.version, capabilities: adapter.supportedOperations(), epoch }));
|
|
16467
|
+
socket.send(JSON.stringify({ type: "hello", protocol: PROTOCOL_VERSION, version: runtime.version, proxyVersion, capabilities: adapter.supportedOperations(), epoch }));
|
|
16351
16468
|
});
|
|
16352
16469
|
socket.on("message", async (raw) => {
|
|
16353
16470
|
try {
|
|
@@ -16430,5 +16547,6 @@ export {
|
|
|
16430
16547
|
Uploads,
|
|
16431
16548
|
createRuntime,
|
|
16432
16549
|
createAdapter,
|
|
16550
|
+
proxyVersion,
|
|
16433
16551
|
runDaemon
|
|
16434
16552
|
};
|
package/dist/cli.js
CHANGED
|
@@ -17,15 +17,15 @@ import {
|
|
|
17
17
|
launchdLogPath,
|
|
18
18
|
launchdPlistPath,
|
|
19
19
|
loadConfig,
|
|
20
|
+
proxyVersion,
|
|
20
21
|
resolveConfigPath,
|
|
21
22
|
runDaemon,
|
|
22
23
|
supportedPlatform,
|
|
23
24
|
systemdUnitPath
|
|
24
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-JHJQMGCD.js";
|
|
25
26
|
|
|
26
27
|
// src/cli.ts
|
|
27
28
|
import { spawn } from "node:child_process";
|
|
28
|
-
import { readFileSync } from "node:fs";
|
|
29
29
|
import { access as access4 } from "node:fs/promises";
|
|
30
30
|
|
|
31
31
|
// src/doctor.ts
|
|
@@ -363,9 +363,7 @@ async function runDoctor(opts) {
|
|
|
363
363
|
const descriptor = describeTool(config.tool.kind);
|
|
364
364
|
const checkName = `${descriptor.label} \u547D\u4EE4`;
|
|
365
365
|
const command2 = config.tool.command;
|
|
366
|
-
if (
|
|
367
|
-
record(checkName, "ok", "external \u6A21\u5F0F\u901A\u8FC7 HTTP \u68C0\u67E5\u670D\u52A1\uFF0C\u4E0D\u9700\u8981 Proxy \u6267\u884C OpenCode \u547D\u4EE4");
|
|
368
|
-
} else if (!isAbsolute2(command2)) {
|
|
366
|
+
if (!isAbsolute2(command2)) {
|
|
369
367
|
record(checkName, "warn", `\u5F53\u524D\u4E3A\u300C${command2}\u300D\uFF0C\u4F9D\u8D56 PATH \u67E5\u627E\uFF1B\u670D\u52A1\u73AF\u5883\u5EFA\u8BAE\u6539\u7528\u7EDD\u5BF9\u8DEF\u5F84\uFF08\u91CD\u65B0\u8FD0\u884C onboard \u6216\u624B\u5DE5\u4FEE\u6539\u914D\u7F6E\uFF09`);
|
|
370
368
|
} else if (!await access2(command2, constants.X_OK).then(() => true, () => false)) {
|
|
371
369
|
record(checkName, "fail", `${command2} \u4E0D\u5B58\u5728\u6216\u4E0D\u53EF\u6267\u884C`);
|
|
@@ -375,7 +373,7 @@ async function runDoctor(opts) {
|
|
|
375
373
|
const version = stdout2.trim().split("\n")[0].trim();
|
|
376
374
|
if (version === descriptor.baseline) record(checkName, "ok", `${command2}\uFF08${version}\uFF09`);
|
|
377
375
|
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`);
|
|
378
|
-
else record(checkName, "warn", `${command2} \u7248\u672C\u4E3A ${version}\uFF0C\u5DF2\u9A8C\u8BC1\u57FA\u7EBF\u662F ${descriptor.baseline}\
|
|
376
|
+
else record(checkName, "warn", `${command2} \u7248\u672C\u4E3A ${version}\uFF0C\u5DF2\u9A8C\u8BC1\u57FA\u7EBF\u662F ${descriptor.baseline}\uFF1B\u5DE5\u5177\u534F\u8BAE\u53EF\u80FD\u6F02\u79FB`);
|
|
379
377
|
} catch (error) {
|
|
380
378
|
record(checkName, "warn", `\u6267\u884C ${command2} ${descriptor.versionArgs.join(" ")} \u5931\u8D25\uFF1A${error instanceof Error ? error.message : String(error)}`);
|
|
381
379
|
}
|
|
@@ -525,8 +523,8 @@ async function runOnboard(opts) {
|
|
|
525
523
|
const found = await resolveCommandOnPath(toolKind, toolSearchDirs(toolKind));
|
|
526
524
|
const example = platform2() === "darwin" ? MACOS_TOOL_EXAMPLES[toolKind] : void 0;
|
|
527
525
|
if (toolKind === "opencode") {
|
|
528
|
-
toolCommand = toolCommand || found ||
|
|
529
|
-
console.log("
|
|
526
|
+
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) }));
|
|
527
|
+
console.log(" Proxy \u53EF\u4ECE Claw \u8FDC\u7A0B\u542F\u52A8 OpenCode\uFF1B\u5DF2\u6709\u670D\u52A1\u4F1A\u76F4\u63A5\u8FDE\u63A5\u3002");
|
|
530
528
|
baseUrl = await ask(rl, "OpenCode \u670D\u52A1\u5730\u5740", { default: baseUrl || "http://127.0.0.1:4096", validate: (value) => {
|
|
531
529
|
try {
|
|
532
530
|
const url = new URL(value);
|
|
@@ -627,7 +625,7 @@ async function runOnboard(opts) {
|
|
|
627
625
|
stateDirectory,
|
|
628
626
|
...loopbackHttp ? { allowInsecureLocalhost: true } : {},
|
|
629
627
|
projects: projects.map((project) => ({ id: project.id, name: project.name, directory: project.directory, specDirectories: project.specDirectories ?? ["specs", "docs"] })),
|
|
630
|
-
tool: { kind: toolKind, command: toolCommand || describeTool(toolKind).defaultCommand, ...toolKind === "opencode" ? { baseUrl, mode: "external" } : {}, ...toolKind === existing?.tool.kind && existing.tool.password ? { password: existing.tool.password } : {},
|
|
628
|
+
tool: { kind: toolKind, command: toolCommand || describeTool(toolKind).defaultCommand, ...toolKind === "opencode" ? { baseUrl, mode: "external" } : {}, ...toolKind === existing?.tool.kind && existing.tool.password ? { password: existing.tool.password } : {}, autoStart: toolKind === existing?.tool.kind ? existing.tool.autoStart : toolKind !== "opencode" }
|
|
631
629
|
});
|
|
632
630
|
await loadConfig(opts.configPath);
|
|
633
631
|
console.log(`\u5DF2\u5199\u5165\u914D\u7F6E\uFF1A${opts.configPath}\uFF080600\uFF09`);
|
|
@@ -796,8 +794,7 @@ async function showLogs(follow) {
|
|
|
796
794
|
}
|
|
797
795
|
var { command, flags, projectArgs } = parse(process.argv.slice(2));
|
|
798
796
|
if (command === "version" || flags["--version"]) {
|
|
799
|
-
|
|
800
|
-
console.log(packageJson.version);
|
|
797
|
+
console.log(proxyVersion);
|
|
801
798
|
process.exit(0);
|
|
802
799
|
}
|
|
803
800
|
if (!command || command === "help" || flags["--help"]) {
|
|
@@ -840,6 +837,7 @@ try {
|
|
|
840
837
|
console.log(`node\uFF1A${outcome.nodePath}`);
|
|
841
838
|
if (outcome.warning) console.log(`\u6CE8\u610F\uFF1A${outcome.warning}`);
|
|
842
839
|
console.log(`\u65E5\u5FD7\uFF1A${logLocationHint()}\uFF08octopus-proxy logs \u67E5\u770B\uFF09`);
|
|
840
|
+
console.log(`\u5DF2\u5B89\u88C5\u7248\u672C\uFF1A${proxyVersion}`);
|
|
843
841
|
break;
|
|
844
842
|
}
|
|
845
843
|
case "uninstall": {
|
package/dist/main.js
CHANGED