@rynx-ai/daemon 0.1.10 → 0.1.11-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 (117) hide show
  1. package/bundled-plugins/.rynx-plugin/marketplace.json +10 -0
  2. package/bundled-plugins/plugins/lark/.rynx-plugin/plugin.json +37 -0
  3. package/bundled-plugins/plugins/lark/dist/cli-mirror/mirror-index.d.ts +48 -0
  4. package/bundled-plugins/plugins/lark/dist/cli-mirror/mirror-index.js +54 -0
  5. package/bundled-plugins/plugins/lark/dist/cli-mirror/rollout-mirror.d.ts +85 -0
  6. package/bundled-plugins/plugins/lark/dist/cli-mirror/rollout-mirror.js +333 -0
  7. package/bundled-plugins/plugins/lark/dist/cli-mirror/rollout-watcher.d.ts +109 -0
  8. package/bundled-plugins/plugins/lark/dist/cli-mirror/rollout-watcher.js +347 -0
  9. package/bundled-plugins/plugins/lark/dist/host-adapters.d.ts +95 -0
  10. package/bundled-plugins/plugins/lark/dist/host-adapters.js +504 -0
  11. package/bundled-plugins/plugins/lark/dist/index.d.ts +7 -0
  12. package/bundled-plugins/plugins/lark/dist/index.js +7 -0
  13. package/bundled-plugins/plugins/lark/dist/lark-card-stream.d.ts +363 -0
  14. package/bundled-plugins/plugins/lark/dist/lark-card-stream.js +1335 -0
  15. package/bundled-plugins/plugins/lark/dist/lark-config-store.d.ts +30 -0
  16. package/bundled-plugins/plugins/lark/dist/lark-config-store.js +135 -0
  17. package/bundled-plugins/plugins/lark/dist/lark-content.d.ts +30 -0
  18. package/bundled-plugins/plugins/lark/dist/lark-content.js +138 -0
  19. package/bundled-plugins/plugins/lark/dist/lark-conversation-directory.d.ts +65 -0
  20. package/bundled-plugins/plugins/lark/dist/lark-conversation-directory.js +106 -0
  21. package/bundled-plugins/plugins/lark/dist/lark-diff-card.d.ts +25 -0
  22. package/bundled-plugins/plugins/lark/dist/lark-diff-card.js +58 -0
  23. package/bundled-plugins/plugins/lark/dist/lark-fork-transcript.d.ts +16 -0
  24. package/bundled-plugins/plugins/lark/dist/lark-fork-transcript.js +141 -0
  25. package/bundled-plugins/plugins/lark/dist/lark-resume-card.d.ts +31 -0
  26. package/bundled-plugins/plugins/lark/dist/lark-resume-card.js +105 -0
  27. package/bundled-plugins/plugins/lark/dist/lark-runtime-state-store.d.ts +18 -0
  28. package/bundled-plugins/plugins/lark/dist/lark-runtime-state-store.js +95 -0
  29. package/bundled-plugins/plugins/lark/dist/lark-sdk/client.d.ts +8 -0
  30. package/bundled-plugins/plugins/lark/dist/lark-sdk/client.js +32 -0
  31. package/bundled-plugins/plugins/lark/dist/lark-sdk/index.d.ts +1 -0
  32. package/bundled-plugins/plugins/lark/dist/lark-sdk/index.js +1 -0
  33. package/bundled-plugins/plugins/lark/dist/lark-sender.d.ts +114 -0
  34. package/bundled-plugins/plugins/lark/dist/lark-sender.js +323 -0
  35. package/bundled-plugins/plugins/lark/dist/lark-session-store.d.ts +13 -0
  36. package/bundled-plugins/plugins/lark/dist/lark-session-store.js +14 -0
  37. package/bundled-plugins/plugins/lark/dist/lark-settings-card.d.ts +55 -0
  38. package/bundled-plugins/plugins/lark/dist/lark-settings-card.js +158 -0
  39. package/bundled-plugins/plugins/lark/dist/lark-setup.d.ts +39 -0
  40. package/bundled-plugins/plugins/lark/dist/lark-setup.js +103 -0
  41. package/bundled-plugins/plugins/lark/dist/lark.d.ts +507 -0
  42. package/bundled-plugins/plugins/lark/dist/lark.js +1977 -0
  43. package/bundled-plugins/plugins/lark/dist/runtime.d.ts +13 -0
  44. package/bundled-plugins/plugins/lark/dist/runtime.js +131316 -0
  45. package/bundled-plugins/plugins/lark/dist/settings.d.ts +7 -0
  46. package/bundled-plugins/plugins/lark/dist/settings.js +126958 -0
  47. package/bundled-plugins/plugins/lark/package.json +57 -0
  48. package/bundled-plugins/plugins/lark/ui/assets/index-B2dhMlMU.css +1 -0
  49. package/bundled-plugins/plugins/lark/ui/assets/index-D9N-AkLX.js +201 -0
  50. package/bundled-plugins/plugins/lark/ui/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  51. package/bundled-plugins/plugins/lark/ui/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  52. package/bundled-plugins/plugins/lark/ui/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  53. package/bundled-plugins/plugins/lark/ui/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  54. package/bundled-plugins/plugins/lark/ui/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  55. package/bundled-plugins/plugins/lark/ui/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  56. package/bundled-plugins/plugins/lark/ui/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  57. package/bundled-plugins/plugins/lark/ui/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
  58. package/bundled-plugins/plugins/lark/ui/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
  59. package/bundled-plugins/plugins/lark/ui/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  60. package/bundled-plugins/plugins/lark/ui/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  61. package/bundled-plugins/plugins/lark/ui/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
  62. package/bundled-plugins/plugins/lark/ui/index.html +13 -0
  63. package/dist/bundled-plugins.d.ts +15 -0
  64. package/dist/bundled-plugins.js +134 -0
  65. package/dist/cli.js +26 -208
  66. package/dist/control-deps.js +2 -79
  67. package/dist/daemon-server.d.ts +4 -1
  68. package/dist/daemon-server.js +167 -57
  69. package/dist/db.js +145 -124
  70. package/dist/index-daemon.js +25 -4
  71. package/dist/migrations/cleanup-legacy-sessions.d.ts +1 -1
  72. package/dist/migrations/cleanup-legacy-sessions.js +33 -15
  73. package/dist/official-plugins.d.ts +2 -0
  74. package/dist/official-plugins.js +2 -0
  75. package/dist/plugin-cli.d.ts +1 -3
  76. package/dist/plugin-cli.js +56 -45
  77. package/dist/plugin-host-leases.d.ts +4 -0
  78. package/dist/plugin-host-leases.js +7 -0
  79. package/dist/plugin-host-rpc.d.ts +42 -4
  80. package/dist/plugin-host-rpc.js +144 -34
  81. package/dist/plugin-installer.d.ts +24 -55
  82. package/dist/plugin-installer.js +202 -415
  83. package/dist/plugin-management-service.d.ts +30 -23
  84. package/dist/plugin-management-service.js +290 -105
  85. package/dist/plugin-marketplace.d.ts +35 -0
  86. package/dist/plugin-marketplace.js +299 -0
  87. package/dist/plugin-package.d.ts +22 -24
  88. package/dist/plugin-package.js +520 -571
  89. package/dist/plugin-skill-context.d.ts +4 -0
  90. package/dist/plugin-skill-context.js +35 -0
  91. package/dist/plugin-store.d.ts +36 -37
  92. package/dist/plugin-store.js +140 -92
  93. package/dist/plugin-supervisor.d.ts +45 -125
  94. package/dist/plugin-supervisor.js +516 -1053
  95. package/dist/pm2.js +15 -10
  96. package/dist/runtime-project-store.d.ts +9 -0
  97. package/dist/runtime-project-store.js +83 -0
  98. package/dist/session-fallback-workspace.d.ts +22 -0
  99. package/dist/session-fallback-workspace.js +120 -0
  100. package/dist/session-fork-store.d.ts +41 -0
  101. package/dist/session-fork-store.js +288 -0
  102. package/dist/session-launch-operations.d.ts +14 -2
  103. package/dist/session-launch-operations.js +34 -2
  104. package/dist/session-meta-store.d.ts +7 -9
  105. package/dist/session-meta-store.js +28 -49
  106. package/dist/setup.js +26 -14
  107. package/package.json +14 -11
  108. package/dist/channel-store.d.ts +0 -22
  109. package/dist/channel-store.js +0 -65
  110. package/dist/instance-store.d.ts +0 -38
  111. package/dist/instance-store.js +0 -85
  112. package/dist/plugin-install-restart.d.ts +0 -13
  113. package/dist/plugin-install-restart.js +0 -24
  114. package/dist/plugin-runtime-control.d.ts +0 -2
  115. package/dist/plugin-runtime-control.js +0 -37
  116. package/dist/registry.d.ts +0 -10
  117. package/dist/registry.js +0 -52
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Lark settings</title>
7
+ <script type="module" crossorigin src="./assets/index-D9N-AkLX.js"></script>
8
+ <link rel="stylesheet" crossorigin href="./assets/index-B2dhMlMU.css">
9
+ </head>
10
+ <body>
11
+ <div id="root"></div>
12
+ </body>
13
+ </html>
@@ -0,0 +1,15 @@
1
+ export interface EnsureBundledPluginsResult {
2
+ status: "ready" | "unavailable";
3
+ installed: string[];
4
+ updated: string[];
5
+ unchanged: string[];
6
+ marketplacePath?: string;
7
+ }
8
+ export declare function ensureBundledOfficialPlugins(options?: {
9
+ bundleDirectory?: string;
10
+ defaultPlugins?: readonly string[];
11
+ onProgress?: (message: string) => void;
12
+ /** Reclaim supervisor leases left by a prior daemon process. The caller
13
+ * must already hold the exclusive RYNX_HOME daemon-owner lock. */
14
+ reclaimHostLeases?: boolean;
15
+ }): Promise<EnsureBundledPluginsResult>;
@@ -0,0 +1,134 @@
1
+ import { chmodSync, copyFileSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readdirSync, renameSync, rmSync, } from "node:fs";
2
+ import { fileURLToPath } from "node:url";
3
+ import { isAbsolute, join } from "node:path";
4
+ import { rynxHome } from "@rynx-ai/core";
5
+ import { removePluginRuntimeLease } from "@rynx-ai/plugin-sdk";
6
+ import { pluginDataDir, preparePluginInstallation, } from "./plugin-installer.js";
7
+ import { PLUGIN_HOST_RUNTIME_LEASE_IDS } from "./plugin-host-leases.js";
8
+ import { addPluginMarketplace, resolveMarketplacePlugin, } from "./plugin-marketplace.js";
9
+ import { DEFAULT_BUNDLED_PLUGINS, OFFICIAL_MARKETPLACE_ID, } from "./official-plugins.js";
10
+ import { getPlugin, setPluginEnabled } from "./plugin-store.js";
11
+ export async function ensureBundledOfficialPlugins(options = {}) {
12
+ const configuredBundle = options.bundleDirectory ?? process.env.RYNX_BUNDLED_PLUGINS_DIR?.trim();
13
+ const bundleDirectory = configuredBundle ?? fileURLToPath(new URL("../bundled-plugins", import.meta.url));
14
+ if (!isAbsolute(bundleDirectory)) {
15
+ throw new Error("bundled plugin directory must be an absolute path");
16
+ }
17
+ if (!existsSync(bundleDirectory)) {
18
+ if (configuredBundle) {
19
+ throw new Error(`bundled plugin directory not found: ${bundleDirectory}`);
20
+ }
21
+ return emptyResult("unavailable");
22
+ }
23
+ if (!lstatSync(bundleDirectory).isDirectory()) {
24
+ throw new Error(`bundled plugin path is not a directory: ${bundleDirectory}`);
25
+ }
26
+ const marketplacePath = syncBundledMarketplace(bundleDirectory);
27
+ await addPluginMarketplace(marketplacePath, {
28
+ allowSourceRebind: true,
29
+ builtIn: true,
30
+ onProgress: options.onProgress,
31
+ });
32
+ const result = {
33
+ ...emptyResult("ready"),
34
+ marketplacePath,
35
+ };
36
+ for (const identity of options.defaultPlugins ?? DEFAULT_BUNDLED_PLUGINS) {
37
+ if (options.reclaimHostLeases)
38
+ reclaimPluginHostLeases(identity);
39
+ const resolved = resolveMarketplacePlugin(identity);
40
+ const prepared = await preparePluginInstallation(resolved.spec, options.onProgress, {
41
+ marketplaceId: OFFICIAL_MARKETPLACE_ID,
42
+ expectedId: resolved.pluginId,
43
+ expectedIdentity: identity,
44
+ });
45
+ try {
46
+ if (prepared.existing?.integrity === prepared.integrity) {
47
+ result.unchanged.push(identity);
48
+ continue;
49
+ }
50
+ const updating = Boolean(prepared.existing);
51
+ await prepared.commit({ expectedDigest: prepared.integrity });
52
+ if (!updating)
53
+ setPluginEnabled(identity, false);
54
+ (updating ? result.updated : result.installed).push(identity);
55
+ }
56
+ finally {
57
+ prepared.discard();
58
+ }
59
+ }
60
+ return result;
61
+ }
62
+ function reclaimPluginHostLeases(identity) {
63
+ const installed = getPlugin(identity);
64
+ if (!installed)
65
+ return;
66
+ const dataDir = pluginDataDir(installed.marketplaceId, installed.pluginId);
67
+ for (const leaseId of PLUGIN_HOST_RUNTIME_LEASE_IDS) {
68
+ removePluginRuntimeLease(dataDir, leaseId);
69
+ }
70
+ }
71
+ function syncBundledMarketplace(bundleDirectory) {
72
+ const distributionRoot = join(rynxHome(), "distribution");
73
+ const marketplacePath = join(distributionRoot, "official-market");
74
+ mkdirSync(distributionRoot, { recursive: true, mode: 0o700 });
75
+ const transactionDirectory = mkdtempSync(join(distributionRoot, "official-market-stage-"));
76
+ const stagedMarketplace = join(transactionDirectory, "market");
77
+ const backupPath = join(distributionRoot, `official-market-backup-${process.pid}-${Date.now()}`);
78
+ let backedUp = false;
79
+ try {
80
+ copyBundledTree(bundleDirectory, stagedMarketplace);
81
+ if (existsSync(marketplacePath)) {
82
+ renameSync(marketplacePath, backupPath);
83
+ backedUp = true;
84
+ }
85
+ try {
86
+ renameSync(stagedMarketplace, marketplacePath);
87
+ }
88
+ catch (error) {
89
+ if (backedUp && !existsSync(marketplacePath)) {
90
+ renameSync(backupPath, marketplacePath);
91
+ backedUp = false;
92
+ }
93
+ throw error;
94
+ }
95
+ if (backedUp) {
96
+ rmSync(backupPath, { recursive: true, force: true });
97
+ backedUp = false;
98
+ }
99
+ return marketplacePath;
100
+ }
101
+ finally {
102
+ rmSync(transactionDirectory, { recursive: true, force: true });
103
+ if (backedUp && !existsSync(marketplacePath) && existsSync(backupPath)) {
104
+ renameSync(backupPath, marketplacePath);
105
+ }
106
+ }
107
+ }
108
+ function copyBundledTree(source, destination) {
109
+ const info = lstatSync(source);
110
+ const mode = info.mode & 0o777;
111
+ if (info.isSymbolicLink()) {
112
+ throw new Error(`bundled plugin tree must not contain symbolic links: ${source}`);
113
+ }
114
+ if (info.isDirectory()) {
115
+ mkdirSync(destination, { recursive: true, mode: 0o700 });
116
+ for (const entry of readdirSync(source)) {
117
+ copyBundledTree(join(source, entry), join(destination, entry));
118
+ }
119
+ return;
120
+ }
121
+ if (!info.isFile()) {
122
+ throw new Error(`bundled plugin tree contains an unsupported entry: ${source}`);
123
+ }
124
+ copyFileSync(source, destination);
125
+ chmodSync(destination, mode);
126
+ }
127
+ function emptyResult(status) {
128
+ return {
129
+ status,
130
+ installed: [],
131
+ updated: [],
132
+ unchanged: [],
133
+ };
134
+ }
package/dist/cli.js CHANGED
@@ -3,13 +3,13 @@
3
3
  * `rynx` — the daemon CLI.
4
4
  *
5
5
  * rynx start | stop | restart | status | logs process lifecycle (pm2)
6
- * rynx plugin manage install|list|uninstall|enable|disable ...
7
- * rynx plugin <plugin-id> <plugin-command> ...
6
+ * rynx market add|list|remove|refresh ...
7
+ * rynx plugin install|list|update|uninstall|enable|disable ...
8
+ * rynx plugin <plugin-id>@<marketplace-id> <plugin-command> ...
8
9
  * rynx agent list | show <name> | add <name> | rm <name>
9
10
  *
10
- * The CLI owns process lifecycle, plugin install/registry, and the file-based
11
- * declarative agent specs. Channel instances (a channel + an agent) are
12
- * configured in the web control console (`rynx start`, then open it).
11
+ * The CLI owns process lifecycle and file-based declarative agent specs.
12
+ * Plugin and Market mutations go through the resident daemon management API.
13
13
  *
14
14
  * Hand-rolled arg dispatch (no arg-parser dep) to match the repo's zero-dep CLI
15
15
  * style.
@@ -18,21 +18,15 @@ import { spawn } from "node:child_process";
18
18
  import { existsSync } from "node:fs";
19
19
  import { createRequire } from "node:module";
20
20
  import path from "node:path";
21
- import { confirm, isCancel } from "@clack/prompts";
21
+ import { runCli as runModernCli } from "@rynx-ai/cli";
22
22
  import { listAgentSpecs, loadAgentSpec, loadConfig, rynxAgentsDir } from "@rynx-ai/core";
23
23
  import { encodePairingCode } from "@rynx-ai/protocol/direct-runtime";
24
24
  import { agentSpecPath, removeAgentSpec, scaffoldAgentSpec } from "./agent-file.js";
25
25
  import { parseBrowserCliArgs, resolveBrowserCliTarget, } from "./browser-cli-args.js";
26
26
  import { resolveChromeForTestingRelease } from "./chrome-for-testing-release-resolver.js";
27
27
  import { createChromeForTestingArtifactStore } from "./chrome-for-testing-store.js";
28
- import { dbPath } from "./db.js";
29
28
  import { cleanupLegacySessions } from "./migrations/cleanup-legacy-sessions.js";
30
- import { PluginCliUsageError, runPluginCliCommand } from "./plugin-cli.js";
31
- import { installPlugin, uninstallPlugin } from "./plugin-installer.js";
32
- import { restartAfterPluginInstall } from "./plugin-install-restart.js";
33
- import { getPlugin, isRunnablePluginRecord, listPlugins, setPluginEnabled } from "./plugin-store.js";
34
- import { ensureResidentPluginRuntime, reloadResidentPluginRuntime, } from "./plugin-runtime-control.js";
35
- import { isPm2DaemonOnline, startDaemon, statusDaemon, stopDaemon, streamLogs } from "./pm2.js";
29
+ import { startDaemon, statusDaemon, stopDaemon, streamLogs } from "./pm2.js";
36
30
  import { runDoctor, runSetup } from "./setup.js";
37
31
  import { runUpdate } from "./update.js";
38
32
  import { createResidentRemoteRuntimePairingOffer, callManagedRuntimeBrowser, callResidentRuntime, forgetResidentRuntimeTarget, getResidentRuntimeLocalBrowserEndpoint, listResidentRemoteRuntimeClients, listResidentRuntimeTargets, pairResidentRuntimeTarget, revokeResidentRemoteRuntimeClient, readOptionalManagedRuntimeBrowserCredential, testResidentRuntimeTarget, } from "./control-client.js";
@@ -50,15 +44,19 @@ Lifecycle:
50
44
  logs tail the server logs
51
45
  update [version] upgrade @rynx-ai/daemon (npm) + restart (--check: report only)
52
46
 
53
- Plugins (static manifest + isolated runner):
54
- plugin manage list
55
- plugin manage install <npm|path|git|url> [...] [--expect-digest <sha256-...>]
56
- plugin manage update <id> [...] [--expect-digest <sha256-...>]
57
- plugin manage uninstall <id>
58
- plugin manage enable|disable <id>
59
- manage arbitrary installed plugins
60
- plugin <id> <command> run a command declared by that plugin
61
- (\`manage\` is a reserved plugin id)
47
+ Plugin marketplaces:
48
+ market add <git|path> [--alias <id>]
49
+ market list
50
+ market refresh [id]
51
+ market remove <id>
52
+
53
+ Plugins:
54
+ plugin list
55
+ plugin install <git|path|plugin@market> [--force]
56
+ plugin update <plugin@market>
57
+ plugin uninstall <plugin@market>
58
+ plugin enable|disable <plugin@market>
59
+ plugin <plugin@market> <command> [args...]
62
60
 
63
61
  Agents (~/.rynx/agents/<id>/agent.json — dir name is the id):
64
62
  agent list list agents (id · runtime · model · [display name])
@@ -115,189 +113,13 @@ Browser (one Browser per Runtime Session):
115
113
  browser close [--session <id>] [--runtime <local|daemon-id>] [--json]
116
114
  close the current Browser generation
117
115
 
118
- Channel instances (a channel + an agent) are configured in the web control
119
- console (run \`rynx start\`, then open the printed URL).
116
+ Plugin-owned settings and channel instances are configured from each plugin's
117
+ management interface (run \`rynx start\`, then open the printed URL).
120
118
  `;
121
119
  function fail(message) {
122
120
  console.error(message);
123
121
  process.exit(2);
124
122
  }
125
- async function pluginManageCommand(sub, arg) {
126
- switch (sub) {
127
- case "list": {
128
- const plugins = listPlugins();
129
- console.log(`Plugins (db: ${dbPath()})`);
130
- if (plugins.length === 0) {
131
- console.log(" (none)");
132
- return;
133
- }
134
- for (const p of plugins) {
135
- const state = !isRunnablePluginRecord(p)
136
- ? "reinstall-required"
137
- : p.enabled
138
- ? "enabled"
139
- : "disabled";
140
- console.log(` ${p.name.padEnd(16)} ${(p.version ?? "-").padEnd(10)} ${p.source.padEnd(6)} ${state}`);
141
- }
142
- return;
143
- }
144
- case "install": {
145
- if (!arg)
146
- fail("plugin manage install: missing npm/path/git/url source");
147
- const force = process.argv.includes("--force");
148
- const grantAll = process.argv.includes("--grant-all");
149
- const allowScripts = process.argv.includes("--allow-scripts");
150
- const expectedDigest = digestOption(process.argv);
151
- const result = await installPlugin(arg, (line) => console.log(line), {
152
- allowScripts,
153
- ...(expectedDigest ? { expectedDigest } : {}),
154
- onConflict: async (existing) => {
155
- if (force)
156
- return true;
157
- const desc = `${existing.source}${existing.version ? ` @${existing.version}` : ""}`;
158
- if (!process.stdin.isTTY) {
159
- fail(`plugin "${existing.name}" already installed (${desc}). ` +
160
- `Re-run with --force, or \`rynx plugin manage uninstall ${existing.name}\` first.`);
161
- }
162
- const yes = await confirm({
163
- message: `插件 "${existing.name}" 已存在(${desc}),覆盖吗?`,
164
- initialValue: false,
165
- });
166
- return !isCancel(yes) && yes === true;
167
- },
168
- approveCapabilities: (manifest, existing) => approvePluginCapabilities(manifest, existing?.grants ?? [], grantAll),
169
- });
170
- if (result.skipped) {
171
- console.log(`已取消,保留现有插件 "${result.name}"。`);
172
- }
173
- else {
174
- console.log(`Plugin "${result.name}" (${result.source}${result.version ? ` @${result.version}` : ""}) installed.`);
175
- await restartAfterPluginInstall({
176
- restart: process.argv.includes("--restart"),
177
- interactive: Boolean(process.stdin.isTTY && process.stdout.isTTY),
178
- }, {
179
- isDaemonOnline: isPm2DaemonOnline,
180
- confirmRestart: async () => {
181
- const answer = await confirm({
182
- message: "Restart Rynx now to apply the plugin?",
183
- initialValue: true,
184
- });
185
- return !isCancel(answer) && answer === true;
186
- },
187
- restartDaemon: () => startDaemon({ force: true }),
188
- log: (message) => console.log(message),
189
- });
190
- }
191
- return;
192
- }
193
- case "update": {
194
- if (!arg)
195
- fail("plugin manage update: missing plugin id");
196
- const existing = getPlugin(arg);
197
- if (!existing)
198
- fail(`plugin "${arg}" not found`);
199
- if (!isRunnablePluginRecord(existing)) {
200
- fail(`plugin "${arg}" uses the removed legacy format; reinstall it from an explicit source`);
201
- }
202
- const expectedDigest = digestOption(process.argv);
203
- const result = await installPlugin(existing.spec, (line) => console.log(line), {
204
- expectedId: existing.name,
205
- allowScripts: process.argv.includes("--allow-scripts"),
206
- ...(expectedDigest ? { expectedDigest } : {}),
207
- onConflict: () => true,
208
- approveCapabilities: (manifest, current) => approvePluginCapabilities(manifest, current?.grants ?? [], process.argv.includes("--grant-all")),
209
- });
210
- console.log(`Plugin "${result.name}" updated${result.version ? ` to ${result.version}` : ""}. Run \`rynx restart\` to apply.`);
211
- return;
212
- }
213
- case "uninstall": {
214
- if (!arg)
215
- fail("plugin manage uninstall: missing plugin id");
216
- if (!(await uninstallPlugin(arg, (line) => console.log(line))))
217
- fail(`plugin "${arg}" not found`);
218
- console.log(`Plugin "${arg}" uninstalled. Run \`rynx restart\` to apply.`);
219
- return;
220
- }
221
- case "enable":
222
- case "disable": {
223
- if (!arg)
224
- fail(`plugin manage ${sub}: missing plugin id`);
225
- const plugin = getPlugin(arg);
226
- if (!plugin)
227
- fail(`plugin "${arg}" not found`);
228
- if (!isRunnablePluginRecord(plugin))
229
- fail(`plugin "${arg}" must be reinstalled before it can be enabled`);
230
- setPluginEnabled(arg, sub === "enable");
231
- console.log(`Plugin "${arg}" ${sub}d. Run \`rynx restart\` to apply.`);
232
- return;
233
- }
234
- default:
235
- fail(USAGE);
236
- }
237
- }
238
- async function pluginCommand(args) {
239
- const [pluginId, command] = args;
240
- if (pluginId === "manage") {
241
- await pluginManageCommand(command, args[2]);
242
- return;
243
- }
244
- if (!pluginId)
245
- fail(USAGE);
246
- try {
247
- const result = await runPluginCliCommand(pluginId, args.slice(1));
248
- if (result.code !== 0) {
249
- process.exitCode = result.code;
250
- return;
251
- }
252
- const reload = result.effects.some((effect) => effect.effect === "runtime.reload");
253
- const ensure = result.effects.some((effect) => effect.effect === "runtime.ensure");
254
- if (reload || ensure) {
255
- try {
256
- if (reload)
257
- await reloadResidentPluginRuntime(pluginId);
258
- else
259
- await ensureResidentPluginRuntime(pluginId);
260
- }
261
- catch (error) {
262
- const detail = error instanceof Error ? error.message : String(error);
263
- throw new Error(reload
264
- ? `Plugin "${pluginId}" configuration was saved, but its runtime did not become ready: ${detail}`
265
- : `Plugin "${pluginId}" runtime did not become ready: ${detail}`);
266
- }
267
- if (!args.includes("--json"))
268
- console.log(`Plugin "${pluginId}" runtime is ready.`);
269
- }
270
- }
271
- catch (error) {
272
- if (error instanceof PluginCliUsageError) {
273
- console.log(error.message);
274
- return;
275
- }
276
- throw error;
277
- }
278
- }
279
- async function approvePluginCapabilities(manifest, existing, grantAll) {
280
- const requested = manifest.requestedCapabilities;
281
- const existingSet = new Set(existing);
282
- const newCapabilities = requested.filter((capability) => !existingSet.has(capability));
283
- if (newCapabilities.length === 0)
284
- return requested.filter((capability) => existingSet.has(capability));
285
- if (grantAll)
286
- return requested;
287
- console.log(`Plugin "${manifest.id}" requests host capabilities:`);
288
- for (const capability of newCapabilities)
289
- console.log(` - ${capability}`);
290
- if (!process.stdin.isTTY) {
291
- throw new Error("capability approval required; inspect the list and re-run with --grant-all");
292
- }
293
- const approved = await confirm({
294
- message: `Grant these ${newCapabilities.length} capability/capabilities to "${manifest.id}"?`,
295
- initialValue: false,
296
- });
297
- if (isCancel(approved) || approved !== true)
298
- throw new Error("plugin installation cancelled");
299
- return requested;
300
- }
301
123
  async function agentCommand(sub, name) {
302
124
  switch (sub) {
303
125
  case "list": {
@@ -645,13 +467,6 @@ function optionValue(args, option) {
645
467
  fail(`${option}: missing value`);
646
468
  return value;
647
469
  }
648
- function digestOption(args) {
649
- const expected = optionValue(args, "--expect-digest");
650
- const legacyAlias = optionValue(args, "--expected-digest");
651
- if (expected && legacyAlias)
652
- fail("pass only one digest expectation option");
653
- return expected ?? legacyAlias;
654
- }
655
470
  async function readStdinBounded(maxBytes) {
656
471
  const chunks = [];
657
472
  let bytes = 0;
@@ -699,7 +514,10 @@ async function main() {
699
514
  streamLogs();
700
515
  break;
701
516
  case "plugin":
702
- await pluginCommand(process.argv.slice(3));
517
+ process.exitCode = await runModernCli(["plugin", ...process.argv.slice(3)]);
518
+ break;
519
+ case "market":
520
+ process.exitCode = await runModernCli(["market", ...process.argv.slice(3)]);
703
521
  break;
704
522
  case "agent":
705
523
  await agentCommand(sub, arg);
@@ -1,89 +1,19 @@
1
1
  /**
2
2
  * Assemble the daemon-side {@link ControlPlaneDeps} the server needs to serve
3
- * the control console: db-backed channel + instance mutators, file-backed agent
4
- * specs, channel-type info from the loaded plugins, and a log tail. Plugins are
5
- * managed via the CLI, not here. Keeps `~/.rynx` knowledge in the daemon (the
3
+ * the control console: file-backed agent specs, skills, sessions, and a log
4
+ * tail. Plugin-private configuration is served by each plugin. Keeps `~/.rynx` knowledge in the daemon (the
6
5
  * composition root); the server stays storage-agnostic.
7
6
  */
8
7
  import { readFile } from "node:fs/promises";
9
8
  import { join } from "node:path";
10
9
  import { listAgentSpecs, loadAgentSpec, rynxHome } from "@rynx-ai/core";
11
10
  import { createEmulatorControlDeps } from "@rynx-ai/emulator";
12
- import { normalizePluginConfig } from "@rynx-ai/plugin-sdk";
13
11
  import { removeAgentSpec, writeAgentSpec } from "./agent-file.js";
14
12
  import { getCatalogSkill, installCatalogSkill, listCatalogSkills, readCatalogSkillFile, removeCatalogSkill, } from "./skills-catalog.js";
15
- import { createChannel, getChannel, listChannels, removeChannel, renameChannel, setChannelOptions, } from "./channel-store.js";
16
- import { createInstance, listInstances, removeInstance, setInstanceAgent, setInstanceEnabled, } from "./instance-store.js";
17
- import { getChannelType, listChannelTypes } from "./registry.js";
18
13
  import { createSessionMeta, getSessionMeta, listSessionMetas, removeSessionMeta, setSessionTitle, } from "./session-meta-store.js";
19
14
  import { consumeControlLink } from "./control-link-store.js";
20
15
  export function buildControlDeps() {
21
16
  return {
22
- // ── channel types (from loaded plugins) ──────────────────────────────
23
- listChannelTypes: () => listChannelTypes().map((descriptor) => ({
24
- type: descriptor.type,
25
- configSchema: descriptor.configSchema,
26
- authorize: Boolean(descriptor.authorize),
27
- })),
28
- // ── channels (config + credentials) ──────────────────────────────────
29
- listChannels: () => listChannels(),
30
- createChannel: (input) => {
31
- if (!getChannelType(input.type))
32
- throw new Error(`unknown chat channel type: ${input.type}`);
33
- return {
34
- id: createChannel({
35
- ...input,
36
- ...(input.options !== undefined
37
- ? { options: normalizeChannelOptions(input.type, input.options) }
38
- : {}),
39
- }).id,
40
- };
41
- },
42
- setChannel: (id, patch) => {
43
- let normalizedOptions;
44
- if (patch.options !== undefined) {
45
- const current = getChannel(id);
46
- if (!current)
47
- throw new Error(`channel not found: ${id}`);
48
- normalizedOptions = normalizeChannelOptions(current.type, {
49
- ...(current.options ?? {}),
50
- ...patch.options,
51
- });
52
- }
53
- if (patch.name !== undefined)
54
- renameChannel(id, patch.name);
55
- if (normalizedOptions !== undefined)
56
- setChannelOptions(id, normalizedOptions);
57
- },
58
- removeChannel: (id) => {
59
- removeChannel(id);
60
- },
61
- authorizeChannel: (type, context, emit, signal) => {
62
- const descriptor = getChannelType(type);
63
- if (!descriptor?.authorize) {
64
- throw new Error(`channel type "${type}" has no authorize flow`);
65
- }
66
- return descriptor.authorize(emit, signal, context);
67
- },
68
- // ── instances (channel + agent binding) ──────────────────────────────
69
- listInstancesConfig: () => listInstances().map((i) => ({
70
- id: i.id,
71
- channelId: i.channelId,
72
- channelName: i.channelName,
73
- type: i.type,
74
- agent: i.agent,
75
- enabled: i.enabled,
76
- })),
77
- createInstance: (input) => ({ id: createInstance(input).id }),
78
- setInstance: (id, patch) => {
79
- if (patch.agent !== undefined)
80
- setInstanceAgent(id, patch.agent);
81
- if (patch.enabled !== undefined)
82
- setInstanceEnabled(id, patch.enabled);
83
- },
84
- removeInstance: (id) => {
85
- removeInstance(id);
86
- },
87
17
  // ── declarative agents ───────────────────────────────────────────────
88
18
  listAgents: async () => (await listAgentSpecs()).map((spec) => ({
89
19
  id: spec.id,
@@ -114,13 +44,6 @@ export function buildControlDeps() {
114
44
  tailLogs: (instanceId, lines) => tailLogs(instanceId, lines),
115
45
  };
116
46
  }
117
- function normalizeChannelOptions(type, options) {
118
- const descriptor = getChannelType(type);
119
- if (!descriptor)
120
- throw new Error(`unknown chat channel type: ${type}`);
121
- const schema = descriptor.configSchema ?? [];
122
- return schema.length > 0 ? normalizePluginConfig(schema, options) : { ...options };
123
- }
124
47
  /** Tail the daemon's merged pm2 logs, best-effort filtered by instance id. */
125
48
  async function tailLogs(instanceId, lines) {
126
49
  const dir = join(rynxHome(), "logs");
@@ -13,6 +13,9 @@ export interface StartRynxDaemonServerOptions {
13
13
  writeEndpoint?: (config: AppConfig, managementToken: string) => Promise<void>;
14
14
  /** Test seam for owner-lock failure injection. Production always uses the RYNX_HOME lease. */
15
15
  acquireOwner?: () => Promise<DaemonOwnerLease>;
16
+ /** Runs after the exclusive daemon-owner lock is acquired and before plugin
17
+ * records are loaded or runners are started. */
18
+ preparePlugins?: () => Promise<void>;
16
19
  /** Test/deployment seam for the always-available Direct Runtime socket. */
17
20
  loadDirectConfig?: () => Promise<DirectRuntimeConfig>;
18
21
  /** Test seam for listener lifecycle and bind failures. */
@@ -22,7 +25,7 @@ export interface StartRynxDaemonServerOptions {
22
25
  /** Test seam for the App-only embedded Chromium process supervisor. */
23
26
  createBrowserHostSupervisor?: (options: AppBrowserHostSupervisorOptions) => Pick<AppBrowserHostSupervisor, "ensureConnected" | "restart" | "stop">;
24
27
  }
25
- export declare function startRynxDaemonServer({ config, warn, writeEndpoint, acquireOwner, loadDirectConfig, startDirectServer, createChromeInspectionManager, createBrowserHostSupervisor, }?: StartRynxDaemonServerOptions): Promise<Awaited<ReturnType<typeof startServer>>>;
28
+ export declare function startRynxDaemonServer({ config, warn, writeEndpoint, acquireOwner, preparePlugins, loadDirectConfig, startDirectServer, createChromeInspectionManager, createBrowserHostSupervisor, }?: StartRynxDaemonServerOptions): Promise<Awaited<ReturnType<typeof startServer>>>;
26
29
  export declare function createSupervisedAppSessionBrowserHost(desktop: SessionBrowserHost, supervisor: Pick<AppBrowserHostSupervisor, "ensureConnected" | "restart">): SessionBrowserHost;
27
30
  /**
28
31
  * Resolve the authenticated semantic target inside its owning Runtime and