@skylandnpm/octopus-proxy 0.1.27 → 0.1.29
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 +1 -1
- package/dist/{chunk-X2VENWTX.js → chunk-XHNRMXZT.js} +48 -7
- package/dist/cli.js +1 -1
- package/dist/main.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ octopus-proxy onboard
|
|
|
15
15
|
|
|
16
16
|
向导默认使用 `https://octopus.wuyehao.com` 作为 Hub 地址;按需可输入其他地址,或用 `--hub-url` 指定。再次运行向导时会读取现有配置,直接回车即可保留各项设置;机器人 Secret 只提示已配置,不在终端显示原值。macOS 上选择编码工具时,向导优先显示已配置或探测到的命令路径,否则给出该工具的 macOS 路径示例;回车后会检查文件可执行性并运行版本命令,失败时要求重新输入。无交互环境可使用 `octopus-proxy onboard --non-interactive`,未提供 `--hub-url` 时也使用该默认地址;完整选项见 `octopus-proxy help`。安装服务后可运行 `octopus-proxy status` 和 `octopus-proxy doctor` 检查状态。
|
|
17
17
|
|
|
18
|
-
Proxy 始终使用独立的控制与文件 WebSocket;文件读取和上传不会回退到控制通道。两个通道会同时连接并各自重连,Hub 只会把文件命令发送到同一 Proxy
|
|
18
|
+
Proxy 始终使用独立的控制与文件 WebSocket;文件读取和上传不会回退到控制通道。两个通道会同时连接并各自重连,Hub 只会把文件命令发送到同一 Proxy 实例的已就绪文件通道。文件连接同时携带经过 Proxy 凭据认证的通道标识,因此即使反向代理将 `/ws/proxy-files` 重写为上游 `/ws/proxy`,Hub 仍能正确路由文件命令;反向代理仍需转发 WebSocket Upgrade 与 `X-Octopus-Proxy-Channel` 请求头。若旧 Hub 无法识别该标识,Proxy 会保留控制通道以免影响对话,同时在日志中提示升级 Hub 路由。同一套 bot ID 和 Secret 被另一台 Proxy 主机连接后,Hub 会让新实例接管,旧实例会同时关闭两个通道并停止自动重连;若要让旧实例重新接管,必须显式重启其服务。
|
|
19
19
|
|
|
20
20
|
添加授权项目时,填写项目 ID 后会询问是否继续添加;直接回车即可结束添加并继续保存配置。
|
|
21
21
|
|
|
@@ -16630,6 +16630,29 @@ function createAdapter(runtime, workspace, uploads, emit) {
|
|
|
16630
16630
|
return new OpenCodeAdapter(runtime, workspace, uploads, forward);
|
|
16631
16631
|
}
|
|
16632
16632
|
|
|
16633
|
+
// src/hub-channel-compatibility.ts
|
|
16634
|
+
var HubChannelCompatibility = class {
|
|
16635
|
+
enabled = false;
|
|
16636
|
+
filesAttemptedAt = 0;
|
|
16637
|
+
filesFailures = 0;
|
|
16638
|
+
filesAttempt(now = Date.now()) {
|
|
16639
|
+
this.filesAttemptedAt = now;
|
|
16640
|
+
}
|
|
16641
|
+
filesWelcome() {
|
|
16642
|
+
this.filesFailures = 0;
|
|
16643
|
+
}
|
|
16644
|
+
controlClosed(filesPending, now = Date.now()) {
|
|
16645
|
+
if (this.enabled || !filesPending || now - this.filesAttemptedAt >= 5e3) return false;
|
|
16646
|
+
this.enabled = true;
|
|
16647
|
+
return true;
|
|
16648
|
+
}
|
|
16649
|
+
filesClosedBeforeWelcome(stopping) {
|
|
16650
|
+
if (this.enabled || stopping || ++this.filesFailures < 3) return false;
|
|
16651
|
+
this.enabled = true;
|
|
16652
|
+
return true;
|
|
16653
|
+
}
|
|
16654
|
+
};
|
|
16655
|
+
|
|
16633
16656
|
// src/proxy-binding.ts
|
|
16634
16657
|
var PROXY_REPLACED_CLOSE_CODE = 4009;
|
|
16635
16658
|
var ProxyBinding = class {
|
|
@@ -16779,6 +16802,7 @@ async function runDaemon(configPath) {
|
|
|
16779
16802
|
const workspace = new Workspace(config.projects), runtime = createRuntime(config), uploads = new Uploads(workspace, join10(config.stateDirectory, "uploads")), journal = new Journal(join10(config.stateDirectory, "journal"));
|
|
16780
16803
|
const epoch = randomUUID12();
|
|
16781
16804
|
let stopping = false, botId = "";
|
|
16805
|
+
const hubChannels = new HubChannelCompatibility();
|
|
16782
16806
|
const binding = new ProxyBinding();
|
|
16783
16807
|
const sockets = /* @__PURE__ */ new Set();
|
|
16784
16808
|
let controlSocket, filesSocket;
|
|
@@ -16847,21 +16871,29 @@ async function runDaemon(configPath) {
|
|
|
16847
16871
|
return adapter.dispatch(c);
|
|
16848
16872
|
}
|
|
16849
16873
|
function activateChannels() {
|
|
16850
|
-
if (channelsActivated || !controlWelcome || !filesWelcomed || controlWelcome.socket.readyState !== WebSocket2.OPEN) return;
|
|
16874
|
+
if (channelsActivated || !controlWelcome || !filesWelcomed && !hubChannels.enabled || controlWelcome.socket.readyState !== WebSocket2.OPEN) return;
|
|
16851
16875
|
channelsActivated = true;
|
|
16852
16876
|
events.bind(controlWelcome.socket, controlWelcome.resume);
|
|
16853
16877
|
for (const p of config.projects) emit(p.id, "octopus.resync", {});
|
|
16854
16878
|
}
|
|
16855
16879
|
function connect(files, attempt = 0) {
|
|
16856
|
-
if (stopping || !binding.active) return;
|
|
16880
|
+
if (stopping || !binding.active || files && hubChannels.enabled) return;
|
|
16857
16881
|
const url = new URL(files ? "/ws/proxy-files" : "/ws/proxy", config.hubUrl);
|
|
16858
16882
|
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
16859
|
-
const headers = {
|
|
16883
|
+
const headers = {
|
|
16884
|
+
Authorization: `SecretKey ${config.secretKey}`,
|
|
16885
|
+
"X-Octopus-Bot-Id": config.botId,
|
|
16886
|
+
"X-Octopus-Proxy-Instance-Id": epoch,
|
|
16887
|
+
// Preserve the logical channel when a reverse proxy rewrites
|
|
16888
|
+
// /ws/proxy-files to the generic /ws/proxy upstream endpoint.
|
|
16889
|
+
"X-Octopus-Proxy-Channel": files ? "files" : "control"
|
|
16890
|
+
};
|
|
16860
16891
|
const socket = new WebSocket2(url, { headers, maxPayload: 3 * 1024 * 1024 });
|
|
16861
16892
|
sockets.add(socket);
|
|
16862
16893
|
if (files) {
|
|
16863
16894
|
filesSocket = socket;
|
|
16864
16895
|
filesWelcomed = false;
|
|
16896
|
+
hubChannels.filesAttempt();
|
|
16865
16897
|
} else controlSocket = socket;
|
|
16866
16898
|
let lastPong = Date.now();
|
|
16867
16899
|
const heartbeat = setInterval(() => {
|
|
@@ -16886,8 +16918,10 @@ async function runDaemon(configPath) {
|
|
|
16886
16918
|
if (value.type === "welcome") {
|
|
16887
16919
|
botId = value.botId;
|
|
16888
16920
|
uploads.limits = value.limits;
|
|
16889
|
-
if (files)
|
|
16890
|
-
|
|
16921
|
+
if (files) {
|
|
16922
|
+
filesWelcomed = true;
|
|
16923
|
+
hubChannels.filesWelcome();
|
|
16924
|
+
} else controlWelcome = { socket, resume: value.resume };
|
|
16891
16925
|
activateChannels();
|
|
16892
16926
|
return;
|
|
16893
16927
|
}
|
|
@@ -16922,10 +16956,17 @@ async function runDaemon(configPath) {
|
|
|
16922
16956
|
console.error("This bot was claimed by another Proxy instance; automatic reconnection is disabled until this service is restarted");
|
|
16923
16957
|
for (const peer of sockets) peer.close();
|
|
16924
16958
|
}
|
|
16925
|
-
|
|
16959
|
+
const collision = binding.active && !files && hubChannels.controlClosed(filesSocket !== void 0 && filesSocket.readyState !== WebSocket2.CLOSED);
|
|
16960
|
+
const rejectedFilesChannel = binding.active && files && !filesWelcomed && hubChannels.filesClosedBeforeWelcome(stopping);
|
|
16961
|
+
if (collision || rejectedFilesChannel) {
|
|
16962
|
+
console.warn("Hub does not preserve the Proxy files channel; keeping the control channel online until Hub routing is upgraded");
|
|
16963
|
+
filesSocket?.close();
|
|
16964
|
+
filesSocket = void 0;
|
|
16965
|
+
activateChannels();
|
|
16966
|
+
} else if (!stopping && binding.active && code !== 1e3) {
|
|
16926
16967
|
console.warn(`Hub ${files ? "files" : "control"} connection closed: ${code}${reason.length ? ` ${reason.toString()}` : ""}`);
|
|
16927
16968
|
}
|
|
16928
|
-
if (!stopping && binding.active) setTimeout(() => connect(files, attempt + 1), Math.min(3e4, 1e3 * 2 ** Math.min(attempt, 5)) + Math.random() * 500);
|
|
16969
|
+
if (!stopping && binding.active && (!files || !hubChannels.enabled)) setTimeout(() => connect(files, attempt + 1), collision ? 100 : Math.min(3e4, 1e3 * 2 ** Math.min(attempt, 5)) + Math.random() * 500);
|
|
16929
16970
|
});
|
|
16930
16971
|
}
|
|
16931
16972
|
await runtime.init().catch((error) => console.error(`${runtime.kind} startup:`, error.message));
|
package/dist/cli.js
CHANGED
package/dist/main.js
CHANGED