@rynx-ai/daemon 0.1.11-beta.14 → 0.1.11-beta.16

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rynx-ai/plugin-channel-lark",
3
- "version": "0.1.11-beta.14",
3
+ "version": "0.1.11-beta.16",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/rynx-ai/rynx.git",
@@ -4,6 +4,7 @@ export interface StandaloneDaemonLaunchContext {
4
4
  builtinSkillsDir: string;
5
5
  cliVersion: string;
6
6
  productVersion: string;
7
+ tmuxBin?: string;
7
8
  }
8
9
  export declare function startDaemon(options: {
9
10
  force: boolean;
package/dist/lifecycle.js CHANGED
@@ -11,6 +11,7 @@ export function startDaemon(options) {
11
11
  RYNX_DISTRIBUTION: "standalone",
12
12
  RYNX_PRODUCT_VERSION: options.context.productVersion,
13
13
  RYNX_DAEMON_LIFECYCLE: "standalone",
14
+ ...(options.context.tmuxBin ? { RYNX_TMUX_BIN: options.context.tmuxBin } : {}),
14
15
  },
15
16
  });
16
17
  }
@@ -20,6 +20,16 @@ export interface PluginCliCommandResult {
20
20
  stderrTruncated: boolean;
21
21
  };
22
22
  }
23
+ export interface InstalledPluginListItem {
24
+ id: string;
25
+ version: string;
26
+ source: "local" | "git";
27
+ state: "enabled" | "disabled";
28
+ }
29
+ /** Read the durable installed-plugin inventory without contacting the daemon. */
30
+ export declare function listInstalledPlugins(): InstalledPluginListItem[];
31
+ /** Render manifest-declared command help without executing plugin code. */
32
+ export declare function installedPluginCommandUsage(pluginId: string): string;
23
33
  /** Execute one statically declared, language-neutral plugin command. */
24
34
  export declare function runPluginCliCommand(pluginId: string, args: readonly string[], options?: RunPluginCliOptions): Promise<PluginCliCommandResult>;
25
35
  export declare function pluginCommandUsage(record: RunnablePluginRecord): string;
@@ -6,10 +6,26 @@ import { db } from "./db.js";
6
6
  import { transaction } from "./sqlite.js";
7
7
  import { pluginDataDir, pluginRuntimeCacheDir } from "./plugin-installer.js";
8
8
  import { validatePluginPackageSnapshot } from "./plugin-package.js";
9
- import { getPlugin } from "./plugin-store.js";
9
+ import { getPlugin, listPlugins, } from "./plugin-store.js";
10
10
  import { loadOrCreateDaemonInstallationIdentitySync } from "./installation-id.js";
11
11
  const DEFAULT_TERMINATION_GRACE_MS = 2_000;
12
12
  const MAX_TERMINATION_GRACE_MS = 30_000;
13
+ /** Read the durable installed-plugin inventory without contacting the daemon. */
14
+ export function listInstalledPlugins() {
15
+ return listPlugins().map((record) => ({
16
+ id: record.canonicalId,
17
+ version: record.version,
18
+ source: record.source,
19
+ state: record.enabled ? "enabled" : "disabled",
20
+ }));
21
+ }
22
+ /** Render manifest-declared command help without executing plugin code. */
23
+ export function installedPluginCommandUsage(pluginId) {
24
+ const record = getPlugin(pluginId);
25
+ if (!record)
26
+ throw new Error(`plugin "${pluginId}" is not installed`);
27
+ return pluginCommandUsage(record);
28
+ }
13
29
  /** Execute one statically declared, language-neutral plugin command. */
14
30
  export async function runPluginCliCommand(pluginId, args, options = {}) {
15
31
  const hostEnv = options.env ?? process.env;
@@ -1,4 +1,4 @@
1
- export { ensureSystemDependencies, inspectSystemDependencies, type SystemDependencySnapshot, } from "./system-dependencies.js";
1
+ export { inspectSystemDependencies, type SystemDependencySnapshot, verifySystemDependencies, } from "./system-dependencies.js";
2
2
  export interface DaemonDiagnosticSnapshot {
3
3
  databasePath: string;
4
4
  databaseExists: boolean;
@@ -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 { ensureSystemDependencies, inspectSystemDependencies, } from "./system-dependencies.js";
7
+ export { inspectSystemDependencies, verifySystemDependencies, } from "./system-dependencies.js";
8
8
  export async function prepareBundledPlugins() {
9
9
  return ensureBundledOfficialPlugins();
10
10
  }
@@ -16,11 +16,9 @@ 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
- onProgress?: (message: string) => void;
20
+ tmuxBin?: string;
24
21
  }
25
- export declare function inspectSystemDependencies(options?: Pick<SystemDependencyOptions, "runCommand">): Promise<SystemDependencySnapshot>;
26
- export declare function ensureSystemDependencies(options?: SystemDependencyOptions): Promise<SystemDependencySnapshot>;
22
+ export declare function inspectSystemDependencies(options?: Pick<SystemDependencyOptions, "runCommand" | "tmuxBin">): Promise<SystemDependencySnapshot>;
23
+ /** Validate the selected executable without installing or changing the host. */
24
+ export declare function verifySystemDependencies(options?: SystemDependencyOptions): Promise<SystemDependencySnapshot>;
@@ -1,128 +1,65 @@
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;
4
+ const MINIMUM_TMUX_VERSION = { major: 3, minor: 4 };
7
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 ?? "tmux");
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;
14
+ /** Validate the selected executable without installing or changing the host. */
15
+ export async function verifySystemDependencies(options = {}) {
16
+ const snapshot = await inspectSystemDependencies(options);
17
+ if (!snapshot.tmux.installed)
18
+ throw new Error(snapshot.tmux.detail);
19
+ return snapshot;
65
20
  }
66
21
  /**
67
22
  * A version probe only proves the binary can start. Live Sessions need tmux to
68
23
  * create a server and socket, so exercise that path with a private probe server
69
24
  * that cannot interfere with a user's existing tmux sessions.
70
25
  */
71
- async function probeTmux(runCommand) {
72
- const version = await runCommand("tmux", ["-V"], { timeoutMs: PROBE_TIMEOUT_MS });
26
+ async function probeTmux(runCommand, tmuxBin) {
27
+ const version = await runCommand(tmuxBin, ["-V"], { timeoutMs: PROBE_TIMEOUT_MS });
73
28
  if (version.exitCode !== 0) {
74
29
  return {
75
30
  installed: false,
76
- binaryPresent: version.errorCode !== "ENOENT",
77
31
  detail: `tmux -V failed: ${commandFailure(version)}`,
78
32
  };
79
33
  }
34
+ if (!isSupportedTmuxVersion(version.stdout)) {
35
+ return {
36
+ installed: false,
37
+ detail: `${version.stdout.trim() || tmuxBin} is too old; ` +
38
+ `Rynx requires tmux ${MINIMUM_TMUX_VERSION.major}.${MINIMUM_TMUX_VERSION.minor} or newer`,
39
+ };
40
+ }
80
41
  const socketName = `rynx-setup-probe-${process.pid}-${Date.now()}`;
81
- const functional = await runCommand("tmux", ["-L", socketName, "new-session", "-d", "-s", "probe", "true"], { timeoutMs: PROBE_TIMEOUT_MS });
82
- await runCommand("tmux", ["-L", socketName, "kill-server"], {
42
+ const functional = await runCommand(tmuxBin, ["-L", socketName, "new-session", "-d", "-s", "probe", "true"], { timeoutMs: PROBE_TIMEOUT_MS });
43
+ await runCommand(tmuxBin, ["-L", socketName, "kill-server"], {
83
44
  timeoutMs: PROBE_TIMEOUT_MS,
84
45
  });
85
46
  if (functional.exitCode !== 0) {
86
47
  return {
87
48
  installed: false,
88
- binaryPresent: true,
89
49
  detail: `${version.stdout.trim() || "tmux"} is installed but cannot start a server: ` +
90
50
  commandFailure(functional),
91
51
  };
92
52
  }
93
- return { installed: true, binaryPresent: true };
53
+ return { installed: true };
94
54
  }
95
- async function resolvePackageManager(platform, findExecutable) {
96
- if (platform === "darwin") {
97
- const brew = await findExecutable("brew");
98
- return brew ? { family: "brew", path: brew, commandForHumans: "brew" } : undefined;
99
- }
100
- if (platform !== "linux")
101
- return undefined;
102
- const apt = await findExecutable("apt-get");
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;
55
+ function isSupportedTmuxVersion(stdout) {
56
+ const match = /^tmux\s+(\d+)\.(\d+)/i.exec(stdout.trim());
57
+ if (!match)
58
+ return false;
59
+ const major = Number(match[1]);
60
+ const minor = Number(match[2]);
61
+ return major > MINIMUM_TMUX_VERSION.major ||
62
+ (major === MINIMUM_TMUX_VERSION.major && minor >= MINIMUM_TMUX_VERSION.minor);
126
63
  }
127
64
  function runSystemDependencyCommand(command, args, options) {
128
65
  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.14",
3
+ "version": "0.1.11-beta.16",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/rynx-ai/rynx.git",
@@ -36,6 +36,10 @@
36
36
  "types": "./dist/setup-service.d.ts",
37
37
  "default": "./dist/setup-service.js"
38
38
  },
39
+ "./plugin-cli": {
40
+ "types": "./dist/plugin-cli.d.ts",
41
+ "default": "./dist/plugin-cli.js"
42
+ },
39
43
  "./internal/process": {
40
44
  "types": "./dist/process.d.ts",
41
45
  "default": "./dist/process.js"
@@ -46,17 +50,17 @@
46
50
  "pm2": "^6.0.0",
47
51
  "tar": "^7.5.19",
48
52
  "ws": "^8.21.0",
49
- "@rynx-ai/core": "0.1.11-beta.14",
50
- "@rynx-ai/emulator": "0.1.11-beta.14",
51
- "@rynx-ai/plugin-runner": "0.1.11-beta.14",
52
- "@rynx-ai/plugin-sdk": "0.1.11-beta.14",
53
- "@rynx-ai/protocol": "0.1.11-beta.14",
54
- "@rynx-ai/remote-runtime-client": "0.1.11-beta.14",
55
- "@rynx-ai/server": "0.1.11-beta.14"
53
+ "@rynx-ai/core": "0.1.11-beta.16",
54
+ "@rynx-ai/emulator": "0.1.11-beta.16",
55
+ "@rynx-ai/plugin-runner": "0.1.11-beta.16",
56
+ "@rynx-ai/plugin-sdk": "0.1.11-beta.16",
57
+ "@rynx-ai/protocol": "0.1.11-beta.16",
58
+ "@rynx-ai/remote-runtime-client": "0.1.11-beta.16",
59
+ "@rynx-ai/server": "0.1.11-beta.16"
56
60
  },
57
61
  "devDependencies": {
58
62
  "@types/ws": "^8.18.1",
59
- "@rynx-ai/plugin-channel-lark": "0.1.11-beta.14"
63
+ "@rynx-ai/plugin-channel-lark": "0.1.11-beta.16"
60
64
  },
61
65
  "scripts": {
62
66
  "build": "rm -rf dist bundled-plugins && tsc -p tsconfig.json && chmod +x dist/index-daemon.js && node ../../scripts/stage-bundled-plugins.mjs",