@poolzin/pool-bot 2026.2.23 → 2026.2.25

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 (235) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/acp/client.js +207 -18
  3. package/dist/acp/secret-file.js +22 -0
  4. package/dist/agents/agent-scope.js +10 -0
  5. package/dist/agents/bash-process-registry.test-helpers.js +29 -0
  6. package/dist/agents/bash-tools.exec-approval-request.js +20 -0
  7. package/dist/agents/bash-tools.exec-host-gateway.js +230 -0
  8. package/dist/agents/bash-tools.exec-host-node.js +235 -0
  9. package/dist/agents/bash-tools.exec-types.js +1 -0
  10. package/dist/agents/bash-tools.process.js +224 -218
  11. package/dist/agents/content-blocks.js +16 -0
  12. package/dist/agents/model-fallback.js +96 -101
  13. package/dist/agents/models-config.providers.js +299 -182
  14. package/dist/agents/pi-embedded-payloads.js +1 -0
  15. package/dist/agents/pi-embedded-runner/run.overflow-compaction.fixture.js +34 -0
  16. package/dist/agents/skills.test-helpers.js +13 -0
  17. package/dist/agents/stable-stringify.js +12 -0
  18. package/dist/agents/subagent-registry.mocks.shared.js +12 -0
  19. package/dist/agents/test-helpers/assistant-message-fixtures.js +29 -0
  20. package/dist/agents/test-helpers/pi-tools-sandbox-context.js +27 -0
  21. package/dist/agents/tool-policy-shared.js +108 -0
  22. package/dist/agents/tools/browser-tool.js +160 -54
  23. package/dist/agents/tools/cron-tool.test-helpers.js +12 -0
  24. package/dist/agents/tools/discord-actions-moderation-shared.js +27 -0
  25. package/dist/agents/tools/image-tool.js +214 -99
  26. package/dist/agents/tools/sessions-history-tool.js +140 -108
  27. package/dist/agents/workspace.js +222 -46
  28. package/dist/auto-reply/commands-registry.js +15 -18
  29. package/dist/auto-reply/fallback-state.js +114 -0
  30. package/dist/auto-reply/model-runtime.js +68 -0
  31. package/dist/auto-reply/reply/agent-runner-execution.js +36 -4
  32. package/dist/auto-reply/reply/agent-runner.js +165 -39
  33. package/dist/auto-reply/reply/commands-setunset-standard.js +13 -0
  34. package/dist/browser/config.js +26 -0
  35. package/dist/browser/navigation-guard.js +31 -0
  36. package/dist/browser/routes/agent.act.js +431 -424
  37. package/dist/browser/routes/agent.shared.js +47 -3
  38. package/dist/browser/routes/agent.snapshot.js +122 -116
  39. package/dist/browser/routes/agent.storage.js +303 -297
  40. package/dist/browser/routes/tabs.js +154 -100
  41. package/dist/browser/server-lifecycle.js +37 -0
  42. package/dist/build-info.json +3 -3
  43. package/dist/channels/allow-from.js +25 -0
  44. package/dist/channels/plugins/account-action-gate.js +13 -0
  45. package/dist/channels/plugins/message-actions.js +10 -0
  46. package/dist/channels/telegram/api.js +18 -0
  47. package/dist/cli/argv.js +84 -21
  48. package/dist/cli/banner.js +2 -1
  49. package/dist/cli/exec-approvals-cli.js +92 -124
  50. package/dist/cli/memory-cli.js +158 -61
  51. package/dist/cli/nodes-cli/register.push.js +63 -0
  52. package/dist/cli/nodes-media-utils.js +21 -0
  53. package/dist/cli/plugins-cli.js +245 -61
  54. package/dist/cli/program/build-program.js +3 -1
  55. package/dist/cli/program/command-registry.js +223 -136
  56. package/dist/cli/program/help.js +43 -12
  57. package/dist/cli/route.js +1 -1
  58. package/dist/cli/test-runtime-capture.js +24 -0
  59. package/dist/commands/agent.js +163 -87
  60. package/dist/commands/channels.mock-harness.js +23 -0
  61. package/dist/commands/daemon-install-runtime-warning.js +11 -0
  62. package/dist/commands/onboard-helpers.js +4 -4
  63. package/dist/commands/sessions.test-helpers.js +61 -0
  64. package/dist/compat/legacy-names.js +2 -2
  65. package/dist/config/commands.js +3 -0
  66. package/dist/config/config.js +1 -1
  67. package/dist/config/env-substitution.js +62 -34
  68. package/dist/config/env-vars.js +9 -0
  69. package/dist/config/io.js +571 -171
  70. package/dist/config/merge-patch.js +50 -4
  71. package/dist/config/redact-snapshot.js +404 -76
  72. package/dist/config/schema.js +58 -570
  73. package/dist/config/validation.js +140 -85
  74. package/dist/config/zod-schema.hooks.js +40 -11
  75. package/dist/config/zod-schema.installs.js +20 -0
  76. package/dist/config/zod-schema.js +8 -7
  77. package/dist/control-ui/assets/{index-HRr1grwl.js → index-Dvkl4Xlx.js} +2 -1
  78. package/dist/control-ui/assets/{index-HRr1grwl.js.map → index-Dvkl4Xlx.js.map} +1 -1
  79. package/dist/control-ui/index.html +1 -1
  80. package/dist/daemon/cmd-argv.js +21 -0
  81. package/dist/daemon/cmd-set.js +58 -0
  82. package/dist/daemon/service-types.js +1 -0
  83. package/dist/discord/monitor/exec-approvals.js +357 -162
  84. package/dist/gateway/auth.js +38 -3
  85. package/dist/gateway/call.js +149 -68
  86. package/dist/gateway/canvas-capability.js +75 -0
  87. package/dist/gateway/control-plane-audit.js +28 -0
  88. package/dist/gateway/control-plane-rate-limit.js +53 -0
  89. package/dist/gateway/events.js +1 -0
  90. package/dist/gateway/hooks.js +109 -54
  91. package/dist/gateway/http-common.js +22 -0
  92. package/dist/gateway/method-scopes.js +169 -0
  93. package/dist/gateway/net.js +23 -0
  94. package/dist/gateway/openresponses-http.js +120 -110
  95. package/dist/gateway/probe-auth.js +2 -0
  96. package/dist/gateway/protocol/index.js +3 -2
  97. package/dist/gateway/protocol/schema/protocol-schemas.js +2 -0
  98. package/dist/gateway/protocol/schema/push.js +18 -0
  99. package/dist/gateway/protocol/schema.js +1 -0
  100. package/dist/gateway/server-http.js +236 -52
  101. package/dist/gateway/server-methods/agent.js +162 -24
  102. package/dist/gateway/server-methods/chat.js +461 -130
  103. package/dist/gateway/server-methods/config.js +193 -150
  104. package/dist/gateway/server-methods/nodes.helpers.js +12 -0
  105. package/dist/gateway/server-methods/nodes.js +251 -69
  106. package/dist/gateway/server-methods/push.js +53 -0
  107. package/dist/gateway/server-reload-handlers.js +2 -3
  108. package/dist/gateway/server-runtime-config.js +5 -0
  109. package/dist/gateway/server-runtime-state.js +2 -0
  110. package/dist/gateway/server-ws-runtime.js +1 -0
  111. package/dist/gateway/server.impl.js +296 -139
  112. package/dist/gateway/session-preview.test-helpers.js +11 -0
  113. package/dist/gateway/startup-auth.js +126 -0
  114. package/dist/gateway/test-helpers.agent-results.js +15 -0
  115. package/dist/gateway/test-helpers.mocks.js +37 -14
  116. package/dist/gateway/test-helpers.server.js +161 -77
  117. package/dist/hooks/bundled/session-memory/handler.js +165 -34
  118. package/dist/hooks/gmail-watcher-lifecycle.js +23 -0
  119. package/dist/infra/archive-path.js +49 -0
  120. package/dist/infra/device-pairing.js +148 -167
  121. package/dist/infra/exec-approvals-allowlist.js +19 -70
  122. package/dist/infra/exec-approvals-analysis.js +44 -17
  123. package/dist/infra/exec-safe-bin-policy.js +269 -0
  124. package/dist/infra/fixed-window-rate-limit.js +33 -0
  125. package/dist/infra/git-root.js +61 -0
  126. package/dist/infra/heartbeat-active-hours.js +2 -2
  127. package/dist/infra/heartbeat-reason.js +40 -0
  128. package/dist/infra/heartbeat-runner.js +72 -32
  129. package/dist/infra/install-source-utils.js +91 -7
  130. package/dist/infra/node-pairing.js +50 -105
  131. package/dist/infra/npm-integrity.js +45 -0
  132. package/dist/infra/npm-pack-install.js +40 -0
  133. package/dist/infra/outbound/channel-adapters.js +20 -7
  134. package/dist/infra/outbound/message-action-runner.js +107 -327
  135. package/dist/infra/outbound/message.js +59 -36
  136. package/dist/infra/outbound/outbound-policy.js +52 -25
  137. package/dist/infra/outbound/outbound-send-service.js +58 -71
  138. package/dist/infra/pairing-files.js +10 -0
  139. package/dist/infra/plain-object.js +9 -0
  140. package/dist/infra/push-apns.js +365 -0
  141. package/dist/infra/restart-sentinel.js +16 -1
  142. package/dist/infra/restart.js +229 -26
  143. package/dist/infra/scp-host.js +54 -0
  144. package/dist/infra/update-startup.js +86 -9
  145. package/dist/media/inbound-path-policy.js +114 -0
  146. package/dist/media/input-files.js +16 -0
  147. package/dist/memory/test-manager.js +8 -0
  148. package/dist/plugin-sdk/temp-path.js +47 -0
  149. package/dist/plugins/discovery.js +217 -23
  150. package/dist/plugins/hook-runner-global.js +16 -0
  151. package/dist/plugins/loader.js +192 -26
  152. package/dist/plugins/logger.js +8 -0
  153. package/dist/plugins/manifest-registry.js +3 -0
  154. package/dist/plugins/path-safety.js +34 -0
  155. package/dist/plugins/registry.js +5 -2
  156. package/dist/plugins/runtime/index.js +271 -206
  157. package/dist/providers/github-copilot-models.js +4 -1
  158. package/dist/security/audit-channel.js +8 -19
  159. package/dist/security/audit-extra.async.js +354 -182
  160. package/dist/security/audit-extra.js +11 -1
  161. package/dist/security/audit-extra.sync.js +340 -33
  162. package/dist/security/audit-fs.js +31 -13
  163. package/dist/security/audit.js +145 -371
  164. package/dist/security/dm-policy-shared.js +24 -0
  165. package/dist/security/external-content.js +20 -8
  166. package/dist/security/fix.js +49 -85
  167. package/dist/security/scan-paths.js +20 -0
  168. package/dist/security/secret-equal.js +3 -7
  169. package/dist/security/windows-acl.js +30 -15
  170. package/dist/shared/node-list-parse.js +13 -0
  171. package/dist/shared/operator-scope-compat.js +37 -0
  172. package/dist/shared/text-chunking.js +29 -0
  173. package/dist/slack/blocks.test-helpers.js +31 -0
  174. package/dist/slack/monitor/mrkdwn.js +8 -0
  175. package/dist/telegram/bot-message-dispatch.js +366 -164
  176. package/dist/telegram/draft-stream.js +30 -7
  177. package/dist/telegram/reasoning-lane-coordinator.js +128 -0
  178. package/dist/terminal/prompt-select-styled.js +9 -0
  179. package/dist/test-utils/command-runner.js +6 -0
  180. package/dist/test-utils/internal-hook-event-payload.js +10 -0
  181. package/dist/test-utils/model-auth-mock.js +12 -0
  182. package/dist/test-utils/provider-usage-fetch.js +14 -0
  183. package/dist/test-utils/temp-home.js +33 -0
  184. package/dist/tui/components/chat-log.js +9 -0
  185. package/dist/tui/tui-command-handlers.js +36 -27
  186. package/dist/tui/tui-event-handlers.js +122 -32
  187. package/dist/tui/tui.js +181 -45
  188. package/dist/utils/mask-api-key.js +10 -0
  189. package/dist/utils/run-with-concurrency.js +39 -0
  190. package/dist/web/media.js +4 -0
  191. package/docs/tools/slash-commands.md +5 -1
  192. package/extensions/bluebubbles/package.json +1 -1
  193. package/extensions/copilot-proxy/package.json +1 -1
  194. package/extensions/diagnostics-otel/package.json +1 -1
  195. package/extensions/discord/package.json +1 -1
  196. package/extensions/feishu/package.json +1 -1
  197. package/extensions/feishu/src/external-keys.ts +19 -0
  198. package/extensions/google-antigravity-auth/package.json +1 -1
  199. package/extensions/google-gemini-cli-auth/package.json +1 -1
  200. package/extensions/googlechat/package.json +1 -1
  201. package/extensions/imessage/package.json +1 -1
  202. package/extensions/irc/package.json +1 -1
  203. package/extensions/line/package.json +1 -1
  204. package/extensions/llm-task/package.json +1 -1
  205. package/extensions/lobster/package.json +1 -1
  206. package/extensions/lobster/src/windows-spawn.ts +193 -0
  207. package/extensions/matrix/CHANGELOG.md +5 -0
  208. package/extensions/matrix/package.json +1 -1
  209. package/extensions/matrix/src/matrix/actions/limits.ts +6 -0
  210. package/extensions/mattermost/package.json +1 -1
  211. package/extensions/mattermost/src/mattermost/reactions.test-helpers.ts +83 -0
  212. package/extensions/memory-core/package.json +1 -1
  213. package/extensions/memory-lancedb/package.json +1 -1
  214. package/extensions/minimax-portal-auth/package.json +1 -1
  215. package/extensions/msteams/CHANGELOG.md +5 -0
  216. package/extensions/msteams/package.json +1 -1
  217. package/extensions/nextcloud-talk/package.json +1 -1
  218. package/extensions/nostr/CHANGELOG.md +5 -0
  219. package/extensions/nostr/package.json +1 -1
  220. package/extensions/open-prose/package.json +1 -1
  221. package/extensions/openai-codex-auth/package.json +1 -1
  222. package/extensions/signal/package.json +1 -1
  223. package/extensions/slack/package.json +1 -1
  224. package/extensions/telegram/package.json +1 -1
  225. package/extensions/tlon/package.json +1 -1
  226. package/extensions/twitch/CHANGELOG.md +5 -0
  227. package/extensions/twitch/package.json +1 -1
  228. package/extensions/voice-call/CHANGELOG.md +5 -0
  229. package/extensions/voice-call/package.json +1 -1
  230. package/extensions/whatsapp/package.json +1 -1
  231. package/extensions/zalo/CHANGELOG.md +5 -0
  232. package/extensions/zalo/package.json +1 -1
  233. package/extensions/zalouser/CHANGELOG.md +5 -0
  234. package/extensions/zalouser/package.json +1 -1
  235. package/package.json +1 -1
@@ -1,17 +1,48 @@
1
1
  import fs from "node:fs";
2
+ import os from "node:os";
2
3
  import path from "node:path";
3
4
  import { loadConfig, writeConfigFile } from "../config/config.js";
5
+ import { resolveStateDir } from "../config/paths.js";
4
6
  import { resolveArchiveKind } from "../infra/archive.js";
5
7
  import { installPluginFromNpmSpec, installPluginFromPath } from "../plugins/install.js";
6
8
  import { recordPluginInstall } from "../plugins/installs.js";
9
+ import { clearPluginManifestRegistryCache } from "../plugins/manifest-registry.js";
7
10
  import { applyExclusiveSlotSelection } from "../plugins/slots.js";
11
+ import { resolvePluginSourceRoots, formatPluginSourceForTable } from "../plugins/source-display.js";
8
12
  import { buildPluginStatusReport } from "../plugins/status.js";
13
+ import { resolveUninstallDirectoryTarget, uninstallPlugin } from "../plugins/uninstall.js";
9
14
  import { updateNpmInstalledPlugins } from "../plugins/update.js";
10
15
  import { defaultRuntime } from "../runtime.js";
11
16
  import { formatDocsLink } from "../terminal/links.js";
12
17
  import { renderTable } from "../terminal/table.js";
13
18
  import { theme } from "../terminal/theme.js";
14
19
  import { resolveUserPath, shortenHomeInString, shortenHomePath } from "../utils.js";
20
+ import { promptYesNo } from "./prompt.js";
21
+ function resolveFileNpmSpecToLocalPath(raw) {
22
+ const trimmed = raw.trim();
23
+ if (!trimmed.toLowerCase().startsWith("file:")) {
24
+ return null;
25
+ }
26
+ const rest = trimmed.slice("file:".length);
27
+ if (!rest) {
28
+ return { ok: false, error: "unsupported file: spec: missing path" };
29
+ }
30
+ if (rest.startsWith("///")) {
31
+ // file:///abs/path -> /abs/path
32
+ return { ok: true, path: rest.slice(2) };
33
+ }
34
+ if (rest.startsWith("//localhost/")) {
35
+ // file://localhost/abs/path -> /abs/path
36
+ return { ok: true, path: rest.slice("//localhost".length) };
37
+ }
38
+ if (rest.startsWith("//")) {
39
+ return {
40
+ ok: false,
41
+ error: 'unsupported file: URL host (expected "file:<path>" or "file:///abs/path")',
42
+ };
43
+ }
44
+ return { ok: true, path: rest };
45
+ }
15
46
  function formatPluginLine(plugin, verbose = false) {
16
47
  const status = plugin.status === "loaded"
17
48
  ? theme.success("loaded")
@@ -33,13 +64,15 @@ function formatPluginLine(plugin, verbose = false) {
33
64
  ` source: ${theme.muted(shortenHomeInString(plugin.source))}`,
34
65
  ` origin: ${plugin.origin}`,
35
66
  ];
36
- if (plugin.version)
67
+ if (plugin.version) {
37
68
  parts.push(` version: ${plugin.version}`);
69
+ }
38
70
  if (plugin.providerIds.length > 0) {
39
71
  parts.push(` providers: ${plugin.providerIds.join(", ")}`);
40
72
  }
41
- if (plugin.error)
73
+ if (plugin.error) {
42
74
  parts.push(theme.error(` error: ${plugin.error}`));
75
+ }
43
76
  return parts.join("\n");
44
77
  }
45
78
  function applySlotSelectionForPlugin(config, pluginId) {
@@ -56,9 +89,31 @@ function applySlotSelectionForPlugin(config, pluginId) {
56
89
  });
57
90
  return { config: result.config, warnings: result.warnings };
58
91
  }
92
+ function createPluginInstallLogger() {
93
+ return {
94
+ info: (msg) => defaultRuntime.log(msg),
95
+ warn: (msg) => defaultRuntime.log(theme.warn(msg)),
96
+ };
97
+ }
98
+ function enablePluginInConfig(config, pluginId) {
99
+ return {
100
+ ...config,
101
+ plugins: {
102
+ ...config.plugins,
103
+ entries: {
104
+ ...config.plugins?.entries,
105
+ [pluginId]: {
106
+ ...config.plugins?.entries?.[pluginId],
107
+ enabled: true,
108
+ },
109
+ },
110
+ },
111
+ };
112
+ }
59
113
  function logSlotWarnings(warnings) {
60
- if (warnings.length === 0)
114
+ if (warnings.length === 0) {
61
115
  return;
116
+ }
62
117
  for (const warning of warnings) {
63
118
  defaultRuntime.log(theme.warn(warning));
64
119
  }
@@ -66,8 +121,8 @@ function logSlotWarnings(warnings) {
66
121
  export function registerPluginsCli(program) {
67
122
  const plugins = program
68
123
  .command("plugins")
69
- .description("Manage Poolbot plugins/extensions")
70
- .addHelpText("after", () => `\n${theme.muted("Docs:")} ${formatDocsLink("/cli/plugins", "docs.molt.bot/cli/plugins")}\n`);
124
+ .description("Manage Pool Bot plugins and extensions")
125
+ .addHelpText("after", () => `\n${theme.muted("Docs:")} ${formatDocsLink("/cli/plugins", "docs.poolbot.ai/cli/plugins")}\n`);
71
126
  plugins
72
127
  .command("list")
73
128
  .description("List discovered plugins")
@@ -96,9 +151,17 @@ export function registerPluginsCli(program) {
96
151
  defaultRuntime.log(`${theme.heading("Plugins")} ${theme.muted(`(${loaded}/${list.length} loaded)`)}`);
97
152
  if (!opts.verbose) {
98
153
  const tableWidth = Math.max(60, (process.stdout.columns ?? 120) - 1);
154
+ const sourceRoots = resolvePluginSourceRoots({
155
+ workspaceDir: report.workspaceDir,
156
+ });
157
+ const usedRoots = new Set();
99
158
  const rows = list.map((plugin) => {
100
159
  const desc = plugin.description ? theme.muted(plugin.description) : "";
101
- const sourceLine = desc ? `${plugin.source}\n${desc}` : plugin.source;
160
+ const formattedSource = formatPluginSourceForTable(plugin, sourceRoots);
161
+ if (formattedSource.rootKey) {
162
+ usedRoots.add(formattedSource.rootKey);
163
+ }
164
+ const sourceLine = desc ? `${formattedSource.value}\n${desc}` : formattedSource.value;
102
165
  return {
103
166
  Name: plugin.name || plugin.id,
104
167
  ID: plugin.name && plugin.name !== plugin.id ? plugin.id : "",
@@ -111,6 +174,20 @@ export function registerPluginsCli(program) {
111
174
  Version: plugin.version ?? "",
112
175
  };
113
176
  });
177
+ if (usedRoots.size > 0) {
178
+ defaultRuntime.log(theme.muted("Source roots:"));
179
+ for (const key of ["stock", "workspace", "global"]) {
180
+ if (!usedRoots.has(key)) {
181
+ continue;
182
+ }
183
+ const dir = sourceRoots[key];
184
+ if (!dir) {
185
+ continue;
186
+ }
187
+ defaultRuntime.log(` ${theme.command(`${key}:`)} ${theme.muted(dir)}`);
188
+ }
189
+ defaultRuntime.log("");
190
+ }
114
191
  defaultRuntime.log(renderTable({
115
192
  width: tableWidth,
116
193
  columns: [
@@ -154,14 +231,16 @@ export function registerPluginsCli(program) {
154
231
  if (plugin.name && plugin.name !== plugin.id) {
155
232
  lines.push(theme.muted(`id: ${plugin.id}`));
156
233
  }
157
- if (plugin.description)
234
+ if (plugin.description) {
158
235
  lines.push(plugin.description);
236
+ }
159
237
  lines.push("");
160
238
  lines.push(`${theme.muted("Status:")} ${plugin.status}`);
161
239
  lines.push(`${theme.muted("Source:")} ${shortenHomeInString(plugin.source)}`);
162
240
  lines.push(`${theme.muted("Origin:")} ${plugin.origin}`);
163
- if (plugin.version)
241
+ if (plugin.version) {
164
242
  lines.push(`${theme.muted("Version:")} ${plugin.version}`);
243
+ }
165
244
  if (plugin.toolNames.length > 0) {
166
245
  lines.push(`${theme.muted("Tools:")} ${plugin.toolNames.join(", ")}`);
167
246
  }
@@ -180,21 +259,27 @@ export function registerPluginsCli(program) {
180
259
  if (plugin.services.length > 0) {
181
260
  lines.push(`${theme.muted("Services:")} ${plugin.services.join(", ")}`);
182
261
  }
183
- if (plugin.error)
262
+ if (plugin.error) {
184
263
  lines.push(`${theme.error("Error:")} ${plugin.error}`);
264
+ }
185
265
  if (install) {
186
266
  lines.push("");
187
267
  lines.push(`${theme.muted("Install:")} ${install.source}`);
188
- if (install.spec)
268
+ if (install.spec) {
189
269
  lines.push(`${theme.muted("Spec:")} ${install.spec}`);
190
- if (install.sourcePath)
270
+ }
271
+ if (install.sourcePath) {
191
272
  lines.push(`${theme.muted("Source path:")} ${shortenHomePath(install.sourcePath)}`);
192
- if (install.installPath)
273
+ }
274
+ if (install.installPath) {
193
275
  lines.push(`${theme.muted("Install path:")} ${shortenHomePath(install.installPath)}`);
194
- if (install.version)
276
+ }
277
+ if (install.version) {
195
278
  lines.push(`${theme.muted("Recorded version:")} ${install.version}`);
196
- if (install.installedAt)
279
+ }
280
+ if (install.installedAt) {
197
281
  lines.push(`${theme.muted("Installed at:")} ${install.installedAt}`);
282
+ }
198
283
  }
199
284
  defaultRuntime.log(lines.join("\n"));
200
285
  });
@@ -245,13 +330,133 @@ export function registerPluginsCli(program) {
245
330
  await writeConfigFile(next);
246
331
  defaultRuntime.log(`Disabled plugin "${id}". Restart the gateway to apply.`);
247
332
  });
333
+ plugins
334
+ .command("uninstall")
335
+ .description("Uninstall a plugin")
336
+ .argument("<id>", "Plugin id")
337
+ .option("--keep-files", "Keep installed files on disk", false)
338
+ .option("--keep-config", "Deprecated alias for --keep-files", false)
339
+ .option("--force", "Skip confirmation prompt", false)
340
+ .option("--dry-run", "Show what would be removed without making changes", false)
341
+ .action(async (id, opts) => {
342
+ const cfg = loadConfig();
343
+ const report = buildPluginStatusReport({ config: cfg });
344
+ const extensionsDir = path.join(resolveStateDir(process.env, os.homedir), "extensions");
345
+ const keepFiles = Boolean(opts.keepFiles || opts.keepConfig);
346
+ if (opts.keepConfig) {
347
+ defaultRuntime.log(theme.warn("`--keep-config` is deprecated, use `--keep-files`."));
348
+ }
349
+ // Find plugin by id or name
350
+ const plugin = report.plugins.find((p) => p.id === id || p.name === id);
351
+ const pluginId = plugin?.id ?? id;
352
+ // Check if plugin exists in config
353
+ const hasEntry = pluginId in (cfg.plugins?.entries ?? {});
354
+ const hasInstall = pluginId in (cfg.plugins?.installs ?? {});
355
+ if (!hasEntry && !hasInstall) {
356
+ if (plugin) {
357
+ defaultRuntime.error(`Plugin "${pluginId}" is not managed by plugins config/install records and cannot be uninstalled.`);
358
+ }
359
+ else {
360
+ defaultRuntime.error(`Plugin not found: ${id}`);
361
+ }
362
+ process.exit(1);
363
+ }
364
+ const install = cfg.plugins?.installs?.[pluginId];
365
+ const isLinked = install?.source === "path";
366
+ // Build preview of what will be removed
367
+ const preview = [];
368
+ if (hasEntry) {
369
+ preview.push("config entry");
370
+ }
371
+ if (hasInstall) {
372
+ preview.push("install record");
373
+ }
374
+ if (cfg.plugins?.allow?.includes(pluginId)) {
375
+ preview.push("allowlist entry");
376
+ }
377
+ if (isLinked &&
378
+ install?.sourcePath &&
379
+ cfg.plugins?.load?.paths?.includes(install.sourcePath)) {
380
+ preview.push("load path");
381
+ }
382
+ if (cfg.plugins?.slots?.memory === pluginId) {
383
+ preview.push(`memory slot (will reset to "memory-core")`);
384
+ }
385
+ const deleteTarget = !keepFiles
386
+ ? resolveUninstallDirectoryTarget({
387
+ pluginId,
388
+ hasInstall,
389
+ installRecord: install,
390
+ extensionsDir,
391
+ })
392
+ : null;
393
+ if (deleteTarget) {
394
+ preview.push(`directory: ${shortenHomePath(deleteTarget)}`);
395
+ }
396
+ const pluginName = plugin?.name || pluginId;
397
+ defaultRuntime.log(`Plugin: ${theme.command(pluginName)}${pluginName !== pluginId ? theme.muted(` (${pluginId})`) : ""}`);
398
+ defaultRuntime.log(`Will remove: ${preview.length > 0 ? preview.join(", ") : "(nothing)"}`);
399
+ if (opts.dryRun) {
400
+ defaultRuntime.log(theme.muted("Dry run, no changes made."));
401
+ return;
402
+ }
403
+ if (!opts.force) {
404
+ const confirmed = await promptYesNo(`Uninstall plugin "${pluginId}"?`);
405
+ if (!confirmed) {
406
+ defaultRuntime.log("Cancelled.");
407
+ return;
408
+ }
409
+ }
410
+ const result = await uninstallPlugin({
411
+ config: cfg,
412
+ pluginId,
413
+ deleteFiles: !keepFiles,
414
+ extensionsDir,
415
+ });
416
+ if (!result.ok) {
417
+ defaultRuntime.error(result.error);
418
+ process.exit(1);
419
+ }
420
+ for (const warning of result.warnings) {
421
+ defaultRuntime.log(theme.warn(warning));
422
+ }
423
+ await writeConfigFile(result.config);
424
+ const removed = [];
425
+ if (result.actions.entry) {
426
+ removed.push("config entry");
427
+ }
428
+ if (result.actions.install) {
429
+ removed.push("install record");
430
+ }
431
+ if (result.actions.allowlist) {
432
+ removed.push("allowlist");
433
+ }
434
+ if (result.actions.loadPath) {
435
+ removed.push("load path");
436
+ }
437
+ if (result.actions.memorySlot) {
438
+ removed.push("memory slot");
439
+ }
440
+ if (result.actions.directory) {
441
+ removed.push("directory");
442
+ }
443
+ defaultRuntime.log(`Uninstalled plugin "${pluginId}". Removed: ${removed.length > 0 ? removed.join(", ") : "nothing"}.`);
444
+ defaultRuntime.log("Restart the gateway to apply changes.");
445
+ });
248
446
  plugins
249
447
  .command("install")
250
448
  .description("Install a plugin (path, archive, or npm spec)")
251
449
  .argument("<path-or-spec>", "Path (.ts/.js/.zip/.tgz/.tar.gz) or an npm package spec")
252
450
  .option("-l, --link", "Link a local path instead of copying", false)
451
+ .option("--pin", "Record npm installs as exact resolved <name>@<version>", false)
253
452
  .action(async (raw, opts) => {
254
- const resolved = resolveUserPath(raw);
453
+ const fileSpec = resolveFileNpmSpecToLocalPath(raw);
454
+ if (fileSpec && !fileSpec.ok) {
455
+ defaultRuntime.error(fileSpec.error);
456
+ process.exit(1);
457
+ }
458
+ const normalized = fileSpec && fileSpec.ok ? fileSpec.path : raw;
459
+ const resolved = resolveUserPath(normalized);
255
460
  const cfg = loadConfig();
256
461
  if (fs.existsSync(resolved)) {
257
462
  if (opts.link) {
@@ -262,7 +467,7 @@ export function registerPluginsCli(program) {
262
467
  defaultRuntime.error(probe.error);
263
468
  process.exit(1);
264
469
  }
265
- let next = {
470
+ let next = enablePluginInConfig({
266
471
  ...cfg,
267
472
  plugins: {
268
473
  ...cfg.plugins,
@@ -270,15 +475,8 @@ export function registerPluginsCli(program) {
270
475
  ...cfg.plugins?.load,
271
476
  paths: merged,
272
477
  },
273
- entries: {
274
- ...cfg.plugins?.entries,
275
- [probe.pluginId]: {
276
- ...cfg.plugins?.entries?.[probe.pluginId],
277
- enabled: true,
278
- },
279
- },
280
478
  },
281
- };
479
+ }, probe.pluginId);
282
480
  next = recordPluginInstall(next, {
283
481
  pluginId: probe.pluginId,
284
482
  source: "path",
@@ -296,28 +494,16 @@ export function registerPluginsCli(program) {
296
494
  }
297
495
  const result = await installPluginFromPath({
298
496
  path: resolved,
299
- logger: {
300
- info: (msg) => defaultRuntime.log(msg),
301
- warn: (msg) => defaultRuntime.log(theme.warn(msg)),
302
- },
497
+ logger: createPluginInstallLogger(),
303
498
  });
304
499
  if (!result.ok) {
305
500
  defaultRuntime.error(result.error);
306
501
  process.exit(1);
307
502
  }
308
- let next = {
309
- ...cfg,
310
- plugins: {
311
- ...cfg.plugins,
312
- entries: {
313
- ...cfg.plugins?.entries,
314
- [result.pluginId]: {
315
- ...cfg.plugins?.entries?.[result.pluginId],
316
- enabled: true,
317
- },
318
- },
319
- },
320
- };
503
+ // Plugin CLI registrars may have warmed the manifest registry cache before install;
504
+ // force a rescan so config validation sees the freshly installed plugin.
505
+ clearPluginManifestRegistryCache();
506
+ let next = enablePluginInConfig(cfg, result.pluginId);
321
507
  const source = resolveArchiveKind(resolved) ? "archive" : "path";
322
508
  next = recordPluginInstall(next, {
323
509
  pluginId: result.pluginId,
@@ -355,32 +541,28 @@ export function registerPluginsCli(program) {
355
541
  }
356
542
  const result = await installPluginFromNpmSpec({
357
543
  spec: raw,
358
- logger: {
359
- info: (msg) => defaultRuntime.log(msg),
360
- warn: (msg) => defaultRuntime.log(theme.warn(msg)),
361
- },
544
+ logger: createPluginInstallLogger(),
362
545
  });
363
546
  if (!result.ok) {
364
547
  defaultRuntime.error(result.error);
365
548
  process.exit(1);
366
549
  }
367
- let next = {
368
- ...cfg,
369
- plugins: {
370
- ...cfg.plugins,
371
- entries: {
372
- ...cfg.plugins?.entries,
373
- [result.pluginId]: {
374
- ...cfg.plugins?.entries?.[result.pluginId],
375
- enabled: true,
376
- },
377
- },
378
- },
379
- };
550
+ // Ensure config validation sees newly installed plugin(s) even if the cache was warmed at startup.
551
+ clearPluginManifestRegistryCache();
552
+ let next = enablePluginInConfig(cfg, result.pluginId);
553
+ // PB's installPluginFromNpmSpec does not return npmResolution metadata yet;
554
+ // record what we can (version from manifest) and store the raw spec.
555
+ const recordSpec = opts.pin && result.version ? `${result.pluginId}@${result.version}` : raw;
556
+ if (opts.pin && !result.version) {
557
+ defaultRuntime.log(theme.warn("Could not resolve exact npm version for --pin; storing original npm spec."));
558
+ }
559
+ if (opts.pin && result.version) {
560
+ defaultRuntime.log(`Pinned npm install record to ${result.pluginId}@${result.version}.`);
561
+ }
380
562
  next = recordPluginInstall(next, {
381
563
  pluginId: result.pluginId,
382
564
  source: "npm",
383
- spec: raw,
565
+ spec: recordSpec,
384
566
  installPath: result.targetDir,
385
567
  version: result.version,
386
568
  });
@@ -417,6 +599,7 @@ export function registerPluginsCli(program) {
417
599
  info: (msg) => defaultRuntime.log(msg),
418
600
  warn: (msg) => defaultRuntime.log(theme.warn(msg)),
419
601
  },
602
+ // NOTE: onIntegrityDrift is available in OC but not yet ported to PB's update module.
420
603
  });
421
604
  for (const outcome of result.outcomes) {
422
605
  if (outcome.status === "error") {
@@ -453,15 +636,16 @@ export function registerPluginsCli(program) {
453
636
  }
454
637
  }
455
638
  if (diags.length > 0) {
456
- if (lines.length > 0)
639
+ if (lines.length > 0) {
457
640
  lines.push("");
641
+ }
458
642
  lines.push(theme.warn("Diagnostics:"));
459
643
  for (const diag of diags) {
460
644
  const target = diag.pluginId ? `${diag.pluginId}: ` : "";
461
645
  lines.push(`- ${target}${diag.message}`);
462
646
  }
463
647
  }
464
- const docs = formatDocsLink("/plugin", "docs.molt.bot/plugin");
648
+ const docs = formatDocsLink("/plugin", "docs.poolbot.ai/plugin");
465
649
  lines.push("");
466
650
  lines.push(`${theme.muted("Docs:")} ${docs}`);
467
651
  defaultRuntime.log(lines.join("\n"));
@@ -1,12 +1,14 @@
1
1
  import { Command } from "commander";
2
- import { createProgramContext } from "./context.js";
3
2
  import { registerProgramCommands } from "./command-registry.js";
3
+ import { createProgramContext } from "./context.js";
4
4
  import { configureProgramHelp } from "./help.js";
5
5
  import { registerPreActionHooks } from "./preaction.js";
6
+ import { setProgramContext } from "./program-context.js";
6
7
  export function buildProgram() {
7
8
  const program = new Command();
8
9
  const ctx = createProgramContext();
9
10
  const argv = process.argv;
11
+ setProgramContext(program, ctx);
10
12
  configureProgramHelp(program, ctx);
11
13
  registerPreActionHooks(program, ctx.programVersion);
12
14
  registerProgramCommands(program, ctx, argv);