@xopcai/xopc 0.0.20 → 0.0.22
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/dist/extensions/feishu/src/adapters/cli-login.d.ts +8 -0
- package/dist/extensions/feishu/src/adapters/cli-login.js +225 -0
- package/dist/extensions/feishu/src/adapters/cli-login.js.map +1 -0
- package/dist/extensions/feishu/src/adapters/onboard-cli.js +1 -105
- package/dist/extensions/feishu/src/adapters/onboard-cli.js.map +1 -1
- package/dist/extensions/feishu/src/auth/app-registration.d.ts +47 -0
- package/dist/extensions/feishu/src/auth/app-registration.js +122 -0
- package/dist/extensions/feishu/src/auth/app-registration.js.map +1 -0
- package/dist/extensions/feishu/src/plugin.d.ts +2 -0
- package/dist/extensions/feishu/src/plugin.js +2 -0
- package/dist/extensions/feishu/src/plugin.js.map +1 -1
- package/dist/extensions/telegram/src/inbound-processor.js +1 -1
- package/dist/extensions/telegram/src/plugin.d.ts +1 -1
- package/dist/extensions/telegram/src/plugin.js +1 -1
- package/dist/extensions/telegram/src/routing-integration.js +2 -2
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/extensions/weixin/src/plugin.js +1 -1
- package/dist/gateway/static/root/assets/{agents-DbLV2ldC.js → agents-BcLv59-r.js} +2 -2
- package/dist/gateway/static/root/assets/{agents-DbLV2ldC.js.map → agents-BcLv59-r.js.map} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-CDRSbv3l.js → apps-page-Bl-yxbQo.js} +2 -2
- package/dist/gateway/static/root/assets/{apps-page-CDRSbv3l.js.map → apps-page-Bl-yxbQo.js.map} +1 -1
- package/dist/gateway/static/root/assets/channels-settings-BGueHxMv.js +9 -0
- package/dist/gateway/static/root/assets/channels-settings-BGueHxMv.js.map +1 -0
- package/dist/gateway/static/root/assets/{cron-page-D-fhl446.js → cron-page-DsVZzPqv.js} +2 -2
- package/dist/gateway/static/root/assets/{cron-page-D-fhl446.js.map → cron-page-DsVZzPqv.js.map} +1 -1
- package/dist/gateway/static/root/assets/{cron-utils-DqyPqEDr.js → cron-utils-zbRs2yND.js} +2 -2
- package/dist/gateway/static/root/assets/{cron-utils-DqyPqEDr.js.map → cron-utils-zbRs2yND.js.map} +1 -1
- package/dist/gateway/static/root/assets/{dist-BTNDXpKu.js → dist-CDA7gR_M.js} +2 -2
- package/dist/gateway/static/root/assets/{dist-BTNDXpKu.js.map → dist-CDA7gR_M.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-CiOtMG3X.js → extension-debug-page-CDLp4DAs.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-debug-page-CiOtMG3X.js.map → extension-debug-page-CDLp4DAs.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-a59AFw7Q.js → extension-page-DwSCjzHO.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-page-a59AFw7Q.js.map → extension-page-DwSCjzHO.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-BQyLvxBY.js → extension-settings-page-Rdmxe24_.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-settings-page-BQyLvxBY.js.map → extension-settings-page-Rdmxe24_.js.map} +1 -1
- package/dist/gateway/static/root/assets/index-D9Wmfh2f.css +1 -0
- package/dist/gateway/static/root/assets/index-DG8WvMbu.js +150 -0
- package/dist/gateway/static/root/assets/index-DG8WvMbu.js.map +1 -0
- package/dist/gateway/static/root/assets/{logs-page-DMSWW0-k.js → logs-page-ChJ0nsPh.js} +2 -2
- package/dist/gateway/static/root/assets/{logs-page-DMSWW0-k.js.map → logs-page-ChJ0nsPh.js.map} +1 -1
- package/dist/gateway/static/root/assets/{sessions-page-CL2E3nPk.js → sessions-page-Cle4fPla.js} +2 -2
- package/dist/gateway/static/root/assets/{sessions-page-CL2E3nPk.js.map → sessions-page-Cle4fPla.js.map} +1 -1
- package/dist/gateway/static/root/assets/settings-page-Dyo2NYdy.js +2 -0
- package/dist/gateway/static/root/assets/settings-page-Dyo2NYdy.js.map +1 -0
- package/dist/gateway/static/root/assets/{skills-page-0rmNu4AL.js → skills-page-B-smhcB2.js} +2 -2
- package/dist/gateway/static/root/assets/{skills-page-0rmNu4AL.js.map → skills-page-B-smhcB2.js.map} +1 -1
- package/dist/gateway/static/root/index.html +2 -2
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.js +6 -6
- package/dist/src/agent/context/workspace-seed.js +1 -1
- package/dist/src/agent/ipc/bus.js +1 -1
- package/dist/src/agent/ipc/inbox.js +1 -1
- package/dist/src/agent/ipc/socket.js +1 -1
- package/dist/src/agent/memory/builtin-memory-store.d.ts +2 -1
- package/dist/src/agent/memory/builtin-memory-store.js +7 -6
- package/dist/src/agent/memory/builtin-memory-store.js.map +1 -1
- package/dist/src/agent/models/manager.js +1 -1
- package/dist/src/agent/prompt/memory/index.d.ts +4 -2
- package/dist/src/agent/prompt/memory/index.js +22 -10
- package/dist/src/agent/prompt/memory/index.js.map +1 -1
- package/dist/src/agent/prompt/service-prompt-builder.js +1 -1
- package/dist/src/agent/service.js +5 -5
- package/dist/src/agent/skills/index.js +1 -1
- package/dist/src/agent/skills/scanner.js +1 -1
- package/dist/src/agent/skills/skill-manage-ops.js +1 -1
- package/dist/src/agent/skills/skill-manager.js +1 -1
- package/dist/src/agent/tools/factory.js +10 -3
- package/dist/src/agent/tools/factory.js.map +1 -1
- package/dist/src/agent/tools/index.d.ts +1 -1
- package/dist/src/agent/tools/memory-tool.d.ts +7 -2
- package/dist/src/agent/tools/memory-tool.js +11 -5
- package/dist/src/agent/tools/memory-tool.js.map +1 -1
- package/dist/src/agent/tools/send-media.js +1 -1
- package/dist/src/agent/tools/skill-manage-tool.js +1 -1
- package/dist/src/agent/tools/write.js +1 -1
- package/dist/src/auth/credentials.js +2 -2
- package/dist/src/auth/sync-provider-auth.js +1 -1
- package/dist/src/channels/attachments/inbound-persist.js +1 -1
- package/dist/src/channels/attachments/outbound-tts-persist.js +1 -1
- package/dist/src/channels/registry.d.ts +1 -1
- package/dist/src/channels/registry.js +25 -1
- package/dist/src/channels/registry.js.map +1 -1
- package/dist/src/chat-commands/builtins/config.js +3 -3
- package/dist/src/chat-commands/builtins/session.js +1 -1
- package/dist/src/chat-commands/context.js +1 -1
- package/dist/src/chat-commands/index.js +1 -1
- package/dist/src/chat-commands/processor.js +1 -1
- package/dist/src/cli/commands/agent.js +1 -1
- package/dist/src/cli/commands/channels.js +20 -2
- package/dist/src/cli/commands/channels.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
- package/dist/src/cli/commands/gateway/call.d.ts +2 -0
- package/dist/src/cli/commands/gateway/call.js +90 -0
- package/dist/src/cli/commands/gateway/call.js.map +1 -0
- package/dist/src/cli/commands/gateway/health.d.ts +2 -0
- package/dist/src/cli/commands/gateway/health.js +77 -0
- package/dist/src/cli/commands/gateway/health.js.map +1 -0
- package/dist/src/cli/commands/gateway/index.d.ts +3 -0
- package/dist/src/cli/commands/gateway/index.js +4 -1
- package/dist/src/cli/commands/gateway/probe.d.ts +2 -0
- package/dist/src/cli/commands/gateway/probe.js +102 -0
- package/dist/src/cli/commands/gateway/probe.js.map +1 -0
- package/dist/src/cli/commands/gateway/status.d.ts +0 -3
- package/dist/src/cli/commands/gateway/status.js +107 -24
- package/dist/src/cli/commands/gateway/status.js.map +1 -1
- package/dist/src/cli/commands/gateway.js +7 -1
- package/dist/src/cli/commands/gateway.js.map +1 -1
- package/dist/src/cli/commands/init.js +3 -3
- package/dist/src/cli/commands/update.js +19 -1
- package/dist/src/cli/commands/update.js.map +1 -1
- package/dist/src/cli/utils/gateway-client.d.ts +28 -0
- package/dist/src/cli/utils/gateway-client.js +115 -0
- package/dist/src/cli/utils/gateway-client.js.map +1 -0
- package/dist/src/config/index.js +2 -2
- package/dist/src/config/loader.js +1 -1
- package/dist/src/config/models-json.js +1 -1
- package/dist/src/config/paths-state.d.ts +4 -0
- package/dist/src/config/paths-state.js +9 -1
- package/dist/src/config/paths-state.js.map +1 -1
- package/dist/src/config/profile.js +2 -2
- package/dist/src/config/reload.d.ts +2 -0
- package/dist/src/config/reload.js +9 -1
- package/dist/src/config/reload.js.map +1 -1
- package/dist/src/config/rules.js +12 -2
- package/dist/src/config/rules.js.map +1 -1
- package/dist/src/cron/executor.js +2 -2
- package/dist/src/cron/persistence.js +1 -1
- package/dist/src/cron/run-log-store.js +1 -1
- package/dist/src/extensions/api.d.ts +6 -1
- package/dist/src/extensions/api.js +52 -1
- package/dist/src/extensions/api.js.map +1 -1
- package/dist/src/extensions/health.js +1 -1
- package/dist/src/extensions/loader.d.ts +6 -1
- package/dist/src/extensions/loader.js +21 -2
- package/dist/src/extensions/loader.js.map +1 -1
- package/dist/src/extensions/lockfile.js +1 -1
- package/dist/src/extensions/normalize-manifest.js +33 -0
- package/dist/src/extensions/normalize-manifest.js.map +1 -1
- package/dist/src/extensions/sdk/index.d.ts +1 -1
- package/dist/src/extensions/sdk/index.js.map +1 -1
- package/dist/src/extensions/types/core.d.ts +35 -1
- package/dist/src/extensions/types/manifest.d.ts +14 -0
- package/dist/src/gateway/agents-admin.js +1 -1
- package/dist/src/gateway/hono/lib/config-payload.d.ts +3 -0
- package/dist/src/gateway/hono/lib/config-payload.js +1 -0
- package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
- package/dist/src/gateway/hono/oauth.js +1 -1
- package/dist/src/gateway/hono/routes/channels.js +111 -0
- package/dist/src/gateway/hono/routes/channels.js.map +1 -1
- package/dist/src/gateway/hono/routes/commands-skills.js +13 -2
- package/dist/src/gateway/hono/routes/commands-skills.js.map +1 -1
- package/dist/src/gateway/hono/routes/config.js +82 -1
- package/dist/src/gateway/hono/routes/config.js.map +1 -1
- package/dist/src/gateway/hono/routes/public-gateway.js +17 -0
- package/dist/src/gateway/hono/routes/public-gateway.js.map +1 -1
- package/dist/src/gateway/hono/routes/sessions.js +16 -0
- package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
- package/dist/src/gateway/hono/routes/status.js +31 -7
- package/dist/src/gateway/hono/routes/status.js.map +1 -1
- package/dist/src/gateway/hono/routes/update.js +118 -15
- package/dist/src/gateway/hono/routes/update.js.map +1 -1
- package/dist/src/gateway/hono/routes/workspace.js +2 -2
- package/dist/src/gateway/hono/sse.js +2 -2
- package/dist/src/gateway/index.js +1 -1
- package/dist/src/gateway/server.js +3 -0
- package/dist/src/gateway/server.js.map +1 -1
- package/dist/src/gateway/service.d.ts +23 -0
- package/dist/src/gateway/service.js +111 -4
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/gateway/workspace-heartbeat-path.js +1 -1
- package/dist/src/infra/update-check.js +54 -21
- package/dist/src/infra/update-check.js.map +1 -1
- package/dist/src/infra/update-lock.d.ts +13 -0
- package/dist/src/infra/update-lock.js +67 -0
- package/dist/src/infra/update-lock.js.map +1 -0
- package/dist/src/infra/update-runner.d.ts +6 -5
- package/dist/src/infra/update-runner.js +93 -13
- package/dist/src/infra/update-runner.js.map +1 -1
- package/dist/src/infra/update-startup.js +37 -11
- package/dist/src/infra/update-startup.js.map +1 -1
- package/dist/src/providers/index.js +2 -2
- package/dist/src/providers/model-registry.js +1 -1
- package/dist/src/session/config-store.js +1 -1
- package/dist/src/session/session-title.js +1 -1
- package/dist/src/session/store.js +3 -3
- package/dist/src/utils/logger/audit.js +1 -1
- package/dist/src/utils/logger/log-store.js +1 -1
- package/dist/src/utils/logger/rotation.js +1 -1
- package/dist/src/voice/tts/audio.js +1 -1
- package/package.json +1 -1
- package/dist/gateway/static/root/assets/channels-settings-DyNnMN1-.js +0 -9
- package/dist/gateway/static/root/assets/channels-settings-DyNnMN1-.js.map +0 -1
- package/dist/gateway/static/root/assets/index-BQNdJlkw.css +0 -1
- package/dist/gateway/static/root/assets/index-fGYWcYhm.js +0 -144
- package/dist/gateway/static/root/assets/index-fGYWcYhm.js.map +0 -1
- package/dist/gateway/static/root/assets/settings-page-CSIVMAJE.js +0 -2
- package/dist/gateway/static/root/assets/settings-page-CSIVMAJE.js.map +0 -1
|
@@ -1,14 +1,37 @@
|
|
|
1
|
+
import { createLogger } from "../utils/logger/index.js";
|
|
2
|
+
import { init_logger } from "../utils/logger.js";
|
|
1
3
|
import { join } from "node:path";
|
|
2
|
-
import { access } from "node:fs/promises";
|
|
4
|
+
import { access, readdir, unlink } from "node:fs/promises";
|
|
3
5
|
import { spawn } from "node:child_process";
|
|
4
6
|
//#region src/infra/update-runner.ts
|
|
7
|
+
init_logger();
|
|
8
|
+
const log = createLogger("UpdateRunner");
|
|
5
9
|
const AUTO_UPDATE_TIMEOUT_MS = 2700 * 1e3;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
function createLineEmitter(onProgress) {
|
|
11
|
+
let bufOut = "";
|
|
12
|
+
let bufErr = "";
|
|
13
|
+
const flush = (buf, source) => {
|
|
14
|
+
const parts = buf.split("\n");
|
|
15
|
+
const rest = parts.pop() ?? "";
|
|
16
|
+
for (const line of parts) if (line.length) onProgress?.(line, source);
|
|
17
|
+
return rest;
|
|
18
|
+
};
|
|
19
|
+
return {
|
|
20
|
+
pushStdout(chunk) {
|
|
21
|
+
bufOut += chunk;
|
|
22
|
+
bufOut = flush(bufOut, "stdout");
|
|
23
|
+
},
|
|
24
|
+
pushStderr(chunk) {
|
|
25
|
+
bufErr += chunk;
|
|
26
|
+
bufErr = flush(bufErr, "stderr");
|
|
27
|
+
},
|
|
28
|
+
flushEnd() {
|
|
29
|
+
if (bufOut.length) onProgress?.(bufOut, "stdout");
|
|
30
|
+
if (bufErr.length) onProgress?.(bufErr, "stderr");
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
async function spawnUpdateCommand(params) {
|
|
12
35
|
const timeoutMs = params.timeoutMs ?? AUTO_UPDATE_TIMEOUT_MS;
|
|
13
36
|
const argv = await resolveUpdateCommandArgv([
|
|
14
37
|
"update",
|
|
@@ -32,32 +55,60 @@ async function runAutoUpdateCommand(params) {
|
|
|
32
55
|
});
|
|
33
56
|
let stdout = "";
|
|
34
57
|
let stderr = "";
|
|
58
|
+
let stdoutTruncated = false;
|
|
59
|
+
let stderrTruncated = false;
|
|
60
|
+
const lineEmitter = createLineEmitter(params.onProgress);
|
|
35
61
|
const timeoutId = setTimeout(() => {
|
|
36
62
|
child.kill("SIGTERM");
|
|
37
63
|
}, timeoutMs);
|
|
38
64
|
const finish = (result) => {
|
|
39
65
|
clearTimeout(timeoutId);
|
|
66
|
+
lineEmitter.flushEnd();
|
|
40
67
|
resolve(result);
|
|
41
68
|
};
|
|
42
69
|
child.stdout?.on("data", (chunk) => {
|
|
43
|
-
|
|
44
|
-
|
|
70
|
+
const text = chunk.toString();
|
|
71
|
+
stdout += text;
|
|
72
|
+
lineEmitter.pushStdout(text);
|
|
73
|
+
if (stdout.length > 64e3) {
|
|
74
|
+
if (!stdoutTruncated) {
|
|
75
|
+
log.warn("Update command stdout exceeded 64KB; truncating");
|
|
76
|
+
stdoutTruncated = true;
|
|
77
|
+
}
|
|
78
|
+
stdout = stdout.slice(-32e3);
|
|
79
|
+
}
|
|
45
80
|
});
|
|
46
81
|
child.stderr?.on("data", (chunk) => {
|
|
47
|
-
|
|
48
|
-
|
|
82
|
+
const text = chunk.toString();
|
|
83
|
+
stderr += text;
|
|
84
|
+
lineEmitter.pushStderr(text);
|
|
85
|
+
if (stderr.length > 64e3) {
|
|
86
|
+
if (!stderrTruncated) {
|
|
87
|
+
log.warn("Update command stderr exceeded 64KB; truncating");
|
|
88
|
+
stderrTruncated = true;
|
|
89
|
+
}
|
|
90
|
+
stderr = stderr.slice(-32e3);
|
|
91
|
+
}
|
|
49
92
|
});
|
|
50
93
|
child.on("error", (err) => {
|
|
94
|
+
log.error({ err }, `Update subprocess spawn error: ${err.message}`);
|
|
51
95
|
finish({
|
|
52
96
|
ok: false,
|
|
53
97
|
exitCode: null,
|
|
54
|
-
reason:
|
|
98
|
+
reason: err.message,
|
|
55
99
|
stdout,
|
|
56
100
|
stderr
|
|
57
101
|
});
|
|
58
102
|
});
|
|
59
103
|
child.on("exit", (code, signal) => {
|
|
60
104
|
if (signal === "SIGTERM" || code === 143) {
|
|
105
|
+
log.warn({
|
|
106
|
+
code,
|
|
107
|
+
signal
|
|
108
|
+
}, "Update subprocess timed out; attempting lock file cleanup");
|
|
109
|
+
cleanupNpmLockFiles(params.root).catch((cleanupErr) => {
|
|
110
|
+
log.warn({ err: cleanupErr }, "Failed to clean npm lock files after timeout");
|
|
111
|
+
});
|
|
61
112
|
finish({
|
|
62
113
|
ok: false,
|
|
63
114
|
exitCode: code,
|
|
@@ -77,6 +128,24 @@ async function runAutoUpdateCommand(params) {
|
|
|
77
128
|
});
|
|
78
129
|
});
|
|
79
130
|
}
|
|
131
|
+
async function runAutoUpdateCommand(params) {
|
|
132
|
+
return spawnUpdateCommand(params);
|
|
133
|
+
}
|
|
134
|
+
async function runAutoUpdateCommandWithProgress(params) {
|
|
135
|
+
return spawnUpdateCommand(params);
|
|
136
|
+
}
|
|
137
|
+
async function cleanupNpmLockFiles(root) {
|
|
138
|
+
if (!root) return;
|
|
139
|
+
let entries;
|
|
140
|
+
try {
|
|
141
|
+
entries = await readdir(root);
|
|
142
|
+
} catch {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
for (const entry of entries) if (entry.startsWith(".package-lock")) try {
|
|
146
|
+
await unlink(join(root, entry));
|
|
147
|
+
} catch {}
|
|
148
|
+
}
|
|
80
149
|
/**
|
|
81
150
|
* Resolve the argv array for spawning the update command.
|
|
82
151
|
*
|
|
@@ -104,9 +173,20 @@ async function resolveUpdateCommandArgv(baseArgs, root) {
|
|
|
104
173
|
];
|
|
105
174
|
} catch {}
|
|
106
175
|
}
|
|
176
|
+
log.warn("Falling back to bare `xopc` command — version mismatch possible");
|
|
177
|
+
try {
|
|
178
|
+
const { execSync } = await import("node:child_process");
|
|
179
|
+
const whichResult = execSync(process.platform === "win32" ? "where xopc" : "which xopc", {
|
|
180
|
+
encoding: "utf-8",
|
|
181
|
+
timeout: 3e3
|
|
182
|
+
}).trim();
|
|
183
|
+
if (whichResult) log.info({ resolvedPath: whichResult.split(/\r?\n/)[0]?.trim() }, "Resolved xopc via PATH");
|
|
184
|
+
} catch {
|
|
185
|
+
log.warn("Could not resolve `xopc` in PATH; update command may fail");
|
|
186
|
+
}
|
|
107
187
|
return ["xopc", ...baseArgs];
|
|
108
188
|
}
|
|
109
189
|
//#endregion
|
|
110
|
-
export { runAutoUpdateCommand };
|
|
190
|
+
export { runAutoUpdateCommand, runAutoUpdateCommandWithProgress };
|
|
111
191
|
|
|
112
192
|
//# sourceMappingURL=update-runner.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-runner.js","names":[],"sources":["../../../src/infra/update-runner.ts"],"sourcesContent":["// src/infra/update-runner.ts\n\nimport { spawn } from 'node:child_process';\nimport { access } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nimport type { UpdateChannel } from './update-channels.js';\n\nconst AUTO_UPDATE_TIMEOUT_MS = 45 * 60 * 1000; // 45 minutes\n\nexport type AutoUpdateResult = {\n ok: boolean;\n exitCode: number | null;\n reason?: string;\n stdout?: string;\n stderr?: string;\n};\n\n
|
|
1
|
+
{"version":3,"file":"update-runner.js","names":[],"sources":["../../../src/infra/update-runner.ts"],"sourcesContent":["// src/infra/update-runner.ts\n\nimport { spawn } from 'node:child_process';\nimport { access, readdir, unlink } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nimport { createLogger } from '../utils/logger.js';\n\nimport type { UpdateChannel } from './update-channels.js';\n\nconst log = createLogger('UpdateRunner');\n\nconst AUTO_UPDATE_TIMEOUT_MS = 45 * 60 * 1000; // 45 minutes\n\nexport type AutoUpdateResult = {\n ok: boolean;\n exitCode: number | null;\n reason?: string;\n stdout?: string;\n stderr?: string;\n};\n\ntype SpawnUpdateParams = {\n channel: UpdateChannel;\n root?: string | null;\n timeoutMs?: number;\n onProgress?: (line: string, source: 'stdout' | 'stderr') => void | Promise<void>;\n};\n\nfunction createLineEmitter(\n onProgress?: (line: string, source: 'stdout' | 'stderr') => void | Promise<void>,\n) {\n let bufOut = '';\n let bufErr = '';\n const flush = (buf: string, source: 'stdout' | 'stderr'): string => {\n const parts = buf.split('\\n');\n const rest = parts.pop() ?? '';\n for (const line of parts) {\n if (line.length) void onProgress?.(line, source);\n }\n return rest;\n };\n return {\n pushStdout(chunk: string) {\n bufOut += chunk;\n bufOut = flush(bufOut, 'stdout');\n },\n pushStderr(chunk: string) {\n bufErr += chunk;\n bufErr = flush(bufErr, 'stderr');\n },\n flushEnd() {\n if (bufOut.length) void onProgress?.(bufOut, 'stdout');\n if (bufErr.length) void onProgress?.(bufErr, 'stderr');\n },\n };\n}\n\nasync function spawnUpdateCommand(params: SpawnUpdateParams): Promise<AutoUpdateResult> {\n const timeoutMs = params.timeoutMs ?? AUTO_UPDATE_TIMEOUT_MS;\n const baseArgs = ['update', '--yes', '--channel', params.channel, '--json'];\n const argv = await resolveUpdateCommandArgv(baseArgs, params.root ?? null);\n\n return new Promise<AutoUpdateResult>((resolve) => {\n const child = spawn(argv[0], argv.slice(1), {\n env: {\n ...process.env,\n XOPC_AUTO_UPDATE: '1',\n },\n stdio: ['ignore', 'pipe', 'pipe'],\n detached: false,\n });\n\n let stdout = '';\n let stderr = '';\n let stdoutTruncated = false;\n let stderrTruncated = false;\n\n const lineEmitter = createLineEmitter(params.onProgress);\n\n const timeoutId = setTimeout(() => {\n child.kill('SIGTERM');\n }, timeoutMs);\n\n const finish = (result: AutoUpdateResult) => {\n clearTimeout(timeoutId);\n lineEmitter.flushEnd();\n resolve(result);\n };\n\n child.stdout?.on('data', (chunk: Buffer) => {\n const text = chunk.toString();\n stdout += text;\n lineEmitter.pushStdout(text);\n if (stdout.length > 64_000) {\n if (!stdoutTruncated) {\n log.warn('Update command stdout exceeded 64KB; truncating');\n stdoutTruncated = true;\n }\n stdout = stdout.slice(-32_000);\n }\n });\n child.stderr?.on('data', (chunk: Buffer) => {\n const text = chunk.toString();\n stderr += text;\n lineEmitter.pushStderr(text);\n if (stderr.length > 64_000) {\n if (!stderrTruncated) {\n log.warn('Update command stderr exceeded 64KB; truncating');\n stderrTruncated = true;\n }\n stderr = stderr.slice(-32_000);\n }\n });\n\n child.on('error', (err) => {\n log.error({ err }, `Update subprocess spawn error: ${err.message}`);\n finish({ ok: false, exitCode: null, reason: err.message, stdout, stderr });\n });\n\n child.on('exit', (code, signal) => {\n if (signal === 'SIGTERM' || code === 143) {\n log.warn({ code, signal }, 'Update subprocess timed out; attempting lock file cleanup');\n void cleanupNpmLockFiles(params.root).catch((cleanupErr) => {\n log.warn({ err: cleanupErr }, 'Failed to clean npm lock files after timeout');\n });\n finish({ ok: false, exitCode: code, reason: 'timeout', stdout, stderr });\n return;\n }\n finish({\n ok: code === 0,\n exitCode: code,\n reason: code === 0 ? undefined : 'non-zero-exit',\n stdout,\n stderr,\n });\n });\n });\n}\n\nexport async function runAutoUpdateCommand(params: {\n channel: UpdateChannel;\n root?: string | null;\n timeoutMs?: number;\n}): Promise<AutoUpdateResult> {\n return spawnUpdateCommand(params);\n}\n\nexport async function runAutoUpdateCommandWithProgress(params: {\n channel: UpdateChannel;\n root?: string | null;\n timeoutMs?: number;\n onProgress?: (line: string, source: 'stdout' | 'stderr') => void | Promise<void>;\n}): Promise<AutoUpdateResult> {\n return spawnUpdateCommand(params);\n}\n\nasync function cleanupNpmLockFiles(root: string | null | undefined): Promise<void> {\n if (!root) return;\n let entries: string[];\n try {\n entries = await readdir(root);\n } catch {\n return;\n }\n for (const entry of entries) {\n if (entry.startsWith('.package-lock')) {\n try {\n await unlink(join(root, entry));\n } catch {\n // best-effort\n }\n }\n }\n}\n\n/**\n * Resolve the argv array for spawning the update command.\n *\n * Priority:\n * 1. process.execPath + process.argv[1] (current runtime + entry point)\n * 2. process.execPath + known dist entry points in root\n * 3. Fallback to bare `xopc` (assumes global install)\n */\nasync function resolveUpdateCommandArgv(\n baseArgs: string[],\n root: string | null,\n): Promise<string[]> {\n const execPath = process.execPath?.trim();\n const argv1 = process.argv[1]?.trim();\n\n if (execPath && argv1) {\n return [execPath, argv1, ...baseArgs];\n }\n\n if (execPath && root) {\n const candidates = [join(root, 'dist/src/cli/index.js'), join(root, 'dist/index.js')];\n for (const candidate of candidates) {\n try {\n await access(candidate);\n return [execPath, candidate, ...baseArgs];\n } catch {\n // try next\n }\n }\n }\n\n log.warn('Falling back to bare `xopc` command — version mismatch possible');\n try {\n const { execSync } = await import('node:child_process');\n const cmd = process.platform === 'win32' ? 'where xopc' : 'which xopc';\n const whichResult = execSync(cmd, { encoding: 'utf-8', timeout: 3000 }).trim();\n if (whichResult) {\n log.info({ resolvedPath: whichResult.split(/\\r?\\n/)[0]?.trim() }, 'Resolved xopc via PATH');\n }\n } catch {\n log.warn('Could not resolve `xopc` in PATH; update command may fail');\n }\n\n return ['xopc', ...baseArgs];\n}\n"],"mappings":"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"}
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
import { PACKAGE_VERSION, init_package_version } from "../package-version.js";
|
|
2
|
+
import { init_paths_state, resolveUpdateCheckStatePath } from "../config/paths-state.js";
|
|
2
3
|
import { createLogger } from "../utils/logger/index.js";
|
|
3
4
|
import { init_logger } from "../utils/logger.js";
|
|
4
|
-
import {
|
|
5
|
-
import { init_paths } from "../config/paths.js";
|
|
5
|
+
import { acquireUpdateLock } from "./update-lock.js";
|
|
6
6
|
import { normalizeUpdateChannel } from "./update-channels.js";
|
|
7
7
|
import { compareSemver, detectInstallKind, resolveNpmChannelTag, resolvePackageRoot } from "./update-check.js";
|
|
8
|
-
import { dirname
|
|
9
|
-
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
10
|
-
import { createHash, randomUUID } from "node:crypto";
|
|
8
|
+
import { dirname } from "node:path";
|
|
9
|
+
import { mkdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
|
|
10
|
+
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
11
11
|
//#region src/infra/update-startup.ts
|
|
12
|
-
|
|
12
|
+
init_paths_state();
|
|
13
13
|
init_package_version();
|
|
14
14
|
init_logger();
|
|
15
15
|
const log = createLogger("UpdateCheck");
|
|
16
|
-
const UPDATE_CHECK_FILENAME = "update-check.json";
|
|
17
16
|
const CHECK_INTERVAL_MS = 1440 * 60 * 1e3;
|
|
18
17
|
const ONE_HOUR_MS = 3600 * 1e3;
|
|
19
18
|
let updateAvailableCache = null;
|
|
@@ -25,14 +24,31 @@ async function readState(statePath) {
|
|
|
25
24
|
try {
|
|
26
25
|
const raw = await readFile(statePath, "utf-8");
|
|
27
26
|
const parsed = JSON.parse(raw);
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
if (!parsed || typeof parsed !== "object") {
|
|
28
|
+
log.warn({ statePath }, "Update check state file contains non-object; resetting");
|
|
29
|
+
return {};
|
|
30
|
+
}
|
|
31
|
+
return parsed;
|
|
32
|
+
} catch (err) {
|
|
33
|
+
if (err.code !== "ENOENT") log.warn({
|
|
34
|
+
err,
|
|
35
|
+
statePath
|
|
36
|
+
}, "Failed to read update check state; resetting");
|
|
30
37
|
return {};
|
|
31
38
|
}
|
|
32
39
|
}
|
|
33
40
|
async function writeState(statePath, state) {
|
|
34
41
|
await mkdir(dirname(statePath), { recursive: true });
|
|
35
|
-
|
|
42
|
+
const tmpPath = `${statePath}.${randomBytes(4).toString("hex")}.tmp`;
|
|
43
|
+
try {
|
|
44
|
+
await writeFile(tmpPath, JSON.stringify(state, null, 2), "utf-8");
|
|
45
|
+
await rename(tmpPath, statePath);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
try {
|
|
48
|
+
await unlink(tmpPath);
|
|
49
|
+
} catch {}
|
|
50
|
+
throw err;
|
|
51
|
+
}
|
|
36
52
|
}
|
|
37
53
|
function resolveCheckIntervalMs(config) {
|
|
38
54
|
const auto = config.update?.auto;
|
|
@@ -59,7 +75,7 @@ async function runGatewayUpdateCheck(params) {
|
|
|
59
75
|
const autoEnabled = config.update?.auto?.enabled ?? false;
|
|
60
76
|
const shouldCheckHints = config.update?.checkOnStart !== false;
|
|
61
77
|
if (!force && !shouldCheckHints && !autoEnabled) return;
|
|
62
|
-
const statePath =
|
|
78
|
+
const statePath = resolveUpdateCheckStatePath();
|
|
63
79
|
const state = await readState(statePath);
|
|
64
80
|
const now = Date.now();
|
|
65
81
|
const lastCheckedAt = state.lastCheckedAt ? Date.parse(state.lastCheckedAt) : null;
|
|
@@ -184,6 +200,14 @@ async function handleAutoUpdate(params) {
|
|
|
184
200
|
version,
|
|
185
201
|
tag
|
|
186
202
|
}, "Starting auto-update");
|
|
203
|
+
const lock = await acquireUpdateLock("auto");
|
|
204
|
+
if (!lock) {
|
|
205
|
+
log.info({
|
|
206
|
+
version,
|
|
207
|
+
tag
|
|
208
|
+
}, "Auto-update skipped: another update is in progress");
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
187
211
|
try {
|
|
188
212
|
const { runAutoUpdateCommand } = await import("./update-runner.js");
|
|
189
213
|
const result = await runAutoUpdateCommand({
|
|
@@ -211,6 +235,8 @@ async function handleAutoUpdate(params) {
|
|
|
211
235
|
channel,
|
|
212
236
|
version
|
|
213
237
|
}, "Auto-update command threw");
|
|
238
|
+
} finally {
|
|
239
|
+
await lock.release();
|
|
214
240
|
}
|
|
215
241
|
}
|
|
216
242
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-startup.js","names":[],"sources":["../../../src/infra/update-startup.ts"],"sourcesContent":["// src/infra/update-startup.ts\n\nimport { createHash, randomUUID } from 'node:crypto';\nimport { readFile, writeFile, mkdir } from 'node:fs/promises';\nimport { join, dirname } from 'node:path';\n\nimport type { Config } from '../config/schema.js';\nimport { resolveStateDir } from '../config/paths.js';\nimport { PACKAGE_VERSION } from '../package-version.js';\nimport { createLogger } from '../utils/logger.js';\n\nimport { normalizeUpdateChannel, DEFAULT_PACKAGE_CHANNEL } from './update-channels.js';\nimport {\n compareSemver,\n resolveNpmChannelTag,\n detectInstallKind,\n resolvePackageRoot,\n type InstallKind,\n type UpdateAvailable,\n} from './update-check.js';\n\nconst log = createLogger('UpdateCheck');\n\n// --- State persistence ---\n\nconst UPDATE_CHECK_FILENAME = 'update-check.json';\nconst CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours\nconst ONE_HOUR_MS = 60 * 60 * 1000;\n\ntype UpdateCheckState = {\n /** `package.json` version at the last successful registry check (used to bypass 24h throttle when you bump the local version). */\n lastCheckPackageVersion?: string;\n lastCheckedAt?: string;\n lastAvailableVersion?: string;\n lastAvailableTag?: string;\n lastNotifiedVersion?: string;\n lastNotifiedTag?: string;\n autoInstallId?: string;\n autoFirstSeenVersion?: string;\n autoFirstSeenTag?: string;\n autoFirstSeenAt?: string;\n autoLastAttemptVersion?: string;\n autoLastAttemptAt?: string;\n autoLastSuccessVersion?: string;\n autoLastSuccessAt?: string;\n};\n\n// --- In-memory cache ---\n\nlet updateAvailableCache: UpdateAvailable | null = null;\n\n/** Get the cached update-available state (populated after startup check). */\nexport function getUpdateAvailable(): UpdateAvailable | null {\n return updateAvailableCache;\n}\n\n// --- Core logic ---\n\nasync function readState(statePath: string): Promise<UpdateCheckState> {\n try {\n const raw = await readFile(statePath, 'utf-8');\n const parsed = JSON.parse(raw) as UpdateCheckState;\n return parsed && typeof parsed === 'object' ? parsed : {};\n } catch {\n return {};\n }\n}\n\nasync function writeState(statePath: string, state: UpdateCheckState): Promise<void> {\n await mkdir(dirname(statePath), { recursive: true });\n await writeFile(statePath, JSON.stringify(state, null, 2), 'utf-8');\n}\n\nfunction resolveCheckIntervalMs(config: Config): number {\n const auto = config.update?.auto;\n if (!auto?.enabled) return CHECK_INTERVAL_MS;\n\n const channel = normalizeUpdateChannel(config.update?.channel) ?? DEFAULT_PACKAGE_CHANNEL;\n if (channel === 'beta') {\n const hours = auto.betaCheckIntervalHours ?? 1;\n return Math.max(ONE_HOUR_MS / 4, Math.floor(hours * ONE_HOUR_MS));\n }\n return ONE_HOUR_MS;\n}\n\n/**\n * Compute a deterministic delay for stable auto-update rollout,\n * based on a per-installation hash to spread updates over time.\n */\nfunction resolveStableJitterMs(\n installId: string,\n version: string,\n tag: string,\n jitterWindowMs: number,\n): number {\n if (jitterWindowMs <= 0) return 0;\n const hash = createHash('sha256').update(`${installId}:${version}:${tag}`).digest();\n const bucket = hash.readUInt32BE(0);\n return bucket % (Math.floor(jitterWindowMs) + 1);\n}\n\n/**\n * Main startup update check. Called once when the gateway starts (or on demand).\n */\nexport async function runGatewayUpdateCheck(params: {\n config: Config;\n onUpdateAvailableChange?: (update: UpdateAvailable | null) => void;\n /** When true, bypass checkOnStart/auto-disabled early exit and throttle (for POST /api/update/check). */\n force?: boolean;\n}): Promise<void> {\n const { config, force } = params;\n\n const autoEnabled = config.update?.auto?.enabled ?? false;\n const shouldCheckHints = config.update?.checkOnStart !== false;\n if (!force && !shouldCheckHints && !autoEnabled) return;\n\n const stateDir = resolveStateDir();\n const statePath = join(stateDir, UPDATE_CHECK_FILENAME);\n const state = await readState(statePath);\n const now = Date.now();\n\n // Hydrate from persisted state if within throttle window\n const lastCheckedAt = state.lastCheckedAt ? Date.parse(state.lastCheckedAt) : null;\n if (state.lastAvailableVersion && (shouldCheckHints || force)) {\n const comparison = compareSemver(PACKAGE_VERSION, state.lastAvailableVersion);\n if (comparison !== null && comparison < 0) {\n const cached: UpdateAvailable = {\n currentVersion: PACKAGE_VERSION,\n latestVersion: state.lastAvailableVersion,\n channel: state.lastAvailableTag ?? 'latest',\n };\n updateAvailableCache = cached;\n params.onUpdateAvailableChange?.(cached);\n }\n }\n\n const checkIntervalMs = resolveCheckIntervalMs(config);\n // Re-check npm when the local package version changed (e.g. after editing package.json) even within 24h.\n const shouldBypassThrottleForVersion =\n state.lastCheckPackageVersion === undefined || state.lastCheckPackageVersion !== PACKAGE_VERSION;\n if (\n !force &&\n !shouldBypassThrottleForVersion &&\n lastCheckedAt &&\n Number.isFinite(lastCheckedAt) &&\n now - lastCheckedAt < checkIntervalMs\n ) {\n return; // Within throttle window\n }\n\n // Install kind: auto-install only for npm global installs, but we still query npm in git\n // so the Web UI / CLI can show \"newer on registry\" and the top reminder bar.\n const root = await resolvePackageRoot();\n let installKind: InstallKind = 'unknown';\n if (root) {\n installKind = await detectInstallKind(root);\n if (installKind === 'git') {\n log.info('Update check: git checkout (hint-only; use git pull to update, no auto npm install)');\n }\n }\n\n // Query npm registry\n const channel = normalizeUpdateChannel(config.update?.channel) ?? DEFAULT_PACKAGE_CHANNEL;\n const resolved = await resolveNpmChannelTag({ channel, timeoutMs: 2500 });\n\n const nextState: UpdateCheckState = {\n ...state,\n lastCheckedAt: new Date(now).toISOString(),\n };\n\n if (!resolved.version) {\n nextState.lastCheckPackageVersion = PACKAGE_VERSION;\n await writeState(statePath, nextState);\n return;\n }\n\n const comparison = compareSemver(PACKAGE_VERSION, resolved.version);\n if (comparison !== null && comparison < 0) {\n // Update available\n const updateInfo: UpdateAvailable = {\n currentVersion: PACKAGE_VERSION,\n latestVersion: resolved.version,\n channel: resolved.tag,\n };\n\n if (shouldCheckHints || force) {\n updateAvailableCache = updateInfo;\n params.onUpdateAvailableChange?.(updateInfo);\n }\n\n nextState.lastAvailableVersion = resolved.version;\n nextState.lastAvailableTag = resolved.tag;\n\n // Log notification (once per version)\n const shouldNotify =\n state.lastNotifiedVersion !== resolved.version || state.lastNotifiedTag !== resolved.tag;\n if ((shouldCheckHints || force) && shouldNotify) {\n log.info(\n { currentVersion: PACKAGE_VERSION, latestVersion: resolved.version, tag: resolved.tag },\n `Update available (${resolved.tag}): v${resolved.version} (current v${PACKAGE_VERSION}). Run: xopc update`,\n );\n nextState.lastNotifiedVersion = resolved.version;\n nextState.lastNotifiedTag = resolved.tag;\n }\n\n // Auto-update logic (never from a git worktree)\n if (\n autoEnabled &&\n (channel === 'stable' || channel === 'beta') &&\n installKind !== 'git'\n ) {\n await handleAutoUpdate({\n channel,\n version: resolved.version,\n tag: resolved.tag,\n state,\n nextState,\n now,\n root,\n config,\n });\n }\n } else {\n // Current version is up to date or newer\n delete nextState.lastAvailableVersion;\n delete nextState.lastAvailableTag;\n updateAvailableCache = null;\n params.onUpdateAvailableChange?.(null);\n }\n\n nextState.lastCheckPackageVersion = PACKAGE_VERSION;\n await writeState(statePath, nextState);\n}\n\nasync function handleAutoUpdate(params: {\n channel: 'stable' | 'beta';\n version: string;\n tag: string;\n state: UpdateCheckState;\n nextState: UpdateCheckState;\n now: number;\n root: string | null;\n config: Config;\n}): Promise<void> {\n const { channel, version, tag, state, nextState, now, root, config } = params;\n const auto = config.update?.auto;\n if (!auto) return;\n\n const stableDelayHours = auto.stableDelayHours ?? 6;\n const stableJitterHours = auto.stableJitterHours ?? 12;\n const betaCheckIntervalHours = auto.betaCheckIntervalHours ?? 1;\n\n // Rate limit: don't re-attempt same version within interval\n const attemptIntervalMs =\n channel === 'beta'\n ? Math.max(ONE_HOUR_MS / 4, Math.floor(betaCheckIntervalHours * ONE_HOUR_MS))\n : ONE_HOUR_MS;\n const lastAttemptAt = state.autoLastAttemptAt ? Date.parse(state.autoLastAttemptAt) : null;\n const recentAttempt =\n state.autoLastAttemptVersion === version &&\n lastAttemptAt !== null &&\n Number.isFinite(lastAttemptAt) &&\n now - lastAttemptAt < attemptIntervalMs;\n\n if (recentAttempt) {\n log.info({ version, tag }, 'Auto-update deferred: recent attempt exists');\n return;\n }\n\n // Stable rollout delay + jitter\n if (channel === 'stable') {\n if (!nextState.autoInstallId) {\n nextState.autoInstallId = state.autoInstallId?.trim() || randomUUID();\n }\n // Track first-seen time for this version\n if (state.autoFirstSeenVersion !== version || state.autoFirstSeenTag !== tag) {\n nextState.autoFirstSeenVersion = version;\n nextState.autoFirstSeenTag = tag;\n nextState.autoFirstSeenAt = new Date(now).toISOString();\n } else {\n nextState.autoFirstSeenAt = state.autoFirstSeenAt;\n }\n\n const firstSeenMs = nextState.autoFirstSeenAt ? Date.parse(nextState.autoFirstSeenAt) : now;\n const baseDelayMs = Math.max(0, stableDelayHours) * ONE_HOUR_MS;\n const jitterWindowMs = Math.max(0, stableJitterHours) * ONE_HOUR_MS;\n const jitterMs = resolveStableJitterMs(nextState.autoInstallId, version, tag, jitterWindowMs);\n const applyAfterMs = firstSeenMs + baseDelayMs + jitterMs;\n\n if (now < applyAfterMs) {\n log.info(\n { version, tag, applyAfter: new Date(applyAfterMs).toISOString() },\n 'Auto-update deferred: stable rollout window not yet due',\n );\n return;\n }\n }\n\n // Execute auto-update\n nextState.autoLastAttemptVersion = version;\n nextState.autoLastAttemptAt = new Date(now).toISOString();\n\n log.info({ channel, version, tag }, 'Starting auto-update');\n\n try {\n const { runAutoUpdateCommand } = await import('./update-runner.js');\n const result = await runAutoUpdateCommand({ channel, root });\n if (result.ok) {\n nextState.autoLastSuccessVersion = version;\n nextState.autoLastSuccessAt = new Date(now).toISOString();\n log.info({ channel, version, tag }, 'Auto-update applied successfully');\n } else {\n log.warn(\n { channel, version, tag, exitCode: result.exitCode, reason: result.reason },\n `Auto-update attempt failed: ${result.reason ?? `exit ${result.exitCode}`}`,\n );\n }\n } catch (err) {\n log.error({ err, channel, version }, 'Auto-update command threw');\n }\n}\n\n/**\n * Schedule periodic update checks. Returns a cleanup function to stop the timer.\n */\nexport function scheduleGatewayUpdateCheck(params: {\n config: Config;\n onUpdateAvailableChange?: (update: UpdateAvailable | null) => void;\n}): () => void {\n let stopped = false;\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n const tick = async () => {\n if (stopped) return;\n try {\n await runGatewayUpdateCheck(params);\n } catch (err) {\n log.warn({ err }, 'Periodic update check failed');\n }\n if (!stopped) {\n const intervalMs = resolveCheckIntervalMs(params.config);\n timer = setTimeout(() => void tick(), intervalMs);\n }\n };\n\n // Initial check after a short delay (don't block startup)\n timer = setTimeout(() => void tick(), 5000);\n\n return () => {\n stopped = true;\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"update-startup.js","names":[],"sources":["../../../src/infra/update-startup.ts"],"sourcesContent":["// src/infra/update-startup.ts\n\nimport { createHash, randomBytes, randomUUID } from 'node:crypto';\nimport { readFile, writeFile, mkdir, rename, unlink } from 'node:fs/promises';\nimport { dirname } from 'node:path';\n\nimport type { Config } from '../config/schema.js';\nimport { resolveUpdateCheckStatePath } from '../config/paths-state.js';\nimport { acquireUpdateLock } from './update-lock.js';\nimport { PACKAGE_VERSION } from '../package-version.js';\nimport { createLogger } from '../utils/logger.js';\n\nimport { normalizeUpdateChannel, DEFAULT_PACKAGE_CHANNEL } from './update-channels.js';\nimport {\n compareSemver,\n resolveNpmChannelTag,\n detectInstallKind,\n resolvePackageRoot,\n type InstallKind,\n type UpdateAvailable,\n} from './update-check.js';\n\nconst log = createLogger('UpdateCheck');\n\n// --- State persistence ---\n\nconst CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours\nconst ONE_HOUR_MS = 60 * 60 * 1000;\n\ntype UpdateCheckState = {\n /** `package.json` version at the last successful registry check (used to bypass 24h throttle when you bump the local version). */\n lastCheckPackageVersion?: string;\n lastCheckedAt?: string;\n lastAvailableVersion?: string;\n lastAvailableTag?: string;\n lastNotifiedVersion?: string;\n lastNotifiedTag?: string;\n autoInstallId?: string;\n autoFirstSeenVersion?: string;\n autoFirstSeenTag?: string;\n autoFirstSeenAt?: string;\n autoLastAttemptVersion?: string;\n autoLastAttemptAt?: string;\n autoLastSuccessVersion?: string;\n autoLastSuccessAt?: string;\n};\n\n// --- In-memory cache ---\n\nlet updateAvailableCache: UpdateAvailable | null = null;\n\n/** Get the cached update-available state (populated after startup check). */\nexport function getUpdateAvailable(): UpdateAvailable | null {\n return updateAvailableCache;\n}\n\n// --- Core logic ---\n\nasync function readState(statePath: string): Promise<UpdateCheckState> {\n try {\n const raw = await readFile(statePath, 'utf-8');\n const parsed = JSON.parse(raw) as UpdateCheckState;\n if (!parsed || typeof parsed !== 'object') {\n log.warn({ statePath }, 'Update check state file contains non-object; resetting');\n return {};\n }\n return parsed;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {\n log.warn({ err, statePath }, 'Failed to read update check state; resetting');\n }\n return {};\n }\n}\n\nasync function writeState(statePath: string, state: UpdateCheckState): Promise<void> {\n await mkdir(dirname(statePath), { recursive: true });\n const tmpPath = `${statePath}.${randomBytes(4).toString('hex')}.tmp`;\n try {\n await writeFile(tmpPath, JSON.stringify(state, null, 2), 'utf-8');\n await rename(tmpPath, statePath);\n } catch (err) {\n try {\n await unlink(tmpPath);\n } catch {\n // ignore\n }\n throw err;\n }\n}\n\nfunction resolveCheckIntervalMs(config: Config): number {\n const auto = config.update?.auto;\n if (!auto?.enabled) return CHECK_INTERVAL_MS;\n\n const channel = normalizeUpdateChannel(config.update?.channel) ?? DEFAULT_PACKAGE_CHANNEL;\n if (channel === 'beta') {\n const hours = auto.betaCheckIntervalHours ?? 1;\n return Math.max(ONE_HOUR_MS / 4, Math.floor(hours * ONE_HOUR_MS));\n }\n return ONE_HOUR_MS;\n}\n\n/**\n * Compute a deterministic delay for stable auto-update rollout,\n * based on a per-installation hash to spread updates over time.\n */\nfunction resolveStableJitterMs(\n installId: string,\n version: string,\n tag: string,\n jitterWindowMs: number,\n): number {\n if (jitterWindowMs <= 0) return 0;\n const hash = createHash('sha256').update(`${installId}:${version}:${tag}`).digest();\n const bucket = hash.readUInt32BE(0);\n return bucket % (Math.floor(jitterWindowMs) + 1);\n}\n\n/**\n * Main startup update check. Called once when the gateway starts (or on demand).\n */\nexport async function runGatewayUpdateCheck(params: {\n config: Config;\n onUpdateAvailableChange?: (update: UpdateAvailable | null) => void;\n /** When true, bypass checkOnStart/auto-disabled early exit and throttle (for POST /api/update/check). */\n force?: boolean;\n}): Promise<void> {\n const { config, force } = params;\n\n const autoEnabled = config.update?.auto?.enabled ?? false;\n const shouldCheckHints = config.update?.checkOnStart !== false;\n if (!force && !shouldCheckHints && !autoEnabled) return;\n\n const statePath = resolveUpdateCheckStatePath();\n const state = await readState(statePath);\n const now = Date.now();\n\n // Hydrate from persisted state if within throttle window\n const lastCheckedAt = state.lastCheckedAt ? Date.parse(state.lastCheckedAt) : null;\n if (state.lastAvailableVersion && (shouldCheckHints || force)) {\n const comparison = compareSemver(PACKAGE_VERSION, state.lastAvailableVersion);\n if (comparison !== null && comparison < 0) {\n const cached: UpdateAvailable = {\n currentVersion: PACKAGE_VERSION,\n latestVersion: state.lastAvailableVersion,\n channel: state.lastAvailableTag ?? 'latest',\n };\n updateAvailableCache = cached;\n params.onUpdateAvailableChange?.(cached);\n }\n }\n\n const checkIntervalMs = resolveCheckIntervalMs(config);\n // Re-check npm when the local package version changed (e.g. after editing package.json) even within 24h.\n const shouldBypassThrottleForVersion =\n state.lastCheckPackageVersion === undefined || state.lastCheckPackageVersion !== PACKAGE_VERSION;\n if (\n !force &&\n !shouldBypassThrottleForVersion &&\n lastCheckedAt &&\n Number.isFinite(lastCheckedAt) &&\n now - lastCheckedAt < checkIntervalMs\n ) {\n return; // Within throttle window\n }\n\n // Install kind: auto-install only for npm global installs, but we still query npm in git\n // so the Web UI / CLI can show \"newer on registry\" and the top reminder bar.\n const root = await resolvePackageRoot();\n let installKind: InstallKind = 'unknown';\n if (root) {\n installKind = await detectInstallKind(root);\n if (installKind === 'git') {\n log.info('Update check: git checkout (hint-only; use git pull to update, no auto npm install)');\n }\n }\n\n // Query npm registry\n const channel = normalizeUpdateChannel(config.update?.channel) ?? DEFAULT_PACKAGE_CHANNEL;\n const resolved = await resolveNpmChannelTag({ channel, timeoutMs: 2500 });\n\n const nextState: UpdateCheckState = {\n ...state,\n lastCheckedAt: new Date(now).toISOString(),\n };\n\n if (!resolved.version) {\n nextState.lastCheckPackageVersion = PACKAGE_VERSION;\n await writeState(statePath, nextState);\n return;\n }\n\n const comparison = compareSemver(PACKAGE_VERSION, resolved.version);\n if (comparison !== null && comparison < 0) {\n // Update available\n const updateInfo: UpdateAvailable = {\n currentVersion: PACKAGE_VERSION,\n latestVersion: resolved.version,\n channel: resolved.tag,\n };\n\n if (shouldCheckHints || force) {\n updateAvailableCache = updateInfo;\n params.onUpdateAvailableChange?.(updateInfo);\n }\n\n nextState.lastAvailableVersion = resolved.version;\n nextState.lastAvailableTag = resolved.tag;\n\n // Log notification (once per version)\n const shouldNotify =\n state.lastNotifiedVersion !== resolved.version || state.lastNotifiedTag !== resolved.tag;\n if ((shouldCheckHints || force) && shouldNotify) {\n log.info(\n { currentVersion: PACKAGE_VERSION, latestVersion: resolved.version, tag: resolved.tag },\n `Update available (${resolved.tag}): v${resolved.version} (current v${PACKAGE_VERSION}). Run: xopc update`,\n );\n nextState.lastNotifiedVersion = resolved.version;\n nextState.lastNotifiedTag = resolved.tag;\n }\n\n // Auto-update logic (never from a git worktree)\n if (\n autoEnabled &&\n (channel === 'stable' || channel === 'beta') &&\n installKind !== 'git'\n ) {\n await handleAutoUpdate({\n channel,\n version: resolved.version,\n tag: resolved.tag,\n state,\n nextState,\n now,\n root,\n config,\n });\n }\n } else {\n // Current version is up to date or newer\n delete nextState.lastAvailableVersion;\n delete nextState.lastAvailableTag;\n updateAvailableCache = null;\n params.onUpdateAvailableChange?.(null);\n }\n\n nextState.lastCheckPackageVersion = PACKAGE_VERSION;\n await writeState(statePath, nextState);\n}\n\nasync function handleAutoUpdate(params: {\n channel: 'stable' | 'beta';\n version: string;\n tag: string;\n state: UpdateCheckState;\n nextState: UpdateCheckState;\n now: number;\n root: string | null;\n config: Config;\n}): Promise<void> {\n const { channel, version, tag, state, nextState, now, root, config } = params;\n const auto = config.update?.auto;\n if (!auto) return;\n\n const stableDelayHours = auto.stableDelayHours ?? 6;\n const stableJitterHours = auto.stableJitterHours ?? 12;\n const betaCheckIntervalHours = auto.betaCheckIntervalHours ?? 1;\n\n // Rate limit: don't re-attempt same version within interval\n const attemptIntervalMs =\n channel === 'beta'\n ? Math.max(ONE_HOUR_MS / 4, Math.floor(betaCheckIntervalHours * ONE_HOUR_MS))\n : ONE_HOUR_MS;\n const lastAttemptAt = state.autoLastAttemptAt ? Date.parse(state.autoLastAttemptAt) : null;\n const recentAttempt =\n state.autoLastAttemptVersion === version &&\n lastAttemptAt !== null &&\n Number.isFinite(lastAttemptAt) &&\n now - lastAttemptAt < attemptIntervalMs;\n\n if (recentAttempt) {\n log.info({ version, tag }, 'Auto-update deferred: recent attempt exists');\n return;\n }\n\n // Stable rollout delay + jitter\n if (channel === 'stable') {\n if (!nextState.autoInstallId) {\n nextState.autoInstallId = state.autoInstallId?.trim() || randomUUID();\n }\n // Track first-seen time for this version\n if (state.autoFirstSeenVersion !== version || state.autoFirstSeenTag !== tag) {\n nextState.autoFirstSeenVersion = version;\n nextState.autoFirstSeenTag = tag;\n nextState.autoFirstSeenAt = new Date(now).toISOString();\n } else {\n nextState.autoFirstSeenAt = state.autoFirstSeenAt;\n }\n\n const firstSeenMs = nextState.autoFirstSeenAt ? Date.parse(nextState.autoFirstSeenAt) : now;\n const baseDelayMs = Math.max(0, stableDelayHours) * ONE_HOUR_MS;\n const jitterWindowMs = Math.max(0, stableJitterHours) * ONE_HOUR_MS;\n const jitterMs = resolveStableJitterMs(nextState.autoInstallId, version, tag, jitterWindowMs);\n const applyAfterMs = firstSeenMs + baseDelayMs + jitterMs;\n\n if (now < applyAfterMs) {\n log.info(\n { version, tag, applyAfter: new Date(applyAfterMs).toISOString() },\n 'Auto-update deferred: stable rollout window not yet due',\n );\n return;\n }\n }\n\n // Execute auto-update\n nextState.autoLastAttemptVersion = version;\n nextState.autoLastAttemptAt = new Date(now).toISOString();\n\n log.info({ channel, version, tag }, 'Starting auto-update');\n\n const lock = await acquireUpdateLock('auto');\n if (!lock) {\n log.info({ version, tag }, 'Auto-update skipped: another update is in progress');\n return;\n }\n try {\n const { runAutoUpdateCommand } = await import('./update-runner.js');\n const result = await runAutoUpdateCommand({ channel, root });\n if (result.ok) {\n nextState.autoLastSuccessVersion = version;\n nextState.autoLastSuccessAt = new Date(now).toISOString();\n log.info({ channel, version, tag }, 'Auto-update applied successfully');\n } else {\n log.warn(\n { channel, version, tag, exitCode: result.exitCode, reason: result.reason },\n `Auto-update attempt failed: ${result.reason ?? `exit ${result.exitCode}`}`,\n );\n }\n } catch (err) {\n log.error({ err, channel, version }, 'Auto-update command threw');\n } finally {\n await lock.release();\n }\n}\n\n/**\n * Schedule periodic update checks. Returns a cleanup function to stop the timer.\n */\nexport function scheduleGatewayUpdateCheck(params: {\n config: Config;\n onUpdateAvailableChange?: (update: UpdateAvailable | null) => void;\n}): () => void {\n let stopped = false;\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n const tick = async () => {\n if (stopped) return;\n try {\n await runGatewayUpdateCheck(params);\n } catch (err) {\n log.warn({ err }, 'Periodic update check failed');\n }\n if (!stopped) {\n const intervalMs = resolveCheckIntervalMs(params.config);\n timer = setTimeout(() => void tick(), intervalMs);\n }\n };\n\n // Initial check after a short delay (don't block startup)\n timer = setTimeout(() => void tick(), 5000);\n\n return () => {\n stopped = true;\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { __esmMin, __exportAll } from "../../_virtual/_rolldown/runtime.js";
|
|
2
|
-
import { PROVIDER_ENV_MAP, getApiKeyFromEnv, init_env_keys } from "./env-keys.js";
|
|
3
|
-
import { CredentialResolver, hasCredentials, init_credentials, resolveApiKey } from "../auth/credentials.js";
|
|
4
2
|
import { hasProviderAuthOnDiskSync, init_sync_provider_auth } from "../auth/sync-provider-auth.js";
|
|
3
|
+
import { PROVIDER_ENV_MAP, getApiKeyFromEnv, init_env_keys } from "./env-keys.js";
|
|
5
4
|
import { ModelRegistry, getModelRegistry, init_model_registry, resetModelRegistry } from "./model-registry.js";
|
|
5
|
+
import { CredentialResolver, hasCredentials, init_credentials, resolveApiKey } from "../auth/credentials.js";
|
|
6
6
|
import { getProviderRegistry, init_plugin_registry } from "./plugin-registry.js";
|
|
7
7
|
import { getModel, getModels, getProviders } from "@mariozechner/pi-ai";
|
|
8
8
|
//#region src/providers/index.ts
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { __esmMin } from "../../_virtual/_rolldown/runtime.js";
|
|
2
2
|
import { createLogger } from "../utils/logger/index.js";
|
|
3
3
|
import { init_logger } from "../utils/logger.js";
|
|
4
|
-
import { getApiKeyFromEnv, init_env_keys } from "./env-keys.js";
|
|
5
4
|
import { resolveModelsJsonPath } from "../config/paths.js";
|
|
6
5
|
import { init_resolve_config_value, resolveConfigValue, resolveHeaders } from "../config/resolve-config-value.js";
|
|
7
6
|
import { getDefaultModelValues, init_models_json, validateModelsConfig } from "../config/models-json.js";
|
|
7
|
+
import { getApiKeyFromEnv, init_env_keys } from "./env-keys.js";
|
|
8
8
|
import { existsSync, readFileSync } from "fs";
|
|
9
9
|
import { getModels, getProviders } from "@mariozechner/pi-ai";
|
|
10
10
|
//#region src/providers/model-registry.ts
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createLogger } from "../utils/logger/index.js";
|
|
2
2
|
import { init_logger } from "../utils/logger.js";
|
|
3
|
-
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
4
3
|
import { join } from "path";
|
|
5
4
|
import { existsSync } from "fs";
|
|
5
|
+
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
6
6
|
//#region src/session/config-store.ts
|
|
7
7
|
/**
|
|
8
8
|
* Session Config Store
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { init_session_key, isCronSessionKey, parseSessionKey } from "../routing/session-key.js";
|
|
1
2
|
import { createLogger } from "../utils/logger/index.js";
|
|
2
3
|
import { init_logger } from "../utils/logger.js";
|
|
3
|
-
import { init_session_key, isCronSessionKey, parseSessionKey } from "../routing/session-key.js";
|
|
4
4
|
import { init_providers, resolveModel } from "../providers/index.js";
|
|
5
5
|
import { stripInboundFileMetadataFromText } from "../channels/attachments/inbound-persist.js";
|
|
6
6
|
import { stripEnvelopeTimestampPrefix } from "../channels/envelope-timestamp.js";
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
+
import { init_agent_scope, resolveDefaultAgentId } from "../agent/agent-scope.js";
|
|
2
|
+
import { init_session_key, parseSessionKey } from "../routing/session-key.js";
|
|
1
3
|
import { createLogger } from "../utils/logger/index.js";
|
|
2
4
|
import { init_logger } from "../utils/logger.js";
|
|
3
|
-
import { init_agent_scope, resolveDefaultAgentId } from "../agent/agent-scope.js";
|
|
4
5
|
import { FILENAMES, init_paths, resolveSessionsDir } from "../config/paths.js";
|
|
5
|
-
import { init_session_key, parseSessionKey } from "../routing/session-key.js";
|
|
6
6
|
import { invalidateSessionSearchIndexCache } from "./search-index-cache.js";
|
|
7
7
|
import { resolveSessionShardRelativePath } from "./shard-path.js";
|
|
8
8
|
import "./types.js";
|
|
9
9
|
import { SessionCompactor } from "../agent/memory/compaction.js";
|
|
10
10
|
import { SlidingWindow } from "../agent/memory/window.js";
|
|
11
11
|
import { cleanTrailingErrors, hasProblematicMessages } from "../agent/memory/message-sanitizer.js";
|
|
12
|
-
import { mkdir, readFile, readdir, stat, unlink, writeFile } from "fs/promises";
|
|
13
12
|
import { basename, join } from "path";
|
|
14
13
|
import { existsSync } from "fs";
|
|
14
|
+
import { mkdir, readFile, readdir, stat, unlink, writeFile } from "fs/promises";
|
|
15
15
|
//#region src/session/store.ts
|
|
16
16
|
init_paths();
|
|
17
17
|
init_agent_scope();
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { __esmMin } from "../../../_virtual/_rolldown/runtime.js";
|
|
2
2
|
import { getLogDir, init_config } from "./config.js";
|
|
3
|
-
import { appendFile } from "fs/promises";
|
|
4
3
|
import path from "path";
|
|
5
4
|
import { existsSync, mkdirSync } from "fs";
|
|
5
|
+
import { appendFile } from "fs/promises";
|
|
6
6
|
//#region src/utils/logger/audit.ts
|
|
7
7
|
/**
|
|
8
8
|
* Audit Log
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { readFile } from "fs/promises";
|
|
2
1
|
import { basename, join } from "path";
|
|
3
2
|
import { createReadStream, existsSync, mkdirSync, readdirSync, statSync, unlinkSync } from "fs";
|
|
3
|
+
import { readFile } from "fs/promises";
|
|
4
4
|
import { gunzip } from "zlib";
|
|
5
5
|
import { promisify } from "util";
|
|
6
6
|
import { createInterface } from "readline";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { __esmMin } from "../../../_virtual/_rolldown/runtime.js";
|
|
2
2
|
import { config, getLogDir, init_config } from "./config.js";
|
|
3
|
-
import { writeFile } from "fs/promises";
|
|
4
3
|
import { join } from "path";
|
|
5
4
|
import { readFileSync, readdirSync, statSync, unlinkSync } from "fs";
|
|
5
|
+
import { writeFile } from "fs/promises";
|
|
6
6
|
import { gzip } from "zlib";
|
|
7
7
|
import { promisify } from "util";
|
|
8
8
|
//#region src/utils/logger/rotation.ts
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createLogger } from "../../utils/logger/index.js";
|
|
2
2
|
import { init_logger } from "../../utils/logger.js";
|
|
3
|
-
import { unlink, writeFile } from "fs/promises";
|
|
4
3
|
import { join } from "path";
|
|
4
|
+
import { unlink, writeFile } from "fs/promises";
|
|
5
5
|
import { spawn } from "child_process";
|
|
6
6
|
import { tmpdir } from "os";
|
|
7
7
|
//#region src/voice/tts/audio.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xopcai/xopc",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.22",
|
|
4
4
|
"description": "Personal AI assistant: CLI, gateway (HTTP/WebSocket + React console), Telegram and WeChat (Weixin) channels — TypeScript, 20+ LLM providers via pi-ai, extensions and skills.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/src/index.js",
|