@rynx-ai/daemon 0.1.11-beta.15 → 0.1.11-beta.17
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/bundled-plugins/plugins/lark/dist/lark.js +12 -4
- package/bundled-plugins/plugins/lark/dist/runtime.js +10 -3
- package/bundled-plugins/plugins/lark/package.json +1 -1
- package/dist/lifecycle.d.ts +1 -0
- package/dist/lifecycle.js +3 -0
- package/dist/setup-service.d.ts +1 -1
- package/dist/setup-service.js +1 -1
- package/dist/system-dependencies.d.ts +2 -6
- package/dist/system-dependencies.js +23 -93
- package/package.json +9 -9
|
@@ -2,7 +2,7 @@ import { execFile } from "node:child_process";
|
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { promisify } from "node:util";
|
|
5
|
-
import { CodexRuntimeError, collectLiveRun, } from "@rynx-ai/core";
|
|
5
|
+
import { AgentRuntimeError, CodexRuntimeError, collectLiveRun, } from "@rynx-ai/core";
|
|
6
6
|
const execFileAsync = promisify(execFile);
|
|
7
7
|
import { LarkSessionCardController, } from "./lark-card-stream.js";
|
|
8
8
|
import { buildLarkCardReplyPrompt, chunkLarkReplyText, extractLarkTextPrompt, isLarkNewSessionCommand, isLarkStopCommand, parseLarkCommand, } from "./lark-content.js";
|
|
@@ -23,10 +23,18 @@ const DEFAULT_EMPTY_RESPONSE = "抱歉,我暂时没有生成回复。";
|
|
|
23
23
|
const DEFAULT_NEW_SESSION_RESPONSE = "已开启新会话。";
|
|
24
24
|
const DEFAULT_STOP_NO_INFLIGHT_RESPONSE = "当前没有正在进行的任务。";
|
|
25
25
|
const DEFAULT_STOP_ACK_RESPONSE = "已停止当前任务。";
|
|
26
|
+
const LIVE_SESSION_UNAVAILABLE_RESPONSE = "当前设备的 Session 运行环境不可用,可能是内置 tmux 缺失或损坏。请执行 `rynx doctor` 查看状态。";
|
|
26
27
|
const LARK_WORK_STARTED_REACTION = "Get";
|
|
27
28
|
const LARK_WORK_COMPLETED_REACTION = "DONE";
|
|
28
29
|
/** Reaction added to a message whose text was steered into the running turn. */
|
|
29
30
|
const LARK_STEER_ACK_REACTION = "Get";
|
|
31
|
+
function buildRunFailureReply(error) {
|
|
32
|
+
if (error instanceof AgentRuntimeError &&
|
|
33
|
+
(error.code === "LIVE_SESSION_UNAVAILABLE" || error.code === "live_unavailable")) {
|
|
34
|
+
return LIVE_SESSION_UNAVAILABLE_RESPONSE;
|
|
35
|
+
}
|
|
36
|
+
return `处理失败:${toErrorMessage(error)}`;
|
|
37
|
+
}
|
|
30
38
|
const LARK_COMMANDS = [
|
|
31
39
|
{ name: "new", help: "/new — 开启新会话", category: "takeover" },
|
|
32
40
|
{ name: "stop", help: "/stop 或 /cancel — 停止当前正在进行的任务", category: "other" },
|
|
@@ -1655,7 +1663,7 @@ export class LarkMessageIngestor {
|
|
|
1655
1663
|
error: errorMessage,
|
|
1656
1664
|
});
|
|
1657
1665
|
return {
|
|
1658
|
-
text:
|
|
1666
|
+
text: buildRunFailureReply(error),
|
|
1659
1667
|
provider: settings.provider,
|
|
1660
1668
|
};
|
|
1661
1669
|
}
|
|
@@ -1711,7 +1719,7 @@ export class LarkMessageIngestor {
|
|
|
1711
1719
|
await orchestrator.finalizeAll(cancelled ? "cancelled" : "failed");
|
|
1712
1720
|
return {
|
|
1713
1721
|
// A user-triggered cancel needs no redundant chat reply (the card carries it).
|
|
1714
|
-
text: cancelled ? "" :
|
|
1722
|
+
text: cancelled ? "" : buildRunFailureReply(error),
|
|
1715
1723
|
provider: started.provider,
|
|
1716
1724
|
...(started.model ? { model: started.model } : {}),
|
|
1717
1725
|
};
|
|
@@ -1780,7 +1788,7 @@ export class LarkMessageIngestor {
|
|
|
1780
1788
|
error: errorMessage,
|
|
1781
1789
|
});
|
|
1782
1790
|
return {
|
|
1783
|
-
text:
|
|
1791
|
+
text: buildRunFailureReply(error),
|
|
1784
1792
|
provider: settings.provider,
|
|
1785
1793
|
};
|
|
1786
1794
|
}
|
|
@@ -129186,9 +129186,16 @@ var DEFAULT_EMPTY_RESPONSE = "\u62B1\u6B49\uFF0C\u6211\u6682\u65F6\u6CA1\u6709\u
|
|
|
129186
129186
|
var DEFAULT_NEW_SESSION_RESPONSE = "\u5DF2\u5F00\u542F\u65B0\u4F1A\u8BDD\u3002";
|
|
129187
129187
|
var DEFAULT_STOP_NO_INFLIGHT_RESPONSE = "\u5F53\u524D\u6CA1\u6709\u6B63\u5728\u8FDB\u884C\u7684\u4EFB\u52A1\u3002";
|
|
129188
129188
|
var DEFAULT_STOP_ACK_RESPONSE = "\u5DF2\u505C\u6B62\u5F53\u524D\u4EFB\u52A1\u3002";
|
|
129189
|
+
var LIVE_SESSION_UNAVAILABLE_RESPONSE = "\u5F53\u524D\u8BBE\u5907\u7684 Session \u8FD0\u884C\u73AF\u5883\u4E0D\u53EF\u7528\uFF0C\u53EF\u80FD\u662F\u5185\u7F6E tmux \u7F3A\u5931\u6216\u635F\u574F\u3002\u8BF7\u6267\u884C `rynx doctor` \u67E5\u770B\u72B6\u6001\u3002";
|
|
129189
129190
|
var LARK_WORK_STARTED_REACTION = "Get";
|
|
129190
129191
|
var LARK_WORK_COMPLETED_REACTION = "DONE";
|
|
129191
129192
|
var LARK_STEER_ACK_REACTION = "Get";
|
|
129193
|
+
function buildRunFailureReply(error51) {
|
|
129194
|
+
if (error51 instanceof AgentRuntimeError && (error51.code === "LIVE_SESSION_UNAVAILABLE" || error51.code === "live_unavailable")) {
|
|
129195
|
+
return LIVE_SESSION_UNAVAILABLE_RESPONSE;
|
|
129196
|
+
}
|
|
129197
|
+
return `\u5904\u7406\u5931\u8D25\uFF1A${toErrorMessage(error51)}`;
|
|
129198
|
+
}
|
|
129192
129199
|
var LARK_COMMANDS = [
|
|
129193
129200
|
{ name: "new", help: "/new \u2014 \u5F00\u542F\u65B0\u4F1A\u8BDD", category: "takeover" },
|
|
129194
129201
|
{ name: "stop", help: "/stop \u6216 /cancel \u2014 \u505C\u6B62\u5F53\u524D\u6B63\u5728\u8FDB\u884C\u7684\u4EFB\u52A1", category: "other" },
|
|
@@ -130768,7 +130775,7 @@ ${transcript}` : header;
|
|
|
130768
130775
|
error: errorMessage
|
|
130769
130776
|
});
|
|
130770
130777
|
return {
|
|
130771
|
-
text:
|
|
130778
|
+
text: buildRunFailureReply(error51),
|
|
130772
130779
|
provider: settings.provider
|
|
130773
130780
|
};
|
|
130774
130781
|
}
|
|
@@ -130816,7 +130823,7 @@ ${transcript}` : header;
|
|
|
130816
130823
|
await orchestrator.finalizeAll(cancelled ? "cancelled" : "failed");
|
|
130817
130824
|
return {
|
|
130818
130825
|
// A user-triggered cancel needs no redundant chat reply (the card carries it).
|
|
130819
|
-
text: cancelled ? "" :
|
|
130826
|
+
text: cancelled ? "" : buildRunFailureReply(error51),
|
|
130820
130827
|
provider: started.provider,
|
|
130821
130828
|
...started.model ? { model: started.model } : {}
|
|
130822
130829
|
};
|
|
@@ -130880,7 +130887,7 @@ ${transcript}` : header;
|
|
|
130880
130887
|
error: errorMessage
|
|
130881
130888
|
});
|
|
130882
130889
|
return {
|
|
130883
|
-
text:
|
|
130890
|
+
text: buildRunFailureReply(error51),
|
|
130884
130891
|
provider: settings.provider
|
|
130885
130892
|
};
|
|
130886
130893
|
}
|
package/dist/lifecycle.d.ts
CHANGED
package/dist/lifecycle.js
CHANGED
|
@@ -11,6 +11,9 @@ export function startDaemon(options) {
|
|
|
11
11
|
RYNX_DISTRIBUTION: "standalone",
|
|
12
12
|
RYNX_PRODUCT_VERSION: options.context.productVersion,
|
|
13
13
|
RYNX_DAEMON_LIFECYCLE: "standalone",
|
|
14
|
+
// An empty value explicitly disables PATH fallback when the optional
|
|
15
|
+
// platform package is missing, while keeping the control plane runnable.
|
|
16
|
+
RYNX_TMUX_BIN: options.context.tmuxBin ?? "",
|
|
14
17
|
},
|
|
15
18
|
});
|
|
16
19
|
}
|
package/dist/setup-service.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { inspectSystemDependencies, type SystemDependencySnapshot, } from "./system-dependencies.js";
|
|
2
2
|
export interface DaemonDiagnosticSnapshot {
|
|
3
3
|
databasePath: string;
|
|
4
4
|
databaseExists: boolean;
|
package/dist/setup-service.js
CHANGED
|
@@ -4,7 +4,7 @@ import { createChromeForTestingArtifactStore, isChromeForTestingHostSupported }
|
|
|
4
4
|
import { ensureBundledOfficialPlugins } from "./bundled-plugins.js";
|
|
5
5
|
import { dbPath } from "./db.js";
|
|
6
6
|
import { listPlugins } from "./plugin-store.js";
|
|
7
|
-
export {
|
|
7
|
+
export { inspectSystemDependencies, } from "./system-dependencies.js";
|
|
8
8
|
export async function prepareBundledPlugins() {
|
|
9
9
|
return ensureBundledOfficialPlugins();
|
|
10
10
|
}
|
|
@@ -16,11 +16,7 @@ export interface SystemDependencyCommandOptions {
|
|
|
16
16
|
}
|
|
17
17
|
export type SystemDependencyCommandRunner = (command: string, args: readonly string[], options: SystemDependencyCommandOptions) => Promise<SystemDependencyCommandResult>;
|
|
18
18
|
export interface SystemDependencyOptions {
|
|
19
|
-
platform?: string;
|
|
20
|
-
getuid?: () => number | undefined;
|
|
21
|
-
findSystemExecutable?: (name: string) => Promise<string | undefined>;
|
|
22
19
|
runCommand?: SystemDependencyCommandRunner;
|
|
23
|
-
|
|
20
|
+
tmuxBin: string;
|
|
24
21
|
}
|
|
25
|
-
export declare function inspectSystemDependencies(options
|
|
26
|
-
export declare function ensureSystemDependencies(options?: SystemDependencyOptions): Promise<SystemDependencySnapshot>;
|
|
22
|
+
export declare function inspectSystemDependencies(options: SystemDependencyOptions): Promise<SystemDependencySnapshot>;
|
|
@@ -1,128 +1,58 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
|
-
import { constants } from "node:fs";
|
|
3
|
-
import { access } from "node:fs/promises";
|
|
4
2
|
const PROBE_TIMEOUT_MS = 10_000;
|
|
5
|
-
const INSTALL_TIMEOUT_MS = 5 * 60_000;
|
|
6
3
|
const MAX_OUTPUT_BYTES = 1024 * 1024;
|
|
7
|
-
|
|
4
|
+
const MINIMUM_TMUX_VERSION = { major: 3, minor: 4 };
|
|
5
|
+
export async function inspectSystemDependencies(options) {
|
|
8
6
|
const runCommand = options.runCommand ?? runSystemDependencyCommand;
|
|
9
|
-
const probe = await probeTmux(runCommand);
|
|
7
|
+
const probe = await probeTmux(runCommand, options.tmuxBin);
|
|
10
8
|
return {
|
|
11
9
|
tmux: probe.installed
|
|
12
10
|
? { installed: true }
|
|
13
11
|
: { installed: false, detail: probe.detail },
|
|
14
12
|
};
|
|
15
13
|
}
|
|
16
|
-
export async function ensureSystemDependencies(options = {}) {
|
|
17
|
-
const runCommand = options.runCommand ?? runSystemDependencyCommand;
|
|
18
|
-
const before = await probeTmux(runCommand);
|
|
19
|
-
if (before.installed)
|
|
20
|
-
return { tmux: { installed: true } };
|
|
21
|
-
if (before.binaryPresent) {
|
|
22
|
-
throw new Error(before.detail);
|
|
23
|
-
}
|
|
24
|
-
const platform = options.platform ?? process.platform;
|
|
25
|
-
const findSystemExecutable = options.findSystemExecutable ?? findSystemCommand;
|
|
26
|
-
const manager = await resolvePackageManager(platform, findSystemExecutable);
|
|
27
|
-
if (!manager) {
|
|
28
|
-
throw new Error(platform === "darwin"
|
|
29
|
-
? "tmux is required for live Sessions, but Homebrew is unavailable; install tmux and retry"
|
|
30
|
-
: "tmux is required for live Sessions, but no supported package manager (apt-get, dnf, or yum) is available");
|
|
31
|
-
}
|
|
32
|
-
const installArgs = manager.family === "apt"
|
|
33
|
-
? ["install", "-y", "--no-install-recommends", "tmux"]
|
|
34
|
-
: manager.family === "brew"
|
|
35
|
-
? ["install", "tmux"]
|
|
36
|
-
: ["install", "-y", "tmux"];
|
|
37
|
-
let command = manager.path;
|
|
38
|
-
let args = installArgs;
|
|
39
|
-
if (manager.family !== "brew") {
|
|
40
|
-
const getuid = options.getuid ?? (() => process.getuid?.());
|
|
41
|
-
if (getuid() !== 0) {
|
|
42
|
-
const sudo = await findSystemExecutable("sudo");
|
|
43
|
-
const sudoReady = sudo
|
|
44
|
-
? await runCommand(sudo, ["-n", "true"], { timeoutMs: PROBE_TIMEOUT_MS })
|
|
45
|
-
: undefined;
|
|
46
|
-
if (!sudo || sudoReady?.exitCode !== 0) {
|
|
47
|
-
throw new Error(`tmux is required for live Sessions; install it as an administrator and retry: ` +
|
|
48
|
-
`${manager.commandForHumans} install -y tmux`);
|
|
49
|
-
}
|
|
50
|
-
command = sudo;
|
|
51
|
-
args = ["-n", manager.path, ...installArgs];
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
options.onProgress?.("正在安装系统依赖 tmux");
|
|
55
|
-
const installed = await runCommand(command, args, { timeoutMs: INSTALL_TIMEOUT_MS });
|
|
56
|
-
if (installed.exitCode !== 0) {
|
|
57
|
-
throw new Error(`failed to install required system dependency tmux: ${commandFailure(installed)}`);
|
|
58
|
-
}
|
|
59
|
-
const after = await inspectSystemDependencies({ runCommand });
|
|
60
|
-
if (!after.tmux.installed) {
|
|
61
|
-
throw new Error(`tmux installation completed but tmux is still unavailable: ${after.tmux.detail}`);
|
|
62
|
-
}
|
|
63
|
-
options.onProgress?.("tmux 安装并验证完成");
|
|
64
|
-
return after;
|
|
65
|
-
}
|
|
66
14
|
/**
|
|
67
15
|
* A version probe only proves the binary can start. Live Sessions need tmux to
|
|
68
16
|
* create a server and socket, so exercise that path with a private probe server
|
|
69
17
|
* that cannot interfere with a user's existing tmux sessions.
|
|
70
18
|
*/
|
|
71
|
-
async function probeTmux(runCommand) {
|
|
72
|
-
const version = await runCommand(
|
|
19
|
+
async function probeTmux(runCommand, tmuxBin) {
|
|
20
|
+
const version = await runCommand(tmuxBin, ["-V"], { timeoutMs: PROBE_TIMEOUT_MS });
|
|
73
21
|
if (version.exitCode !== 0) {
|
|
74
22
|
return {
|
|
75
23
|
installed: false,
|
|
76
|
-
binaryPresent: version.errorCode !== "ENOENT",
|
|
77
24
|
detail: `tmux -V failed: ${commandFailure(version)}`,
|
|
78
25
|
};
|
|
79
26
|
}
|
|
27
|
+
if (!isSupportedTmuxVersion(version.stdout)) {
|
|
28
|
+
return {
|
|
29
|
+
installed: false,
|
|
30
|
+
detail: `${version.stdout.trim() || tmuxBin} is too old; ` +
|
|
31
|
+
`Rynx requires tmux ${MINIMUM_TMUX_VERSION.major}.${MINIMUM_TMUX_VERSION.minor} or newer`,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
80
34
|
const socketName = `rynx-setup-probe-${process.pid}-${Date.now()}`;
|
|
81
|
-
const functional = await runCommand(
|
|
82
|
-
await runCommand(
|
|
35
|
+
const functional = await runCommand(tmuxBin, ["-L", socketName, "new-session", "-d", "-s", "probe", "true"], { timeoutMs: PROBE_TIMEOUT_MS });
|
|
36
|
+
await runCommand(tmuxBin, ["-L", socketName, "kill-server"], {
|
|
83
37
|
timeoutMs: PROBE_TIMEOUT_MS,
|
|
84
38
|
});
|
|
85
39
|
if (functional.exitCode !== 0) {
|
|
86
40
|
return {
|
|
87
41
|
installed: false,
|
|
88
|
-
binaryPresent: true,
|
|
89
42
|
detail: `${version.stdout.trim() || "tmux"} is installed but cannot start a server: ` +
|
|
90
43
|
commandFailure(functional),
|
|
91
44
|
};
|
|
92
45
|
}
|
|
93
|
-
return { installed: true
|
|
46
|
+
return { installed: true };
|
|
94
47
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if (apt)
|
|
104
|
-
return { family: "apt", path: apt, commandForHumans: "apt-get" };
|
|
105
|
-
const dnf = await findExecutable("dnf");
|
|
106
|
-
if (dnf)
|
|
107
|
-
return { family: "rpm", path: dnf, commandForHumans: "dnf" };
|
|
108
|
-
const yum = await findExecutable("yum");
|
|
109
|
-
return yum ? { family: "rpm", path: yum, commandForHumans: "yum" } : undefined;
|
|
110
|
-
}
|
|
111
|
-
async function findSystemCommand(name) {
|
|
112
|
-
const directories = name === "brew"
|
|
113
|
-
? ["/opt/homebrew/bin", "/usr/local/bin"]
|
|
114
|
-
: ["/usr/bin", "/bin", "/usr/sbin", "/sbin"];
|
|
115
|
-
for (const directory of directories) {
|
|
116
|
-
const candidate = `${directory}/${name}`;
|
|
117
|
-
try {
|
|
118
|
-
await access(candidate, constants.X_OK);
|
|
119
|
-
return candidate;
|
|
120
|
-
}
|
|
121
|
-
catch {
|
|
122
|
-
// Try the next standard system directory.
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
return undefined;
|
|
48
|
+
function isSupportedTmuxVersion(stdout) {
|
|
49
|
+
const match = /^tmux\s+(\d+)\.(\d+)/i.exec(stdout.trim());
|
|
50
|
+
if (!match)
|
|
51
|
+
return false;
|
|
52
|
+
const major = Number(match[1]);
|
|
53
|
+
const minor = Number(match[2]);
|
|
54
|
+
return major > MINIMUM_TMUX_VERSION.major ||
|
|
55
|
+
(major === MINIMUM_TMUX_VERSION.major && minor >= MINIMUM_TMUX_VERSION.minor);
|
|
126
56
|
}
|
|
127
57
|
function runSystemDependencyCommand(command, args, options) {
|
|
128
58
|
return new Promise((resolveResult) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rynx-ai/daemon",
|
|
3
|
-
"version": "0.1.11-beta.
|
|
3
|
+
"version": "0.1.11-beta.17",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/rynx-ai/rynx.git",
|
|
@@ -50,17 +50,17 @@
|
|
|
50
50
|
"pm2": "^6.0.0",
|
|
51
51
|
"tar": "^7.5.19",
|
|
52
52
|
"ws": "^8.21.0",
|
|
53
|
-
"@rynx-ai/
|
|
54
|
-
"@rynx-ai/
|
|
55
|
-
"@rynx-ai/plugin-runner": "0.1.11-beta.
|
|
56
|
-
"@rynx-ai/plugin-sdk": "0.1.11-beta.
|
|
57
|
-
"@rynx-ai/protocol": "0.1.11-beta.
|
|
58
|
-
"@rynx-ai/remote-runtime-client": "0.1.11-beta.
|
|
59
|
-
"@rynx-ai/server": "0.1.11-beta.
|
|
53
|
+
"@rynx-ai/core": "0.1.11-beta.17",
|
|
54
|
+
"@rynx-ai/emulator": "0.1.11-beta.17",
|
|
55
|
+
"@rynx-ai/plugin-runner": "0.1.11-beta.17",
|
|
56
|
+
"@rynx-ai/plugin-sdk": "0.1.11-beta.17",
|
|
57
|
+
"@rynx-ai/protocol": "0.1.11-beta.17",
|
|
58
|
+
"@rynx-ai/remote-runtime-client": "0.1.11-beta.17",
|
|
59
|
+
"@rynx-ai/server": "0.1.11-beta.17"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@types/ws": "^8.18.1",
|
|
63
|
-
"@rynx-ai/plugin-channel-lark": "0.1.11-beta.
|
|
63
|
+
"@rynx-ai/plugin-channel-lark": "0.1.11-beta.17"
|
|
64
64
|
},
|
|
65
65
|
"scripts": {
|
|
66
66
|
"build": "rm -rf dist bundled-plugins && tsc -p tsconfig.json && chmod +x dist/index-daemon.js && node ../../scripts/stage-bundled-plugins.mjs",
|