@rynx-ai/daemon 0.1.0 → 0.1.10-beta.2

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.
Files changed (109) hide show
  1. package/dist/app-browser-host-supervisor.d.ts +97 -0
  2. package/dist/app-browser-host-supervisor.js +529 -0
  3. package/dist/browser-artifact-management.d.ts +20 -0
  4. package/dist/browser-artifact-management.js +61 -0
  5. package/dist/browser-cli-args.d.ts +20 -0
  6. package/dist/browser-cli-args.js +100 -0
  7. package/dist/browser-runner-session-context.d.ts +24 -0
  8. package/dist/browser-runner-session-context.js +113 -0
  9. package/dist/cdp-keyboard-input.d.ts +6 -0
  10. package/dist/cdp-keyboard-input.js +209 -0
  11. package/dist/channel-store.d.ts +0 -6
  12. package/dist/channel-store.js +2 -18
  13. package/dist/chrome-for-testing-release-resolver.d.ts +34 -0
  14. package/dist/chrome-for-testing-release-resolver.js +214 -0
  15. package/dist/chrome-for-testing-store.d.ts +85 -0
  16. package/dist/chrome-for-testing-store.js +836 -0
  17. package/dist/chrome-inspection-gateway.d.ts +58 -0
  18. package/dist/chrome-inspection-gateway.js +806 -0
  19. package/dist/chrome-inspection-manager.d.ts +94 -0
  20. package/dist/chrome-inspection-manager.js +573 -0
  21. package/dist/cli.js +478 -30
  22. package/dist/control-client.d.ts +122 -0
  23. package/dist/control-client.js +855 -0
  24. package/dist/control-deps.js +37 -6
  25. package/dist/control-endpoint.d.ts +14 -0
  26. package/dist/control-endpoint.js +124 -0
  27. package/dist/control-link-store.d.ts +16 -0
  28. package/dist/control-link-store.js +53 -0
  29. package/dist/daemon-build-status.d.ts +8 -0
  30. package/dist/daemon-build-status.js +18 -0
  31. package/dist/daemon-owner.d.ts +14 -0
  32. package/dist/daemon-owner.js +104 -0
  33. package/dist/daemon-server.d.ts +29 -2
  34. package/dist/daemon-server.js +1113 -26
  35. package/dist/db.js +196 -10
  36. package/dist/desktop-browser-host-client.d.ts +35 -0
  37. package/dist/desktop-browser-host-client.js +415 -0
  38. package/dist/direct-runtime-authenticator.d.ts +13 -0
  39. package/dist/direct-runtime-authenticator.js +132 -0
  40. package/dist/direct-runtime-config.d.ts +20 -0
  41. package/dist/direct-runtime-config.js +101 -0
  42. package/dist/embedded-browser-profile-cleanup.d.ts +58 -0
  43. package/dist/embedded-browser-profile-cleanup.js +338 -0
  44. package/dist/headless-browser-host.d.ts +14 -0
  45. package/dist/headless-browser-host.js +2173 -0
  46. package/dist/index-daemon.js +55 -5
  47. package/dist/installation-id.d.ts +33 -0
  48. package/dist/installation-id.js +692 -0
  49. package/dist/maintenance-management.d.ts +11 -0
  50. package/dist/maintenance-management.js +13 -0
  51. package/dist/migrations/cleanup-legacy-sessions.d.ts +0 -3
  52. package/dist/migrations/cleanup-legacy-sessions.js +10 -28
  53. package/dist/plugin-cli.d.ts +30 -0
  54. package/dist/plugin-cli.js +270 -0
  55. package/dist/plugin-host-rpc.d.ts +85 -0
  56. package/dist/plugin-host-rpc.js +878 -0
  57. package/dist/plugin-install-restart.d.ts +13 -0
  58. package/dist/plugin-install-restart.js +24 -0
  59. package/dist/plugin-installer.d.ts +71 -15
  60. package/dist/plugin-installer.js +521 -155
  61. package/dist/plugin-management-service.d.ts +58 -0
  62. package/dist/plugin-management-service.js +240 -0
  63. package/dist/plugin-package.d.ts +61 -0
  64. package/dist/plugin-package.js +732 -0
  65. package/dist/plugin-runtime-control.d.ts +2 -0
  66. package/dist/plugin-runtime-control.js +37 -0
  67. package/dist/plugin-store.d.ts +37 -8
  68. package/dist/plugin-store.js +80 -15
  69. package/dist/plugin-supervisor.d.ts +160 -0
  70. package/dist/plugin-supervisor.js +1143 -0
  71. package/dist/pm2.d.ts +3 -1
  72. package/dist/pm2.js +92 -18
  73. package/dist/provider-cli-service.d.ts +46 -0
  74. package/dist/provider-cli-service.js +510 -0
  75. package/dist/registry.d.ts +6 -19
  76. package/dist/registry.js +22 -87
  77. package/dist/remote-runtime-access-store.d.ts +62 -0
  78. package/dist/remote-runtime-access-store.js +393 -0
  79. package/dist/remote-runtime-admin.d.ts +35 -0
  80. package/dist/remote-runtime-admin.js +110 -0
  81. package/dist/remote-runtime-connection-manager.d.ts +103 -0
  82. package/dist/remote-runtime-connection-manager.js +1575 -0
  83. package/dist/remote-runtime-credential-store.d.ts +45 -0
  84. package/dist/remote-runtime-credential-store.js +639 -0
  85. package/dist/remote-runtime-target-control.d.ts +51 -0
  86. package/dist/remote-runtime-target-control.js +488 -0
  87. package/dist/remote-runtime-target-store.d.ts +56 -0
  88. package/dist/remote-runtime-target-store.js +326 -0
  89. package/dist/runtime-share-addresses.d.ts +21 -0
  90. package/dist/runtime-share-addresses.js +50 -0
  91. package/dist/session-browser-capability-store.d.ts +13 -0
  92. package/dist/session-browser-capability-store.js +58 -0
  93. package/dist/session-emulator-binding-store.d.ts +10 -0
  94. package/dist/session-emulator-binding-store.js +61 -0
  95. package/dist/session-launch-operations.d.ts +41 -0
  96. package/dist/session-launch-operations.js +124 -0
  97. package/dist/session-log-store.js +29 -0
  98. package/dist/session-meta-store.d.ts +3 -2
  99. package/dist/session-meta-store.js +59 -7
  100. package/dist/session-pending-message-store.d.ts +2 -0
  101. package/dist/session-pending-message-store.js +41 -0
  102. package/dist/session-resource-store.d.ts +32 -0
  103. package/dist/session-resource-store.js +700 -0
  104. package/dist/setup.d.ts +16 -0
  105. package/dist/setup.js +144 -5
  106. package/dist/skills-catalog.js +8 -4
  107. package/dist/update.d.ts +11 -2
  108. package/dist/update.js +0 -27
  109. package/package.json +23 -10
package/dist/setup.d.ts CHANGED
@@ -1,4 +1,20 @@
1
+ type ControlListenMode = "loopback" | "host";
2
+ type ManagedBrowserSetupPlan = {
3
+ kind: "ready";
4
+ } | {
5
+ kind: "unsupported";
6
+ } | {
7
+ kind: "offer";
8
+ installByDefault: boolean;
9
+ };
10
+ export declare function controlListenModeFromHost(host: unknown): ControlListenMode;
11
+ export declare function controlHostForListenMode(mode: ControlListenMode): "127.0.0.1" | "0.0.0.0";
12
+ export declare function managedBrowserSetupPlan(listenMode: ControlListenMode, browser: {
13
+ supported: boolean;
14
+ installed: boolean;
15
+ }): ManagedBrowserSetupPlan;
1
16
  /** Walk the user through config.json step by step. Returns a process exit code. */
2
17
  export declare function runSetup(): Promise<number>;
3
18
  /** Read-only diagnostics. Returns a process exit code (0 ok, 1 on any failure). */
4
19
  export declare function runDoctor(): Promise<number>;
20
+ export {};
package/dist/setup.js CHANGED
@@ -15,20 +15,45 @@ import { join } from "node:path";
15
15
  import * as p from "@clack/prompts";
16
16
  import { AGENT_RUNTIME_IDS, getRuntimeProfile, listAgentSpecs, loadConfig, resolveRuntimeHome, rynxAgentsDir, rynxConfigFile, rynxHome, } from "@rynx-ai/core";
17
17
  import { listChannels } from "./channel-store.js";
18
+ import { resolveChromeForTestingRelease } from "./chrome-for-testing-release-resolver.js";
19
+ import { createChromeForTestingArtifactStore, isChromeForTestingHostSupported, } from "./chrome-for-testing-store.js";
18
20
  import { dbPath } from "./db.js";
19
21
  import { listInstances } from "./instance-store.js";
20
- import { listPlugins } from "./plugin-store.js";
22
+ import { isRunnablePluginRecord, listPlugins } from "./plugin-store.js";
21
23
  import { formatControlUrl } from "./pm2.js";
22
24
  const MIN_NODE_MAJOR = 20;
25
+ export function controlListenModeFromHost(host) {
26
+ const normalized = typeof host === "string" ? host.trim().replace(/^\[|\]$/g, "") : "";
27
+ return normalized === "" ||
28
+ normalized === "127.0.0.1" ||
29
+ normalized === "::1" ||
30
+ normalized.toLowerCase() === "localhost"
31
+ ? "loopback"
32
+ : "host";
33
+ }
34
+ export function controlHostForListenMode(mode) {
35
+ return mode === "host" ? "0.0.0.0" : "127.0.0.1";
36
+ }
37
+ export function managedBrowserSetupPlan(listenMode, browser) {
38
+ if (!browser.supported)
39
+ return { kind: "unsupported" };
40
+ if (browser.installed)
41
+ return { kind: "ready" };
42
+ return { kind: "offer", installByDefault: listenMode === "host" };
43
+ }
23
44
  // ── rynx setup — interactive wizard ─────────────────────────────────────────
24
45
  /** Walk the user through config.json step by step. Returns a process exit code. */
25
46
  export async function runSetup() {
26
47
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
27
48
  // Non-interactive (CI / piped): never block on a prompt — just ensure defaults.
28
49
  if (!existsSync(rynxConfigFile()))
29
- writeConfig({ HOST: "0.0.0.0", PORT: 3000, LOG_LEVEL: "info" });
50
+ writeConfig({ HOST: "127.0.0.1", PORT: 3000, LOG_LEVEL: "info" });
51
+ const browser = inspectManagedBrowser();
30
52
  console.log(`非交互环境:已确保 ${rynxConfigFile()} 存在(默认值)。` +
31
- "在终端(TTY)下重跑 `rynx setup` 可分步配置,或直接编辑该文件。");
53
+ "在终端(TTY)下重跑 `rynx setup` 可分步配置,或直接编辑该文件。" +
54
+ (browser.supported && !browser.installed
55
+ ? "\nSession Browser 尚未安装;需要时运行 `rynx browser install`。"
56
+ : ""));
32
57
  return 0;
33
58
  }
34
59
  p.intro("rynx setup · 配置 ~/.rynx");
@@ -81,15 +106,89 @@ export async function runSetup() {
81
106
  if (p.isCancel(port))
82
107
  return bail();
83
108
  cfg.PORT = Number(port);
84
- cfg.HOST = cfg.HOST ?? "0.0.0.0";
109
+ const listenMode = await p.select({
110
+ message: "控制台访问范围 HOST",
111
+ initialValue: controlListenModeFromHost(cfg.HOST),
112
+ options: [
113
+ {
114
+ value: "loopback",
115
+ label: "仅本机(Loopback,推荐)",
116
+ hint: "绑定 127.0.0.1",
117
+ },
118
+ {
119
+ value: "host",
120
+ label: "网络访问(Host)",
121
+ hint: "绑定 0.0.0.0,可信网络可通过机器 IP 访问",
122
+ },
123
+ ],
124
+ });
125
+ if (p.isCancel(listenMode))
126
+ return bail();
127
+ cfg.HOST = controlHostForListenMode(listenMode);
85
128
  cfg.LOG_LEVEL = cfg.LOG_LEVEL ?? "info";
129
+ const browserBeforeSetup = inspectManagedBrowser();
130
+ const browserPlan = managedBrowserSetupPlan(listenMode, {
131
+ supported: browserBeforeSetup.supported,
132
+ installed: Boolean(browserBeforeSetup.installed),
133
+ });
134
+ let installBrowser = false;
135
+ if (browserPlan.kind === "ready") {
136
+ p.log.success(`Session Browser 已就绪 · Chrome for Testing ${browserBeforeSetup.installed.version}`);
137
+ }
138
+ else if (browserPlan.kind === "unsupported") {
139
+ p.log.info(`当前平台 ${process.platform}-${process.arch} 不支持 managed Chrome for Testing;跳过 Browser 安装。`);
140
+ }
141
+ else {
142
+ p.note([
143
+ "Session Browser 让 agent 在当前 Runtime 上打开和操作网页。",
144
+ listenMode === "host"
145
+ ? "网络访问模式下,外部浏览器不能提供 Desktop Browser Host,因此推荐安装无头 Browser。"
146
+ : "桌面 App 可使用内置 Browser;独立 daemon 或远端 Runtime 仍需要无头 Browser。",
147
+ "安装会从 Chrome for Testing 官方源下载浏览器,不会改动系统浏览器。",
148
+ ...(process.platform === "linux"
149
+ ? ["Linux 的动态库和字体依赖仍需由系统或主机镜像提供。"]
150
+ : []),
151
+ ].join("\n"), "Session Browser(可选)");
152
+ const choice = await p.confirm({
153
+ message: "现在安装 managed Chrome for Testing?",
154
+ initialValue: browserPlan.installByDefault,
155
+ });
156
+ if (p.isCancel(choice))
157
+ return bail();
158
+ installBrowser = choice;
159
+ }
86
160
  writeConfig(cfg);
87
161
  p.log.success(`已写入 ${rynxConfigFile()}(chmod 600)`);
88
162
  p.log.message(`HOST=${cfg.HOST} PORT=${cfg.PORT} AGENT_RUNTIME=${cfg.AGENT_RUNTIME ?? "(默认)"}`);
89
- const url = formatControlUrl(String(cfg.HOST ?? "0.0.0.0"), Number(cfg.PORT ?? 3000));
163
+ let browserReady = browserPlan.kind === "ready";
164
+ if (installBrowser) {
165
+ const spinner = p.spinner();
166
+ spinner.start("准备安装 Stable Chrome for Testing");
167
+ try {
168
+ const release = await resolveChromeForTestingRelease({
169
+ kind: "channel",
170
+ channel: "Stable",
171
+ });
172
+ const installedBrowser = await createChromeForTestingArtifactStore().installResolved(release, { onProgress: (message) => spinner.message(message) });
173
+ browserReady = true;
174
+ spinner.stop(`Session Browser 已就绪 · Chrome for Testing ${installedBrowser.version}`);
175
+ }
176
+ catch (error) {
177
+ spinner.error("Session Browser 安装失败");
178
+ p.log.warn(`${error instanceof Error ? error.message : String(error)}\n` +
179
+ "配置已保存;修复网络或系统依赖后运行 `rynx browser install` 重试。");
180
+ }
181
+ }
182
+ else if (browserPlan.kind === "offer") {
183
+ p.log.info("已跳过 Browser 安装;需要时运行 `rynx browser install`。");
184
+ }
185
+ const url = formatControlUrl(String(cfg.HOST ?? "127.0.0.1"), Number(cfg.PORT ?? 3000));
90
186
  p.note([
91
187
  `启动常驻服务 rynx start`,
92
188
  `打开控制台 ${url}`,
189
+ browserReady
190
+ ? "Session Browser 已就绪"
191
+ : "启用 Browser rynx browser install",
93
192
  `新建 agent rynx agent add <id>`,
94
193
  `随时体检 rynx doctor`,
95
194
  ].join("\n"), "下一步");
@@ -146,6 +245,11 @@ export async function runDoctor() {
146
245
  else {
147
246
  const plugins = listPlugins();
148
247
  p.log.info(`插件:${plugins.length} 个${plugins.length ? `(${plugins.map((pl) => pl.name).join(", ")})` : ""}`);
248
+ const legacyPlugins = plugins.filter((plugin) => !isRunnablePluginRecord(plugin));
249
+ if (legacyPlugins.length > 0) {
250
+ p.log.warn(`需重新安装的旧插件:${legacyPlugins.map((plugin) => plugin.name).join(", ")}`);
251
+ warn++;
252
+ }
149
253
  const channels = listChannels();
150
254
  const configured = channels.filter((c) => c.options && Object.keys(c.options).length > 0).length;
151
255
  p.log.info(`channel:${channels.length} 个(${configured} 已配置凭据) · instance:${listInstances().length} 个`);
@@ -153,6 +257,24 @@ export async function runDoctor() {
153
257
  (p.log.warn("还没有 channel —— 在 Web 控制台新建并授权"), warn++);
154
258
  }
155
259
  p.log.info(`agent:${(await listAgentSpecs()).length} 个(${rynxAgentsDir()})`);
260
+ const browser = inspectManagedBrowser();
261
+ if (!browser.supported) {
262
+ p.log.info(`Session Browser:当前平台 ${process.platform}-${process.arch} 不受支持`);
263
+ }
264
+ else if (browser.installed) {
265
+ p.log.success(`Session Browser:Chrome for Testing ${browser.installed.version} 已安装`);
266
+ if (process.platform === "linux") {
267
+ p.log.info("Linux Browser 动态库和字体依赖由系统或主机镜像提供");
268
+ }
269
+ }
270
+ else if (browser.error) {
271
+ p.log.warn(`Session Browser 安装无效:${browser.error} —— 运行 \`rynx browser install\` 修复`);
272
+ warn++;
273
+ }
274
+ else {
275
+ p.log.warn("Session Browser 未安装 —— 运行 `rynx browser install`");
276
+ warn++;
277
+ }
156
278
  if (fail > 0) {
157
279
  p.outro(`体检发现 ${fail} 个失败项(警告 ${warn})—— 处理后重跑 rynx doctor`);
158
280
  return 1;
@@ -160,6 +282,23 @@ export async function runDoctor() {
160
282
  p.outro(warn > 0 ? `基本就绪,有 ${warn} 个警告` : "全部通过 ✓");
161
283
  return 0;
162
284
  }
285
+ function inspectManagedBrowser() {
286
+ if (!isChromeForTestingHostSupported())
287
+ return { supported: false };
288
+ try {
289
+ return {
290
+ supported: true,
291
+ installed: createChromeForTestingArtifactStore().readActive(),
292
+ };
293
+ }
294
+ catch (error) {
295
+ return {
296
+ supported: true,
297
+ installed: undefined,
298
+ error: error instanceof Error ? error.message : String(error),
299
+ };
300
+ }
301
+ }
163
302
  function runtimeState(id) {
164
303
  const profile = getRuntimeProfile(id);
165
304
  const installed = onPath(profile.defaultBinary);
@@ -12,7 +12,7 @@ const SKILL_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
12
12
  /** A single file's contents are capped to keep responses small. */
13
13
  const MAX_SKILL_FILE_BYTES = 256 * 1024;
14
14
  export function listCatalogSkills() {
15
- return scanSkillsDir(rynxSkillsDir());
15
+ return listMergedSkills();
16
16
  }
17
17
  /** Install a skill (or a repo's skills) into the catalog per a full install
18
18
  * recipe; delegates to the core catalog layer (which also writes each skill's
@@ -20,7 +20,7 @@ export function listCatalogSkills() {
20
20
  * the refreshed catalog. */
21
21
  export async function installCatalogSkill(recipe) {
22
22
  await installIntoCatalog(recipe, rynxSkillsDir());
23
- return scanSkillsDir(rynxSkillsDir());
23
+ return listMergedSkills();
24
24
  }
25
25
  /** Detail for one catalog skill: its metadata + the file tree under its dir. */
26
26
  export async function getCatalogSkill(name) {
@@ -30,6 +30,7 @@ export async function getCatalogSkill(name) {
30
30
  const meta = await readSkillMeta(dir);
31
31
  if (!meta)
32
32
  return null;
33
+ const ref = meta.ref;
33
34
  const files = await walkSkillFiles(dir, dir);
34
35
  let frontmatter;
35
36
  try {
@@ -41,7 +42,7 @@ export async function getCatalogSkill(name) {
41
42
  return {
42
43
  name: meta.name,
43
44
  description: meta.description,
44
- ...(meta.ref ? { ref: meta.ref } : {}),
45
+ ...(ref ? { ref } : {}),
45
46
  frontmatter,
46
47
  totalSize: files.reduce((sum, f) => sum + f.size, 0),
47
48
  files,
@@ -63,7 +64,7 @@ async function walkSkillFiles(root, dir) {
63
64
  export async function readCatalogSkillFile(name, relPath) {
64
65
  if (!SKILL_NAME_PATTERN.test(name))
65
66
  throw new Error(`invalid skill name: ${name}`);
66
- const dir = path.resolve(rynxSkillsDir(), name);
67
+ const dir = path.resolve(path.join(rynxSkillsDir(), name));
67
68
  const target = path.resolve(dir, relPath);
68
69
  if (target !== dir && !target.startsWith(dir + path.sep)) {
69
70
  throw new Error("path escapes the skill directory");
@@ -78,6 +79,9 @@ export async function readCatalogSkillFile(name, relPath) {
78
79
  throw new Error("binary file — not viewable");
79
80
  return { path: relPath, content: buf.toString("utf8") };
80
81
  }
82
+ async function listMergedSkills() {
83
+ return (await scanSkillsDir(rynxSkillsDir())).sort((a, b) => a.name.localeCompare(b.name));
84
+ }
81
85
  /** Remove a catalog skill by name. Returns false if it wasn't present. */
82
86
  export async function removeCatalogSkill(name) {
83
87
  if (!SKILL_NAME_PATTERN.test(name))
package/dist/update.d.ts CHANGED
@@ -1,11 +1,20 @@
1
- import { type UpdateCheck } from "@rynx-ai/core";
2
1
  export interface UpdateOptions {
3
2
  check?: boolean;
4
3
  /** With `check`, emit a typed {@link UpdateCheck} as JSON instead of a human line. */
5
4
  json?: boolean;
6
5
  version?: string;
7
- resultFile?: string;
8
6
  }
7
+ export type UpdateCheck = {
8
+ status: "up_to_date";
9
+ current: string;
10
+ } | {
11
+ status: "behind";
12
+ current: string;
13
+ latest: string;
14
+ } | {
15
+ status: "error";
16
+ detail: string;
17
+ };
9
18
  /** Is `latest` a newer release than `current`? Numeric major.minor.patch (prerelease ignored). */
10
19
  export declare function isNewer(latest: string, current: string): boolean;
11
20
  /** Typed `--check` result a channel consumes via `--check --json`. */
package/dist/update.js CHANGED
@@ -7,12 +7,8 @@
7
7
  * rynx update [version] upgrade (to a version, or latest) + restart
8
8
  * rynx update --check report status only (human one-liner)
9
9
  * rynx update --check --json report status only (typed UpdateCheck JSON)
10
- * rynx update --result-file <path> after finishing, write the outcome JSON to
11
- * <path> (channel-agnostic UpdateOutcome; a
12
- * plugin reads it post-restart to notify a chat)
13
10
  */
14
11
  import { spawnSync } from "node:child_process";
15
- import { mkdirSync, writeFileSync } from "node:fs";
16
12
  import { createRequire } from "node:module";
17
13
  import { dirname, sep } from "node:path";
18
14
  import { loadConfig } from "@rynx-ai/core";
@@ -98,19 +94,6 @@ async function verifyHealthy() {
98
94
  }
99
95
  return false;
100
96
  }
101
- /** Write the post-restart outcome to `--result-file` (best-effort; stamps `at`). */
102
- function writeOutcome(file, outcome) {
103
- if (!file)
104
- return;
105
- try {
106
- mkdirSync(dirname(file), { recursive: true });
107
- const full = { ...outcome, at: new Date().toISOString() };
108
- writeFileSync(file, `${JSON.stringify(full, null, 2)}\n`);
109
- }
110
- catch {
111
- /* best-effort */
112
- }
113
- }
114
97
  /** Run `rynx update`. Returns a process exit code. */
115
98
  export async function runUpdate(opts) {
116
99
  const { name, version: current } = selfPackage();
@@ -122,13 +105,11 @@ export async function runUpdate(opts) {
122
105
  // Never `npm install -g` over a working checkout / linked dev build.
123
106
  if (isLocalDevInstall()) {
124
107
  console.error(`update: ${name} is a local dev install — update your checkout (e.g. pnpm build), not npm`);
125
- writeOutcome(opts.resultFile, { status: "fail", from: current, to: current, error: "local dev install" });
126
108
  return 1;
127
109
  }
128
110
  const target = opts.version ?? latest;
129
111
  if (!target) {
130
112
  console.error(`update: could not resolve a target version for ${name} (registry unreachable?)`);
131
- writeOutcome(opts.resultFile, { status: "fail", from: current, to: current, error: "could not resolve target version" });
132
113
  return 1;
133
114
  }
134
115
  if (!opts.version && latest && !isNewer(latest, current)) {
@@ -138,12 +119,6 @@ export async function runUpdate(opts) {
138
119
  console.log(`Updating ${name}: ${current} → ${target} …`);
139
120
  if (!npmInstallGlobal(name, target)) {
140
121
  console.error("update: npm install failed");
141
- writeOutcome(opts.resultFile, { status: "fail", from: current, to: target, error: "npm install failed" });
142
- // The install failed before any restart, so the channel that spawned us never
143
- // reboots to drain the outcome. Bounce the daemon (still on the old, healthy
144
- // version) so its boot-time drain delivers the failure to chat.
145
- if (opts.resultFile)
146
- startDaemon({ force: true });
147
122
  return 1;
148
123
  }
149
124
  startDaemon({ force: true });
@@ -151,10 +126,8 @@ export async function runUpdate(opts) {
151
126
  console.error(`update: ${name}@${target} did not come back up — rolling back to ${current}`);
152
127
  npmInstallGlobal(name, current);
153
128
  startDaemon({ force: true });
154
- writeOutcome(opts.resultFile, { status: "fail", from: current, to: target, error: "did not come up; rolled back" });
155
129
  return 1;
156
130
  }
157
131
  console.log(`Updated ${name} → ${target} and restarted.`);
158
- writeOutcome(opts.resultFile, { status: "ok", from: current, to: target });
159
132
  return 0;
160
133
  }
package/package.json CHANGED
@@ -1,7 +1,12 @@
1
1
  {
2
2
  "name": "@rynx-ai/daemon",
3
- "version": "0.1.0",
4
- "description": "rynx daemon CLI: supervises the resident @rynx-ai/server via pm2 and loads channel plugins recorded in ~/.rynx/rynx.db.",
3
+ "version": "0.1.10-beta.2",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "git+https://github.com/rynx-ai/rynx.git",
7
+ "directory": "packages/daemon"
8
+ },
9
+ "description": "Rynx daemon CLI: supervises the local agent control plane and isolated optional plugin runtimes.",
5
10
  "type": "module",
6
11
  "publishConfig": {
7
12
  "registry": "https://registry.npmjs.org/",
@@ -15,20 +20,28 @@
15
20
  },
16
21
  "main": "./dist/index-daemon.js",
17
22
  "exports": {
18
- "./server": {
19
- "types": "./dist/daemon-server.d.ts",
20
- "default": "./dist/daemon-server.js"
23
+ "./client": {
24
+ "types": "./dist/control-client.d.ts",
25
+ "default": "./dist/control-client.js"
21
26
  }
22
27
  },
23
28
  "dependencies": {
24
29
  "@clack/prompts": "^1.6.0",
25
30
  "better-sqlite3": "^12.11.1",
31
+ "extract-zip": "2.0.1",
26
32
  "pm2": "^6.0.0",
27
- "zod": "^4.3.6",
28
- "@rynx-ai/core": "0.1.0",
29
- "@rynx-ai/protocol": "0.1.0",
30
- "@rynx-ai/server": "0.1.0",
31
- "@rynx-ai/emulator": "0.1.7"
33
+ "tar": "^7.5.19",
34
+ "ws": "^8.21.0",
35
+ "@rynx-ai/core": "0.1.10-beta.2",
36
+ "@rynx-ai/emulator": "0.1.10-beta.2",
37
+ "@rynx-ai/plugin-runner": "0.1.10-beta.2",
38
+ "@rynx-ai/plugin-sdk": "0.1.10-beta.2",
39
+ "@rynx-ai/protocol": "0.1.10-beta.2",
40
+ "@rynx-ai/remote-runtime-client": "0.1.10-beta.2",
41
+ "@rynx-ai/server": "0.1.10-beta.2"
42
+ },
43
+ "devDependencies": {
44
+ "@types/ws": "^8.18.1"
32
45
  },
33
46
  "scripts": {
34
47
  "build": "rm -rf dist && tsc -p tsconfig.json && chmod +x dist/cli.js dist/index-daemon.js",