@skylandnpm/octopus-proxy 0.1.26 → 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-3IEPWZVZ.js → chunk-XHNRMXZT.js} +233 -19
- 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
|
|
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
|
|
|
@@ -11611,13 +11611,19 @@ var ToolSchema = z.object({
|
|
|
11611
11611
|
if (tool.kind === "opencode" && tool.mode === "managed") context.addIssue({ code: z.ZodIssueCode.custom, path: ["mode"], message: "OpenCode v2 \u4EC5\u652F\u6301 external \u6A21\u5F0F" });
|
|
11612
11612
|
if (tool.kind !== "opencode" && tool.mode === "external") context.addIssue({ code: z.ZodIssueCode.custom, path: ["mode"], message: "external mode is only supported for opencode" });
|
|
11613
11613
|
}).transform((tool) => ({ ...tool, mode: tool.kind === "opencode" ? "external" : "managed", autoStart: tool.autoStart ?? true }));
|
|
11614
|
+
var ProjectSchema = z.object({
|
|
11615
|
+
id: z.string().regex(/^[a-zA-Z0-9_-]+$/),
|
|
11616
|
+
name: z.string().min(1),
|
|
11617
|
+
directory: z.string().min(1),
|
|
11618
|
+
specDirectories: z.array(z.string()).default(["specs", "docs"])
|
|
11619
|
+
});
|
|
11614
11620
|
var ConfigSchema = z.object({
|
|
11615
11621
|
hubUrl: z.string().url(),
|
|
11616
11622
|
botId: z.string().uuid(),
|
|
11617
11623
|
secretKey: z.string().min(20),
|
|
11618
11624
|
stateDirectory: z.string().min(1),
|
|
11619
11625
|
allowInsecureLocalhost: z.boolean().default(false),
|
|
11620
|
-
projects: z.array(
|
|
11626
|
+
projects: z.array(ProjectSchema).min(1),
|
|
11621
11627
|
tool: ToolSchema.default({ kind: "opencode" })
|
|
11622
11628
|
});
|
|
11623
11629
|
async function loadConfig(path, options = {}) {
|
|
@@ -11682,6 +11688,9 @@ var Operations = [
|
|
|
11682
11688
|
"runtime.restart",
|
|
11683
11689
|
"runtime.logs",
|
|
11684
11690
|
"workspace.projects",
|
|
11691
|
+
"workspace.projects.add",
|
|
11692
|
+
"workspace.projects.update",
|
|
11693
|
+
"workspace.projects.remove",
|
|
11685
11694
|
"workspace.list",
|
|
11686
11695
|
"workspace.read",
|
|
11687
11696
|
"workspace.search",
|
|
@@ -14543,6 +14552,16 @@ var OpenCodeAdapter = class {
|
|
|
14543
14552
|
};
|
|
14544
14553
|
void loop();
|
|
14545
14554
|
}
|
|
14555
|
+
removeProject(project) {
|
|
14556
|
+
this.streams.get(project)?.abort();
|
|
14557
|
+
this.streams.delete(project);
|
|
14558
|
+
for (const [key, terminal] of this.terminals) {
|
|
14559
|
+
if (!key.startsWith(`${project}:`)) continue;
|
|
14560
|
+
terminal.close();
|
|
14561
|
+
this.terminals.delete(key);
|
|
14562
|
+
this.terminalOutput.delete(key);
|
|
14563
|
+
}
|
|
14564
|
+
}
|
|
14546
14565
|
async terminal(project, op, p) {
|
|
14547
14566
|
const id = this.identifier(p.id), key = `${project}:${id}`;
|
|
14548
14567
|
await this.data(project, "GET", `/api/pty/${id}`);
|
|
@@ -15125,6 +15144,24 @@ var StdioRuntime = class {
|
|
|
15125
15144
|
this.refreshing.delete(projectId);
|
|
15126
15145
|
}
|
|
15127
15146
|
}
|
|
15147
|
+
async addProject(projectId) {
|
|
15148
|
+
if (!this.wanted) return;
|
|
15149
|
+
const project = this.config.projects.find((entry) => entry.id === projectId);
|
|
15150
|
+
if (project) await this.track(project.id, project.directory);
|
|
15151
|
+
}
|
|
15152
|
+
async removeProject(projectId) {
|
|
15153
|
+
await this.tracking.get(projectId)?.catch(() => {
|
|
15154
|
+
});
|
|
15155
|
+
this.refreshing.add(projectId);
|
|
15156
|
+
try {
|
|
15157
|
+
this.kill(projectId);
|
|
15158
|
+
} finally {
|
|
15159
|
+
this.refreshing.delete(projectId);
|
|
15160
|
+
this.failures.delete(projectId);
|
|
15161
|
+
this.attempts.delete(projectId);
|
|
15162
|
+
this.logPaths.delete(projectId);
|
|
15163
|
+
}
|
|
15164
|
+
}
|
|
15128
15165
|
async logs() {
|
|
15129
15166
|
const chunks = [];
|
|
15130
15167
|
for (const [projectId, logPath] of this.logPaths) {
|
|
@@ -15296,6 +15333,9 @@ var DAEMON_OPERATIONS = [
|
|
|
15296
15333
|
"runtime.restart",
|
|
15297
15334
|
"runtime.logs",
|
|
15298
15335
|
"workspace.projects",
|
|
15336
|
+
"workspace.projects.add",
|
|
15337
|
+
"workspace.projects.update",
|
|
15338
|
+
"workspace.projects.remove",
|
|
15299
15339
|
"workspace.list",
|
|
15300
15340
|
"workspace.read",
|
|
15301
15341
|
"workspace.search",
|
|
@@ -16453,6 +16493,14 @@ var PiRuntime = class {
|
|
|
16453
16493
|
}
|
|
16454
16494
|
detach() {
|
|
16455
16495
|
}
|
|
16496
|
+
async addProject(projectId) {
|
|
16497
|
+
if (this.wanted) await this.connect(projectId);
|
|
16498
|
+
}
|
|
16499
|
+
async removeProject(projectId) {
|
|
16500
|
+
const prefix = `${projectId}:`;
|
|
16501
|
+
await Promise.allSettled([...this.launching].filter(([key]) => key.startsWith(prefix)).map(([, launch]) => launch));
|
|
16502
|
+
await Promise.all([...this.processes].filter(([, entry]) => entry.projectId === projectId).map(([key]) => this.kill(key)));
|
|
16503
|
+
}
|
|
16456
16504
|
async status() {
|
|
16457
16505
|
const children = [...this.processes.values()].map(({ child, rpc, projectId, sessionId }) => ({
|
|
16458
16506
|
projectId,
|
|
@@ -16620,17 +16668,146 @@ var ProxyBinding = class {
|
|
|
16620
16668
|
}
|
|
16621
16669
|
};
|
|
16622
16670
|
|
|
16671
|
+
// src/project-registry.ts
|
|
16672
|
+
import { realpath as realpath6, readFile as readFile7, stat as stat5 } from "node:fs/promises";
|
|
16673
|
+
import { isAbsolute as isAbsolute4 } from "node:path";
|
|
16674
|
+
var ProjectMutationSchema = ProjectSchema.extend({
|
|
16675
|
+
name: ProjectSchema.shape.name.trim().max(120),
|
|
16676
|
+
specDirectories: ProjectSchema.shape.specDirectories.removeDefault().element.min(1).array().max(20).default(["specs", "docs"])
|
|
16677
|
+
}).strict();
|
|
16678
|
+
var ProjectUpdateSchema = ProjectMutationSchema.omit({ specDirectories: true });
|
|
16679
|
+
function rawConfig(value) {
|
|
16680
|
+
if (typeof value !== "object" || value === null || Array.isArray(value) || !Array.isArray(value.projects)) {
|
|
16681
|
+
throw new Error("Proxy \u914D\u7F6E\u6587\u4EF6\u7F3A\u5C11 projects \u6570\u7EC4");
|
|
16682
|
+
}
|
|
16683
|
+
return value;
|
|
16684
|
+
}
|
|
16685
|
+
function rawProjectId(value) {
|
|
16686
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.id === "string" ? value.id : void 0;
|
|
16687
|
+
}
|
|
16688
|
+
var ProjectRegistry = class {
|
|
16689
|
+
constructor(configPath, config, runtime, adapter) {
|
|
16690
|
+
this.configPath = configPath;
|
|
16691
|
+
this.config = config;
|
|
16692
|
+
this.runtime = runtime;
|
|
16693
|
+
this.adapter = adapter;
|
|
16694
|
+
}
|
|
16695
|
+
pending = Promise.resolve();
|
|
16696
|
+
add(payload) {
|
|
16697
|
+
return this.serial(() => this.addUnlocked(payload));
|
|
16698
|
+
}
|
|
16699
|
+
remove(payload) {
|
|
16700
|
+
return this.serial(() => this.removeUnlocked(payload));
|
|
16701
|
+
}
|
|
16702
|
+
update(payload) {
|
|
16703
|
+
return this.serial(() => this.updateUnlocked(payload));
|
|
16704
|
+
}
|
|
16705
|
+
serial(work) {
|
|
16706
|
+
const result = this.pending.catch(() => {
|
|
16707
|
+
}).then(work);
|
|
16708
|
+
this.pending = result;
|
|
16709
|
+
return result;
|
|
16710
|
+
}
|
|
16711
|
+
async bestEffort(work) {
|
|
16712
|
+
try {
|
|
16713
|
+
await work();
|
|
16714
|
+
} catch {
|
|
16715
|
+
}
|
|
16716
|
+
}
|
|
16717
|
+
async readCurrent() {
|
|
16718
|
+
const current = rawConfig(JSON.parse(await readFile7(this.configPath, "utf8")));
|
|
16719
|
+
const diskProjects = current.projects.map((value) => {
|
|
16720
|
+
const project = ProjectSchema.parse(value);
|
|
16721
|
+
return { ...project, directory: expandHome(project.directory) };
|
|
16722
|
+
});
|
|
16723
|
+
if (JSON.stringify(diskProjects) !== JSON.stringify(this.config.projects)) {
|
|
16724
|
+
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");
|
|
16725
|
+
}
|
|
16726
|
+
return current;
|
|
16727
|
+
}
|
|
16728
|
+
async addUnlocked(payload) {
|
|
16729
|
+
const parsed = ProjectMutationSchema.parse(payload);
|
|
16730
|
+
const directory = expandHome(parsed.directory);
|
|
16731
|
+
if (!isAbsolute4(directory)) throw new Error("\u9879\u76EE\u76EE\u5F55\u5FC5\u987B\u662F Proxy \u4E3B\u673A\u4E0A\u7684\u7EDD\u5BF9\u8DEF\u5F84");
|
|
16732
|
+
const info = await stat5(directory).catch(() => void 0);
|
|
16733
|
+
if (!info?.isDirectory()) throw new Error(`\u9879\u76EE\u76EE\u5F55\u4E0D\u5B58\u5728\u6216\u4E0D\u662F\u76EE\u5F55\uFF1A${directory}`);
|
|
16734
|
+
const canonical = await realpath6(directory);
|
|
16735
|
+
if (this.config.projects.some((project2) => project2.id === parsed.id)) throw new Error(`\u9879\u76EE ID \u5DF2\u5B58\u5728\uFF1A${parsed.id}`);
|
|
16736
|
+
for (const project2 of this.config.projects) {
|
|
16737
|
+
if (await realpath6(project2.directory).catch(() => project2.directory) === canonical) throw new Error("\u8BE5\u76EE\u5F55\u5DF2\u7ECF\u662F\u6388\u6743\u9879\u76EE");
|
|
16738
|
+
}
|
|
16739
|
+
const project = { ...parsed, directory: canonical };
|
|
16740
|
+
const raw = await this.readCurrent();
|
|
16741
|
+
await atomicJson(this.configPath, { ...raw, projects: [...raw.projects, project] });
|
|
16742
|
+
this.config.projects.push(project);
|
|
16743
|
+
await this.bestEffort(() => this.runtime.addProject?.(project.id));
|
|
16744
|
+
return publicProjects(this.config);
|
|
16745
|
+
}
|
|
16746
|
+
async updateUnlocked(payload) {
|
|
16747
|
+
const parsed = ProjectUpdateSchema.parse(payload);
|
|
16748
|
+
const index = this.config.projects.findIndex((project) => project.id === parsed.id);
|
|
16749
|
+
if (index < 0) throw new Error(`\u672A\u77E5\u6388\u6743\u9879\u76EE\uFF1A${parsed.id}`);
|
|
16750
|
+
const directory = expandHome(parsed.directory);
|
|
16751
|
+
if (!isAbsolute4(directory)) throw new Error("\u9879\u76EE\u76EE\u5F55\u5FC5\u987B\u662F Proxy \u4E3B\u673A\u4E0A\u7684\u7EDD\u5BF9\u8DEF\u5F84");
|
|
16752
|
+
const info = await stat5(directory).catch(() => void 0);
|
|
16753
|
+
if (!info?.isDirectory()) throw new Error(`\u9879\u76EE\u76EE\u5F55\u4E0D\u5B58\u5728\u6216\u4E0D\u662F\u76EE\u5F55\uFF1A${directory}`);
|
|
16754
|
+
const canonical = await realpath6(directory);
|
|
16755
|
+
for (const project of this.config.projects) {
|
|
16756
|
+
if (project.id !== parsed.id && await realpath6(project.directory).catch(() => project.directory) === canonical) {
|
|
16757
|
+
throw new Error("\u8BE5\u76EE\u5F55\u5DF2\u7ECF\u662F\u6388\u6743\u9879\u76EE");
|
|
16758
|
+
}
|
|
16759
|
+
}
|
|
16760
|
+
const current = this.config.projects[index];
|
|
16761
|
+
const oldDirectory = await realpath6(current.directory).catch(() => current.directory);
|
|
16762
|
+
const directoryChanged = oldDirectory !== canonical;
|
|
16763
|
+
const raw = await this.readCurrent();
|
|
16764
|
+
await atomicJson(this.configPath, {
|
|
16765
|
+
...raw,
|
|
16766
|
+
projects: raw.projects.map((project) => rawProjectId(project) === parsed.id ? { ...project, name: parsed.name, directory: canonical } : project)
|
|
16767
|
+
});
|
|
16768
|
+
if (directoryChanged) {
|
|
16769
|
+
await Promise.all([
|
|
16770
|
+
this.bestEffort(() => this.adapter.removeProject?.(parsed.id)),
|
|
16771
|
+
this.bestEffort(() => this.runtime.removeProject?.(parsed.id))
|
|
16772
|
+
]);
|
|
16773
|
+
}
|
|
16774
|
+
this.config.projects[index] = { ...current, name: parsed.name, directory: canonical };
|
|
16775
|
+
if (directoryChanged) await this.bestEffort(() => this.runtime.addProject?.(parsed.id));
|
|
16776
|
+
return publicProjects(this.config);
|
|
16777
|
+
}
|
|
16778
|
+
async removeUnlocked(payload) {
|
|
16779
|
+
const keys = Object.keys(payload);
|
|
16780
|
+
if (keys.length !== 1 || keys[0] !== "id" || typeof payload.id !== "string" || !/^[a-zA-Z0-9_-]+$/.test(payload.id)) {
|
|
16781
|
+
throw new Error("\u5220\u9664\u9879\u76EE\u9700\u8981\u5408\u6CD5\u7684\u9879\u76EE ID");
|
|
16782
|
+
}
|
|
16783
|
+
const id = payload.id;
|
|
16784
|
+
if (this.config.projects.length === 1) throw new Error("\u81F3\u5C11\u9700\u8981\u4FDD\u7559\u4E00\u4E2A\u6388\u6743\u9879\u76EE");
|
|
16785
|
+
const index = this.config.projects.findIndex((project) => project.id === id);
|
|
16786
|
+
if (index < 0) throw new Error(`\u672A\u77E5\u6388\u6743\u9879\u76EE\uFF1A${id}`);
|
|
16787
|
+
const raw = await this.readCurrent();
|
|
16788
|
+
await atomicJson(this.configPath, { ...raw, projects: raw.projects.filter((project) => rawProjectId(project) !== id) });
|
|
16789
|
+
await Promise.all([
|
|
16790
|
+
this.bestEffort(() => this.adapter.removeProject?.(id)),
|
|
16791
|
+
this.bestEffort(() => this.runtime.removeProject?.(id))
|
|
16792
|
+
]);
|
|
16793
|
+
this.config.projects.splice(index, 1);
|
|
16794
|
+
return publicProjects(this.config);
|
|
16795
|
+
}
|
|
16796
|
+
};
|
|
16797
|
+
|
|
16623
16798
|
// src/daemon.ts
|
|
16624
16799
|
async function runDaemon(configPath) {
|
|
16625
16800
|
const config = await loadConfig(configPath);
|
|
16626
16801
|
await mkdir7(config.stateDirectory, { recursive: true, mode: 448 });
|
|
16627
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"));
|
|
16628
16803
|
const epoch = randomUUID12();
|
|
16629
|
-
let stopping = false, botId = ""
|
|
16804
|
+
let stopping = false, botId = "";
|
|
16630
16805
|
const hubChannels = new HubChannelCompatibility();
|
|
16631
16806
|
const binding = new ProxyBinding();
|
|
16632
16807
|
const sockets = /* @__PURE__ */ new Set();
|
|
16633
|
-
let filesSocket;
|
|
16808
|
+
let controlSocket, filesSocket;
|
|
16809
|
+
let controlWelcome;
|
|
16810
|
+
let filesWelcomed = false, channelsActivated = false;
|
|
16634
16811
|
const events = new EventStream(epoch, () => {
|
|
16635
16812
|
for (const p of config.projects) emit(p.id, "octopus.resync", {});
|
|
16636
16813
|
});
|
|
@@ -16638,6 +16815,7 @@ async function runDaemon(configPath) {
|
|
|
16638
16815
|
events.publish({ type: "event", botId, projectId, event, data });
|
|
16639
16816
|
}
|
|
16640
16817
|
const adapter = createAdapter(runtime, workspace, uploads, emit), artifacts = new Artifacts(workspace, join10(config.stateDirectory, "artifacts"), emit);
|
|
16818
|
+
const projectRegistry = new ProjectRegistry(configPath, config, runtime, adapter);
|
|
16641
16819
|
async function dispatch(c) {
|
|
16642
16820
|
if (!adapter.supportedOperations().includes(c.operation)) throw new Error(`Unsupported ${runtime.kind} operation: ${c.operation}`);
|
|
16643
16821
|
const p = c.payload;
|
|
@@ -16648,6 +16826,21 @@ async function runDaemon(configPath) {
|
|
|
16648
16826
|
if (c.operation === "runtime.stop") return runtime.stop();
|
|
16649
16827
|
if (c.operation === "runtime.restart") return runtime.restart();
|
|
16650
16828
|
if (c.operation === "workspace.projects") return publicProjects(config);
|
|
16829
|
+
if (c.operation === "workspace.projects.add") {
|
|
16830
|
+
const result = await projectRegistry.add(p);
|
|
16831
|
+
emit(String(p.id), "octopus.resync", {});
|
|
16832
|
+
return result;
|
|
16833
|
+
}
|
|
16834
|
+
if (c.operation === "workspace.projects.update") {
|
|
16835
|
+
const result = await projectRegistry.update(p);
|
|
16836
|
+
emit(String(p.id), "octopus.resync", {});
|
|
16837
|
+
return result;
|
|
16838
|
+
}
|
|
16839
|
+
if (c.operation === "workspace.projects.remove") {
|
|
16840
|
+
const result = await projectRegistry.remove(p);
|
|
16841
|
+
emit(String(p.id), "octopus.resync", {});
|
|
16842
|
+
return result;
|
|
16843
|
+
}
|
|
16651
16844
|
const project = c.projectId;
|
|
16652
16845
|
if (!project) throw new Error("Project required");
|
|
16653
16846
|
workspace.project(project);
|
|
@@ -16677,18 +16870,32 @@ async function runDaemon(configPath) {
|
|
|
16677
16870
|
}
|
|
16678
16871
|
return adapter.dispatch(c);
|
|
16679
16872
|
}
|
|
16873
|
+
function activateChannels() {
|
|
16874
|
+
if (channelsActivated || !controlWelcome || !filesWelcomed && !hubChannels.enabled || controlWelcome.socket.readyState !== WebSocket2.OPEN) return;
|
|
16875
|
+
channelsActivated = true;
|
|
16876
|
+
events.bind(controlWelcome.socket, controlWelcome.resume);
|
|
16877
|
+
for (const p of config.projects) emit(p.id, "octopus.resync", {});
|
|
16878
|
+
}
|
|
16680
16879
|
function connect(files, attempt = 0) {
|
|
16681
16880
|
if (stopping || !binding.active || files && hubChannels.enabled) return;
|
|
16682
16881
|
const url = new URL(files ? "/ws/proxy-files" : "/ws/proxy", config.hubUrl);
|
|
16683
16882
|
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
16684
|
-
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
|
+
};
|
|
16685
16891
|
const socket = new WebSocket2(url, { headers, maxPayload: 3 * 1024 * 1024 });
|
|
16686
16892
|
sockets.add(socket);
|
|
16687
16893
|
if (files) {
|
|
16688
16894
|
filesSocket = socket;
|
|
16895
|
+
filesWelcomed = false;
|
|
16689
16896
|
hubChannels.filesAttempt();
|
|
16690
|
-
}
|
|
16691
|
-
let lastPong = Date.now()
|
|
16897
|
+
} else controlSocket = socket;
|
|
16898
|
+
let lastPong = Date.now();
|
|
16692
16899
|
const heartbeat = setInterval(() => {
|
|
16693
16900
|
if (Date.now() - lastPong > 45e3) socket.terminate();
|
|
16694
16901
|
else if (socket.readyState === WebSocket2.OPEN) socket.send(JSON.stringify({ type: "ping", proxyVersion }));
|
|
@@ -16709,18 +16916,13 @@ async function runDaemon(configPath) {
|
|
|
16709
16916
|
return;
|
|
16710
16917
|
}
|
|
16711
16918
|
if (value.type === "welcome") {
|
|
16712
|
-
welcomed = true;
|
|
16713
|
-
if (files) hubChannels.filesWelcome();
|
|
16714
16919
|
botId = value.botId;
|
|
16715
16920
|
uploads.limits = value.limits;
|
|
16716
|
-
if (
|
|
16717
|
-
|
|
16718
|
-
|
|
16719
|
-
|
|
16720
|
-
|
|
16721
|
-
connect(true);
|
|
16722
|
-
}
|
|
16723
|
-
}
|
|
16921
|
+
if (files) {
|
|
16922
|
+
filesWelcomed = true;
|
|
16923
|
+
hubChannels.filesWelcome();
|
|
16924
|
+
} else controlWelcome = { socket, resume: value.resume };
|
|
16925
|
+
activateChannels();
|
|
16724
16926
|
return;
|
|
16725
16927
|
}
|
|
16726
16928
|
const command = CommandSchema.parse(value);
|
|
@@ -16738,18 +16940,29 @@ async function runDaemon(configPath) {
|
|
|
16738
16940
|
sockets.delete(socket);
|
|
16739
16941
|
clearInterval(heartbeat);
|
|
16740
16942
|
events.unbind(socket);
|
|
16741
|
-
if (files && filesSocket === socket)
|
|
16943
|
+
if (files && filesSocket === socket) {
|
|
16944
|
+
filesSocket = void 0;
|
|
16945
|
+
filesWelcomed = false;
|
|
16946
|
+
channelsActivated = false;
|
|
16947
|
+
events.unbind();
|
|
16948
|
+
}
|
|
16949
|
+
if (!files && controlSocket === socket) {
|
|
16950
|
+
controlSocket = void 0;
|
|
16951
|
+
controlWelcome = void 0;
|
|
16952
|
+
channelsActivated = false;
|
|
16953
|
+
}
|
|
16742
16954
|
const replaced = binding.observeClose(code);
|
|
16743
16955
|
if (replaced) {
|
|
16744
16956
|
console.error("This bot was claimed by another Proxy instance; automatic reconnection is disabled until this service is restarted");
|
|
16745
16957
|
for (const peer of sockets) peer.close();
|
|
16746
16958
|
}
|
|
16747
16959
|
const collision = binding.active && !files && hubChannels.controlClosed(filesSocket !== void 0 && filesSocket.readyState !== WebSocket2.CLOSED);
|
|
16748
|
-
const rejectedFilesChannel = binding.active && files && !
|
|
16960
|
+
const rejectedFilesChannel = binding.active && files && !filesWelcomed && hubChannels.filesClosedBeforeWelcome(stopping);
|
|
16749
16961
|
if (collision || rejectedFilesChannel) {
|
|
16750
|
-
console.warn("Hub
|
|
16962
|
+
console.warn("Hub does not preserve the Proxy files channel; keeping the control channel online until Hub routing is upgraded");
|
|
16751
16963
|
filesSocket?.close();
|
|
16752
16964
|
filesSocket = void 0;
|
|
16965
|
+
activateChannels();
|
|
16753
16966
|
} else if (!stopping && binding.active && code !== 1e3) {
|
|
16754
16967
|
console.warn(`Hub ${files ? "files" : "control"} connection closed: ${code}${reason.length ? ` ${reason.toString()}` : ""}`);
|
|
16755
16968
|
}
|
|
@@ -16758,6 +16971,7 @@ async function runDaemon(configPath) {
|
|
|
16758
16971
|
}
|
|
16759
16972
|
await runtime.init().catch((error) => console.error(`${runtime.kind} startup:`, error.message));
|
|
16760
16973
|
connect(false);
|
|
16974
|
+
connect(true);
|
|
16761
16975
|
const scan = setInterval(async () => {
|
|
16762
16976
|
for (const p of config.projects) await artifacts.scan(p.id).catch(() => {
|
|
16763
16977
|
});
|
package/dist/cli.js
CHANGED
package/dist/main.js
CHANGED