@phuetz/code-buddy 1.0.0 → 1.2.0

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 (312) hide show
  1. package/README.md +160 -164
  2. package/dist/agent/autonomous/agentic-coding-contract.d.ts +6 -6
  3. package/dist/agent/codebuddy-agent.d.ts +1 -1
  4. package/dist/agent/codebuddy-agent.js +101 -13
  5. package/dist/agent/execution/agent-executor.js +122 -12
  6. package/dist/agent/execution/tool-dependency-graph.js +7 -0
  7. package/dist/agent/execution/tool-hooks.d.ts +6 -0
  8. package/dist/agent/execution/tool-hooks.js +13 -5
  9. package/dist/agent/execution/tool-selection-strategy.d.ts +11 -2
  10. package/dist/agent/execution/tool-selection-strategy.js +67 -10
  11. package/dist/agent/extended-thinking.d.ts +8 -0
  12. package/dist/agent/extended-thinking.js +27 -0
  13. package/dist/agent/hermes-browser-backends.js +151 -11
  14. package/dist/agent/hermes-claw-migrate.d.ts +59 -0
  15. package/dist/agent/hermes-claw-migrate.js +427 -26
  16. package/dist/agent/hermes-parity-manifest.js +78 -71
  17. package/dist/agent/hermes-runtime-lifecycle.d.ts +67 -0
  18. package/dist/agent/hermes-runtime-lifecycle.js +468 -0
  19. package/dist/agent/lesson-auto-proposer.js +10 -0
  20. package/dist/agent/middleware/index.d.ts +1 -0
  21. package/dist/agent/middleware/index.js +1 -0
  22. package/dist/agent/middleware/session-duration.d.ts +36 -0
  23. package/dist/agent/middleware/session-duration.js +78 -0
  24. package/dist/agent/middleware/visual-validation-middleware.d.ts +17 -0
  25. package/dist/agent/middleware/visual-validation-middleware.js +67 -0
  26. package/dist/agent/model-benchmark.d.ts +77 -0
  27. package/dist/agent/model-benchmark.js +309 -0
  28. package/dist/agent/model-tier.d.ts +14 -0
  29. package/dist/agent/model-tier.js +71 -0
  30. package/dist/agent/multi-agent/agents/orchestrator-agent.js +17 -6
  31. package/dist/agent/multi-agent/multi-agent-system.js +3 -1
  32. package/dist/agent/multi-agent/session-fleet-bridge.d.ts +2 -0
  33. package/dist/agent/multi-agent/session-fleet-bridge.js +9 -1
  34. package/dist/agent/multi-agent/session-tools.js +22 -5
  35. package/dist/agent/reasoning/mcts.js +20 -6
  36. package/dist/agent/session-end-flush.d.ts +71 -0
  37. package/dist/agent/session-end-flush.js +233 -0
  38. package/dist/agent/streaming/streaming-handler.d.ts +7 -0
  39. package/dist/agent/streaming/streaming-handler.js +8 -1
  40. package/dist/agent/tool-executor.js +1 -0
  41. package/dist/agent/tool-handler.d.ts +5 -0
  42. package/dist/agent/tool-handler.js +130 -4
  43. package/dist/browser-automation/browser-operator-executor.js +15 -0
  44. package/dist/browser-automation/browser-use-runner.d.ts +96 -0
  45. package/dist/browser-automation/browser-use-runner.js +492 -0
  46. package/dist/browser-automation/camofox-runner.d.ts +107 -0
  47. package/dist/browser-automation/camofox-runner.js +287 -0
  48. package/dist/channels/dingtalk/index.js +3 -0
  49. package/dist/channels/discord/client.d.ts +9 -0
  50. package/dist/channels/discord/client.js +12 -0
  51. package/dist/channels/feishu/index.d.ts +167 -1
  52. package/dist/channels/feishu/index.js +432 -7
  53. package/dist/channels/gateway-lifecycle.d.ts +132 -0
  54. package/dist/channels/gateway-lifecycle.js +219 -0
  55. package/dist/channels/google-chat/index.d.ts +5 -1
  56. package/dist/channels/google-chat/index.js +5 -1
  57. package/dist/channels/imessage/index.d.ts +14 -0
  58. package/dist/channels/imessage/index.js +64 -17
  59. package/dist/channels/index.d.ts +4 -0
  60. package/dist/channels/index.js +4 -0
  61. package/dist/channels/irc/index.d.ts +73 -3
  62. package/dist/channels/irc/index.js +446 -11
  63. package/dist/channels/line/index.js +3 -0
  64. package/dist/channels/mattermost/index.d.ts +51 -4
  65. package/dist/channels/mattermost/index.js +303 -20
  66. package/dist/channels/nextcloud-talk/index.d.ts +89 -8
  67. package/dist/channels/nextcloud-talk/index.js +367 -16
  68. package/dist/channels/nostr/index.d.ts +121 -1
  69. package/dist/channels/nostr/index.js +396 -8
  70. package/dist/channels/ntfy/index.js +3 -0
  71. package/dist/channels/qq/index.js +3 -0
  72. package/dist/channels/slash-parity.d.ts +117 -0
  73. package/dist/channels/slash-parity.js +185 -0
  74. package/dist/channels/synology-chat/index.js +3 -0
  75. package/dist/channels/teams/index.d.ts +3 -0
  76. package/dist/channels/teams/index.js +3 -0
  77. package/dist/channels/telegram/client.d.ts +9 -0
  78. package/dist/channels/telegram/client.js +12 -0
  79. package/dist/channels/twilio-voice/index.js +3 -0
  80. package/dist/channels/webchat/index.d.ts +7 -1
  81. package/dist/channels/webchat/index.js +7 -1
  82. package/dist/channels/wecom/index.js +3 -0
  83. package/dist/channels/weixin/index.js +3 -0
  84. package/dist/channels/zalo/index.js +3 -0
  85. package/dist/cli/config-loader.js +1 -1
  86. package/dist/codebuddy/client.d.ts +30 -1
  87. package/dist/codebuddy/client.js +167 -20
  88. package/dist/codebuddy/providers/provider-chatgpt-responses.d.ts +5 -0
  89. package/dist/codebuddy/providers/provider-chatgpt-responses.js +1 -0
  90. package/dist/codebuddy/providers/provider-gemini-native.js +1 -1
  91. package/dist/codebuddy/providers/provider-openai-compat.d.ts +5 -0
  92. package/dist/codebuddy/providers/provider-openai-compat.js +82 -2
  93. package/dist/codebuddy/tool-definitions/computer-control-tools.js +32 -2
  94. package/dist/codebuddy/tool-definitions/core-tools.js +3 -3
  95. package/dist/codebuddy/tool-definitions/index.d.ts +2 -1
  96. package/dist/codebuddy/tool-definitions/index.js +2 -1
  97. package/dist/codebuddy/tool-definitions/multimodal-tools.d.ts +1 -0
  98. package/dist/codebuddy/tool-definitions/multimodal-tools.js +44 -1
  99. package/dist/codebuddy/tool-definitions/windows-tools.d.ts +2 -0
  100. package/dist/codebuddy/tool-definitions/windows-tools.js +34 -0
  101. package/dist/codebuddy/tools.d.ts +1 -1
  102. package/dist/codebuddy/tools.js +27 -24
  103. package/dist/commands/cli/hermes-commands.js +108 -0
  104. package/dist/commands/cli/native-engine-commands.js +107 -3
  105. package/dist/commands/cli/utility-commands.js +45 -0
  106. package/dist/commands/enhanced-command-handler.js +5 -0
  107. package/dist/commands/goal-cli.d.ts +71 -0
  108. package/dist/commands/goal-cli.js +280 -0
  109. package/dist/commands/handlers/goal-handler.d.ts +34 -0
  110. package/dist/commands/handlers/goal-handler.js +155 -0
  111. package/dist/commands/handlers/index.d.ts +1 -0
  112. package/dist/commands/handlers/index.js +2 -0
  113. package/dist/commands/handlers/infra-handlers.js +36 -0
  114. package/dist/commands/handlers/memory-handlers.js +96 -1
  115. package/dist/commands/headless-slash.d.ts +2 -0
  116. package/dist/commands/headless-slash.js +12 -0
  117. package/dist/commands/llm-provider-resolution.d.ts +5 -3
  118. package/dist/commands/llm-provider-resolution.js +87 -33
  119. package/dist/commands/ollama.d.ts +25 -0
  120. package/dist/commands/ollama.js +100 -0
  121. package/dist/commands/provider.d.ts +5 -0
  122. package/dist/commands/provider.js +106 -55
  123. package/dist/commands/slash/builtin-commands.js +20 -0
  124. package/dist/commands/spec-next.js +2 -1
  125. package/dist/commands/spec-plan.js +5 -16
  126. package/dist/commands/tunnel.d.ts +2 -0
  127. package/dist/commands/tunnel.js +48 -0
  128. package/dist/config/config-resolver.d.ts +2 -1
  129. package/dist/config/config-resolver.js +54 -41
  130. package/dist/config/constants.d.ts +28 -0
  131. package/dist/config/constants.js +7 -0
  132. package/dist/config/env-schema.js +770 -0
  133. package/dist/config/feature-flags.js +7 -0
  134. package/dist/config/model-tools.js +4 -4
  135. package/dist/config/toml-config.d.ts +16 -0
  136. package/dist/config/toml-config.js +3 -0
  137. package/dist/context/context-manager-v2.d.ts +39 -0
  138. package/dist/context/context-manager-v2.js +91 -0
  139. package/dist/daemon/agent-task-executor.js +12 -1
  140. package/dist/daemon/autonomous-daemon.d.ts +1 -1
  141. package/dist/daemon/autonomous-daemon.js +5 -3
  142. package/dist/daemon/autonomous-loop.d.ts +21 -1
  143. package/dist/daemon/autonomous-loop.js +69 -0
  144. package/dist/daemon/colab-goal.d.ts +38 -0
  145. package/dist/daemon/colab-goal.js +81 -0
  146. package/dist/daemon/cron-agent-bridge.d.ts +12 -3
  147. package/dist/daemon/cron-agent-bridge.js +25 -9
  148. package/dist/desktop/codebuddy-engine-adapter.d.ts +32 -0
  149. package/dist/desktop/codebuddy-engine-adapter.js +257 -80
  150. package/dist/desktop/engine-adapter.d.ts +14 -0
  151. package/dist/desktop-automation/automation-manager.js +16 -0
  152. package/dist/desktop-automation/omniparser-runner.d.ts +63 -0
  153. package/dist/desktop-automation/omniparser-runner.js +115 -0
  154. package/dist/desktop-automation/smart-snapshot.d.ts +11 -0
  155. package/dist/desktop-automation/smart-snapshot.js +72 -5
  156. package/dist/doctor/index.js +40 -23
  157. package/dist/events/types.d.ts +11 -0
  158. package/dist/fleet/colab-store.d.ts +21 -0
  159. package/dist/fleet/colab-store.js +28 -0
  160. package/dist/fleet/peer-session-bridge.d.ts +1 -1
  161. package/dist/fleet/peer-session-bridge.js +243 -2
  162. package/dist/fleet/peer-session-store.d.ts +3 -0
  163. package/dist/fleet/peer-tool-bridge.js +14 -0
  164. package/dist/fleet/privacy-lint.d.ts +8 -0
  165. package/dist/fleet/privacy-lint.js +22 -0
  166. package/dist/goals/goal-decomposer.d.ts +27 -0
  167. package/dist/goals/goal-decomposer.js +293 -0
  168. package/dist/goals/goal-judge-client.d.ts +15 -0
  169. package/dist/goals/goal-judge-client.js +45 -0
  170. package/dist/goals/goal-judge.d.ts +38 -0
  171. package/dist/goals/goal-judge.js +158 -0
  172. package/dist/goals/goal-loop.d.ts +37 -0
  173. package/dist/goals/goal-loop.js +101 -0
  174. package/dist/goals/goal-manager.d.ts +77 -0
  175. package/dist/goals/goal-manager.js +289 -0
  176. package/dist/goals/goal-state.d.ts +99 -0
  177. package/dist/goals/goal-state.js +315 -0
  178. package/dist/goals/goal-store.d.ts +28 -0
  179. package/dist/goals/goal-store.js +87 -0
  180. package/dist/goals/index.d.ts +5 -0
  181. package/dist/goals/index.js +6 -0
  182. package/dist/hooks/use-input-handler.js +36 -1
  183. package/dist/hooks/user-hooks.js +17 -3
  184. package/dist/index.js +506 -25
  185. package/dist/input/text-to-speech.d.ts +2 -6
  186. package/dist/input/text-to-speech.js +2 -27
  187. package/dist/integrations/screenpipe/screenpipe-client.d.ts +10 -0
  188. package/dist/integrations/screenpipe/screenpipe-client.js +12 -2
  189. package/dist/integrations/tailscale.d.ts +13 -0
  190. package/dist/integrations/tailscale.js +87 -34
  191. package/dist/knowledge/workspace-indexer.js +53 -9
  192. package/dist/mcp/client.js +1 -0
  193. package/dist/mcp/mcp-desktop-tools.d.ts +23 -0
  194. package/dist/mcp/mcp-desktop-tools.js +158 -0
  195. package/dist/mcp/mcp-server.js +2 -0
  196. package/dist/memory/enhanced-memory.d.ts +10 -0
  197. package/dist/memory/enhanced-memory.js +33 -2
  198. package/dist/memory/index.d.ts +3 -1
  199. package/dist/memory/index.js +4 -1
  200. package/dist/memory/memory-auto-proposer.d.ts +23 -0
  201. package/dist/memory/memory-auto-proposer.js +308 -0
  202. package/dist/memory/memory-candidate-queue.d.ts +92 -0
  203. package/dist/memory/memory-candidate-queue.js +261 -0
  204. package/dist/memory/persistent-memory.d.ts +45 -2
  205. package/dist/memory/persistent-memory.js +236 -40
  206. package/dist/ml/bayesian-qualifier.d.ts +1 -1
  207. package/dist/ml/bayesian-qualifier.js +35 -2
  208. package/dist/observability/run-store.d.ts +1 -1
  209. package/dist/openclaw/gateway-bridge.js +5 -0
  210. package/dist/plugins/marketplace.d.ts +1 -0
  211. package/dist/plugins/marketplace.js +7 -0
  212. package/dist/prompts/execution-discipline.d.ts +14 -0
  213. package/dist/prompts/execution-discipline.js +29 -0
  214. package/dist/prompts/variation-injector.js +9 -2
  215. package/dist/protocols/acp/acp-agentic-runner.d.ts +7 -6
  216. package/dist/protocols/acp/acp-agentic-runner.js +115 -18
  217. package/dist/protocols/acp/acp-session-store.d.ts +23 -0
  218. package/dist/protocols/acp/acp-session-store.js +77 -0
  219. package/dist/protocols/acp/acp-stdio-server.d.ts +7 -0
  220. package/dist/protocols/acp/acp-stdio-server.js +71 -16
  221. package/dist/providers/active-llm-registry.d.ts +37 -0
  222. package/dist/providers/active-llm-registry.js +186 -0
  223. package/dist/providers/auxiliary-provider.d.ts +25 -0
  224. package/dist/providers/auxiliary-provider.js +192 -0
  225. package/dist/providers/codex-oauth.d.ts +1 -1
  226. package/dist/providers/codex-oauth.js +27 -4
  227. package/dist/providers/index.d.ts +3 -0
  228. package/dist/providers/index.js +4 -0
  229. package/dist/providers/provider-catalog.d.ts +70 -0
  230. package/dist/providers/provider-catalog.js +738 -0
  231. package/dist/providers/provider-fallback.d.ts +35 -0
  232. package/dist/providers/provider-fallback.js +171 -0
  233. package/dist/providers/xai-oauth.d.ts +128 -0
  234. package/dist/providers/xai-oauth.js +735 -0
  235. package/dist/scheduler/cron-scheduler.d.ts +15 -3
  236. package/dist/scheduler/cron-scheduler.js +53 -7
  237. package/dist/scheduler/script-runner.d.ts +2 -0
  238. package/dist/scheduler/script-runner.js +19 -4
  239. package/dist/scheduler/watchdog-handlers.js +7 -20
  240. package/dist/search/usearch-index.js +7 -2
  241. package/dist/security/tool-policy/tool-groups.js +2 -0
  242. package/dist/server/index.js +17 -2
  243. package/dist/server/routes/mobile.d.ts +12 -4
  244. package/dist/server/routes/mobile.js +116 -1
  245. package/dist/server/tls-config.d.ts +35 -0
  246. package/dist/server/tls-config.js +142 -0
  247. package/dist/server/tunnel-manager.d.ts +20 -0
  248. package/dist/server/tunnel-manager.js +58 -0
  249. package/dist/server/websocket/fleet-bridge.d.ts +13 -1
  250. package/dist/server/websocket/fleet-bridge.js +16 -0
  251. package/dist/services/prompt-builder.d.ts +1 -0
  252. package/dist/services/prompt-builder.js +49 -8
  253. package/dist/shared/engine-types.d.ts +15 -1
  254. package/dist/sidecar/sidecar-bridge.d.ts +1 -0
  255. package/dist/sidecar/sidecar-bridge.js +21 -10
  256. package/dist/spec/spec-store.js +8 -1
  257. package/dist/talk-mode/providers/audioreader-tts.d.ts +1 -1
  258. package/dist/talk-mode/providers/audioreader-tts.js +8 -8
  259. package/dist/templates/project-scaffolding.js +3 -2
  260. package/dist/themes/theme-schema.d.ts +10 -10
  261. package/dist/tools/application-profiles.js +38 -0
  262. package/dist/tools/bash/bash-tool.d.ts +1 -0
  263. package/dist/tools/bash/bash-tool.js +16 -2
  264. package/dist/tools/bash/command-validator.js +3 -0
  265. package/dist/tools/bash/rtk-rewrite.d.ts +16 -0
  266. package/dist/tools/bash/rtk-rewrite.js +100 -0
  267. package/dist/tools/bash/streaming-executor.js +14 -1
  268. package/dist/tools/computer-control-tool.d.ts +16 -1
  269. package/dist/tools/computer-control-tool.js +317 -5
  270. package/dist/tools/document-generator.d.ts +14 -0
  271. package/dist/tools/document-generator.js +79 -0
  272. package/dist/tools/execute-code-rpc-invoker.d.ts +26 -2
  273. package/dist/tools/execute-code-rpc-invoker.js +46 -5
  274. package/dist/tools/metadata.js +28 -0
  275. package/dist/tools/office-macro-tool.d.ts +10 -0
  276. package/dist/tools/office-macro-tool.js +93 -0
  277. package/dist/tools/registry/index.d.ts +2 -1
  278. package/dist/tools/registry/index.js +2 -1
  279. package/dist/tools/registry/memory-tools.d.ts +20 -0
  280. package/dist/tools/registry/memory-tools.js +225 -3
  281. package/dist/tools/registry/multimodal-tools.js +2 -2
  282. package/dist/tools/registry/vision-tools.d.ts +33 -0
  283. package/dist/tools/registry/vision-tools.js +308 -6
  284. package/dist/tools/registry/windows-tools.d.ts +2 -0
  285. package/dist/tools/registry/windows-tools.js +47 -0
  286. package/dist/tools/route-peer-tool.js +14 -0
  287. package/dist/tools/screenshot-tool.js +14 -2
  288. package/dist/tools/text-to-speech-tool.d.ts +1 -1
  289. package/dist/tools/text-to-speech-tool.js +2 -12
  290. package/dist/tools/tool-selector.js +3 -59
  291. package/dist/utils/config-validation/schema.d.ts +40 -7
  292. package/dist/utils/config-validation/schema.js +130 -1
  293. package/dist/utils/cost-tracker.js +21 -1
  294. package/dist/utils/disk-guard.d.ts +180 -0
  295. package/dist/utils/disk-guard.js +404 -0
  296. package/dist/utils/interactive-setup.js +35 -2
  297. package/dist/utils/model-utils.js +11 -2
  298. package/dist/utils/output-sanitizer.js +11 -0
  299. package/dist/utils/provider-detector.d.ts +8 -7
  300. package/dist/utils/provider-detector.js +19 -77
  301. package/dist/utils/settings-manager.d.ts +7 -0
  302. package/dist/utils/token-display.d.ts +3 -3
  303. package/dist/utils/token-display.js +22 -4
  304. package/dist/wizard/onboarding.d.ts +39 -0
  305. package/dist/wizard/onboarding.js +433 -21
  306. package/dist/wizard/provider-onboarding.d.ts +8 -23
  307. package/dist/wizard/provider-onboarding.js +39 -158
  308. package/package.json +4 -2
  309. package/dist/plugins/provider-onboarding.d.ts +0 -23
  310. package/dist/plugins/provider-onboarding.js +0 -116
  311. package/dist/utils/kokoro-tts.d.ts +0 -8
  312. package/dist/utils/kokoro-tts.js +0 -67
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Optional TLS for the API server.
3
+ *
4
+ * The mobile-supervision endpoint (`/api/mobile`) rides the main HTTP server.
5
+ * To expose it off-device securely it can be served over HTTPS. This is purely
6
+ * transport security — it does not change any product gate (the dispatch gate
7
+ * `CODEBUDDY_MOBILE_ALLOW_DISPATCH` is untouched).
8
+ *
9
+ * Behaviour, gated by env (HTTP stays the default — no env, no TLS):
10
+ * - `CODEBUDDY_HTTPS=1` (or `CODEBUDDY_MOBILE_TLS=1`) enables HTTPS.
11
+ * - `CODEBUDDY_TLS_CERT` / `CODEBUDDY_TLS_KEY` = paths to a PEM cert/key. This
12
+ * is the standard production approach (Let's Encrypt / reverse proxy / the
13
+ * operator provisions the cert).
14
+ * - If TLS is enabled but no cert is provided, a dev self-signed cert is
15
+ * generated via `openssl` (if on PATH) into `~/.codebuddy/tls/` and reused.
16
+ * - If TLS is enabled but it cannot be satisfied (cert unreadable, or openssl
17
+ * absent), this **throws** with a clear message. It never silently falls back
18
+ * to plain HTTP — an operator who asked for TLS must not be downgraded.
19
+ *
20
+ * Uses only Node built-ins (`https`, `fs`, `child_process`) — no new dependency.
21
+ */
22
+ import fs from 'fs';
23
+ import os from 'os';
24
+ import path from 'path';
25
+ import { execFileSync } from 'child_process';
26
+ import { logger } from '../utils/logger.js';
27
+ /** Truthy env flag check — accepts `1`, `true`, `yes`, `on` (case-insensitive). */
28
+ function isEnvTrue(value) {
29
+ if (!value)
30
+ return false;
31
+ const v = value.trim().toLowerCase();
32
+ return v === '1' || v === 'true' || v === 'yes' || v === 'on';
33
+ }
34
+ /** Whether HTTPS is requested for the API server. */
35
+ export function isTlsEnabled(env = process.env) {
36
+ return isEnvTrue(env.CODEBUDDY_HTTPS) || isEnvTrue(env.CODEBUDDY_MOBILE_TLS);
37
+ }
38
+ function tlsDir() {
39
+ const home = process.env.CODEBUDDY_HOME || os.homedir();
40
+ return path.join(home, '.codebuddy', 'tls');
41
+ }
42
+ function hasOpenssl() {
43
+ try {
44
+ execFileSync('openssl', ['version'], { stdio: 'ignore' });
45
+ return true;
46
+ }
47
+ catch {
48
+ return false;
49
+ }
50
+ }
51
+ /**
52
+ * Generate (once) and return a dev self-signed cert/key under `~/.codebuddy/tls/`.
53
+ * Reuses an existing pair if both files are present. Throws if openssl is absent.
54
+ */
55
+ function ensureDevSelfSignedCert() {
56
+ const dir = tlsDir();
57
+ const certPath = path.join(dir, 'dev-cert.pem');
58
+ const keyPath = path.join(dir, 'dev-key.pem');
59
+ if (fs.existsSync(certPath) && fs.existsSync(keyPath)) {
60
+ return { cert: fs.readFileSync(certPath), key: fs.readFileSync(keyPath) };
61
+ }
62
+ if (!hasOpenssl()) {
63
+ throw new Error('TLS is enabled (CODEBUDDY_HTTPS) but no cert/key was provided and `openssl` ' +
64
+ 'is not available on PATH to generate a dev self-signed certificate. ' +
65
+ 'Provide CODEBUDDY_TLS_CERT and CODEBUDDY_TLS_KEY (PEM paths), or install openssl.');
66
+ }
67
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
68
+ try {
69
+ execFileSync('openssl', [
70
+ 'req',
71
+ '-x509',
72
+ '-newkey',
73
+ 'rsa:2048',
74
+ '-nodes',
75
+ '-keyout',
76
+ keyPath,
77
+ '-out',
78
+ certPath,
79
+ '-subj',
80
+ '/CN=localhost',
81
+ '-days',
82
+ '365',
83
+ ], { stdio: 'ignore' });
84
+ // Restrict the private key to the owner.
85
+ try {
86
+ fs.chmodSync(keyPath, 0o600);
87
+ }
88
+ catch {
89
+ /* best-effort on platforms without POSIX perms */
90
+ }
91
+ }
92
+ catch (err) {
93
+ throw new Error('TLS is enabled (CODEBUDDY_HTTPS) but generating a dev self-signed certificate ' +
94
+ `with openssl failed: ${err instanceof Error ? err.message : String(err)}. ` +
95
+ 'Provide CODEBUDDY_TLS_CERT and CODEBUDDY_TLS_KEY (PEM paths) instead.');
96
+ }
97
+ logger.warn(`TLS: generated a dev self-signed certificate at ${certPath}. ` +
98
+ 'This is for development/off-device testing only — use a real cert ' +
99
+ '(CODEBUDDY_TLS_CERT / CODEBUDDY_TLS_KEY) in production.');
100
+ return { cert: fs.readFileSync(certPath), key: fs.readFileSync(keyPath) };
101
+ }
102
+ /**
103
+ * Resolve TLS options for the server, or `null` when TLS is not requested.
104
+ *
105
+ * Returns `null` (HTTP, unchanged default) when neither `CODEBUDDY_HTTPS` nor
106
+ * `CODEBUDDY_MOBILE_TLS` is set. Throws a clear error if TLS is requested but
107
+ * cannot be satisfied — it never downgrades to HTTP behind the operator's back.
108
+ */
109
+ export function resolveServerTlsOptions(env = process.env) {
110
+ if (!isTlsEnabled(env)) {
111
+ return null;
112
+ }
113
+ const certPath = env.CODEBUDDY_TLS_CERT?.trim();
114
+ const keyPath = env.CODEBUDDY_TLS_KEY?.trim();
115
+ // One of the two paths set but not the other → operator misconfiguration.
116
+ if ((certPath && !keyPath) || (!certPath && keyPath)) {
117
+ throw new Error('TLS is enabled (CODEBUDDY_HTTPS) but only one of CODEBUDDY_TLS_CERT / ' +
118
+ 'CODEBUDDY_TLS_KEY is set. Provide both, or neither (to use a dev self-signed cert).');
119
+ }
120
+ if (certPath && keyPath) {
121
+ let cert;
122
+ let key;
123
+ try {
124
+ cert = fs.readFileSync(certPath);
125
+ }
126
+ catch (err) {
127
+ throw new Error(`TLS is enabled but CODEBUDDY_TLS_CERT (${certPath}) could not be read: ` +
128
+ `${err instanceof Error ? err.message : String(err)}`);
129
+ }
130
+ try {
131
+ key = fs.readFileSync(keyPath);
132
+ }
133
+ catch (err) {
134
+ throw new Error(`TLS is enabled but CODEBUDDY_TLS_KEY (${keyPath}) could not be read: ` +
135
+ `${err instanceof Error ? err.message : String(err)}`);
136
+ }
137
+ return { cert, key };
138
+ }
139
+ // TLS requested with no explicit cert — generate/reuse a dev self-signed cert.
140
+ return ensureDevSelfSignedCert();
141
+ }
142
+ //# sourceMappingURL=tls-config.js.map
@@ -0,0 +1,20 @@
1
+ export interface TunnelOptions {
2
+ port: number;
3
+ authtoken?: string;
4
+ domain?: string;
5
+ }
6
+ export declare class TunnelManager {
7
+ private static listener;
8
+ /**
9
+ * Start an ngrok tunnel forwarding to the specified port.
10
+ */
11
+ static startTunnel(options: TunnelOptions): Promise<string>;
12
+ /**
13
+ * Stop the active ngrok tunnel.
14
+ */
15
+ static stopTunnel(): Promise<void>;
16
+ /**
17
+ * Get the URL of the currently active tunnel, if any.
18
+ */
19
+ static getActiveTunnelUrl(): string | null;
20
+ }
@@ -0,0 +1,58 @@
1
+ import * as ngrok from '@ngrok/ngrok';
2
+ import { logger } from '../utils/logger.js';
3
+ export class TunnelManager {
4
+ static listener = null;
5
+ /**
6
+ * Start an ngrok tunnel forwarding to the specified port.
7
+ */
8
+ static async startTunnel(options) {
9
+ if (this.listener) {
10
+ logger.warn('A tunnel is already running. Disconnecting existing tunnel.');
11
+ await this.stopTunnel();
12
+ }
13
+ try {
14
+ logger.info(`Starting ngrok tunnel for port ${options.port}...`);
15
+ const ngrokOptions = {
16
+ addr: options.port,
17
+ authtoken_from_env: true,
18
+ };
19
+ if (options.authtoken) {
20
+ ngrokOptions.authtoken = options.authtoken;
21
+ }
22
+ if (options.domain) {
23
+ ngrokOptions.domain = options.domain;
24
+ }
25
+ this.listener = await ngrok.forward(ngrokOptions);
26
+ const url = this.listener.url() ?? '';
27
+ logger.info(`ngrok tunnel established at: ${url}`);
28
+ return url;
29
+ }
30
+ catch (error) {
31
+ logger.error('Failed to start ngrok tunnel', { error });
32
+ throw error;
33
+ }
34
+ }
35
+ /**
36
+ * Stop the active ngrok tunnel.
37
+ */
38
+ static async stopTunnel() {
39
+ if (this.listener) {
40
+ try {
41
+ await this.listener.close();
42
+ this.listener = null;
43
+ logger.info('ngrok tunnel closed.');
44
+ }
45
+ catch (error) {
46
+ logger.error('Failed to close ngrok tunnel', { error });
47
+ throw error;
48
+ }
49
+ }
50
+ }
51
+ /**
52
+ * Get the URL of the currently active tunnel, if any.
53
+ */
54
+ static getActiveTunnelUrl() {
55
+ return this.listener ? this.listener.url() ?? null : null;
56
+ }
57
+ }
58
+ //# sourceMappingURL=tunnel-manager.js.map
@@ -38,7 +38,7 @@
38
38
  * agent-executor events so receivers can map them 1:1 to local
39
39
  * AgentRuntime events for re-display.
40
40
  */
41
- export declare const FLEET_EVENT_TYPES: readonly ["fleet:agent:tool_started", "fleet:agent:tool_completed", "fleet:agent:tool_error", "fleet:agent:reasoning", "fleet:workflow:event", "fleet:workflow:start", "fleet:workflow:complete", "fleet:session:spawn", "fleet:session:message", "fleet:peer:heartbeat", "fleet:peer:compacting:start", "fleet:peer:compacting:complete", "fleet:chat-session:start", "fleet:chat-session:turn", "fleet:chat-session:end"];
41
+ export declare const FLEET_EVENT_TYPES: readonly ["fleet:agent:tool_started", "fleet:agent:tool_completed", "fleet:agent:tool_error", "fleet:agent:reasoning", "fleet:workflow:event", "fleet:workflow:start", "fleet:workflow:complete", "fleet:session:spawn", "fleet:session:message", "fleet:peer:heartbeat", "fleet:peer:compacting:start", "fleet:peer:compacting:complete", "fleet:chat-session:start", "fleet:chat-session:turn", "fleet:chat-session:end", "fleet:chat-session:goal"];
42
42
  export type FleetEventType = (typeof FLEET_EVENT_TYPES)[number];
43
43
  /**
44
44
  * Source identification carried with every fleet event so receivers know
@@ -137,3 +137,15 @@ export declare function broadcastChatSessionEnd(payload: {
137
137
  sessionId: string;
138
138
  reason?: 'end' | 'expired';
139
139
  }): void;
140
+ /**
141
+ * Goal Ralph-loop on a peer session (Hermes gateway parity) — emitted on
142
+ * goal attach/pause/resume/clear and after each post-turn judge verdict.
143
+ * Metadata only: status/verdict/counters, never goal text or judge reasons.
144
+ */
145
+ export declare function broadcastChatSessionGoal(payload: {
146
+ sessionId: string;
147
+ status: string;
148
+ verdict?: string;
149
+ turnsUsed?: number;
150
+ maxTurns?: number;
151
+ }): void;
@@ -67,6 +67,9 @@ export const FLEET_EVENT_TYPES = [
67
67
  'fleet:chat-session:start',
68
68
  'fleet:chat-session:turn',
69
69
  'fleet:chat-session:end',
70
+ // Goal Ralph-loop on peer sessions (Hermes gateway parity). Metadata
71
+ // only — status/verdict/turn counters, never goal text or reasons.
72
+ 'fleet:chat-session:goal',
70
73
  ];
71
74
  /**
72
75
  * Cached source. Resolved lazily on first emit so tests can stub
@@ -167,4 +170,17 @@ export function broadcastChatSessionTurn(payload) {
167
170
  export function broadcastChatSessionEnd(payload) {
168
171
  broadcastFleetEvent('fleet:chat-session:end', { ...payload });
169
172
  }
173
+ /**
174
+ * Goal Ralph-loop on a peer session (Hermes gateway parity) — emitted on
175
+ * goal attach/pause/resume/clear and after each post-turn judge verdict.
176
+ * Metadata only: status/verdict/counters, never goal text or judge reasons.
177
+ */
178
+ export function broadcastChatSessionGoal(payload) {
179
+ broadcastFleetEvent('fleet:chat-session:goal', { ...payload });
180
+ }
181
+ // Wire up the agent runtime hooks (breaks circular dependency #2)
182
+ import { registerFleetBroadcaster } from '../../agent/execution/tool-hooks.js';
183
+ registerFleetBroadcaster(broadcastFleetEvent);
184
+ import { registerSessionFleetBroadcaster } from '../../agent/multi-agent/session-fleet-bridge.js';
185
+ registerSessionFleetBroadcaster(broadcastFleetEvent);
170
186
  //# sourceMappingURL=fleet-bridge.js.map
@@ -39,6 +39,7 @@ export interface BuildOptions {
39
39
  includeWritingRules?: boolean;
40
40
  includeCodingStyle?: boolean;
41
41
  includeWorkflowRules?: boolean;
42
+ includeExecutionDiscipline?: boolean;
42
43
  includeVariation?: boolean;
43
44
  }
44
45
  export declare class PromptBuilder {
@@ -30,6 +30,7 @@ const ALL_BLOCKS = {
30
30
  includeWritingRules: true,
31
31
  includeCodingStyle: true,
32
32
  includeWorkflowRules: true,
33
+ includeExecutionDiscipline: true,
33
34
  includeVariation: true,
34
35
  };
35
36
  const EXTERNAL_PROMPT_MANAGER_TOOLS = [
@@ -83,13 +84,17 @@ export class PromptBuilder {
83
84
  // confuse the LLM into hallucinating JSON tool calls. Force-off here.
84
85
  const toolCfg = getModelToolConfig(modelName);
85
86
  const activeToolFilter = getToolFilter();
86
- if (toolCfg.supportsToolCalls === false) {
87
+ const forceTools = process.env.GROK_FORCE_TOOLS === 'true';
88
+ if (toolCfg.supportsToolCalls === false && !forceTools) {
87
89
  gates.includeMemoryDirective = false;
88
90
  gates.includeLessonsDirective = false;
89
91
  gates.includeUserModelDirective = false;
90
92
  gates.includeWorkflowRules = false;
93
+ gates.includeExecutionDiscipline = false;
91
94
  }
92
- if (!isToolNameAllowed('remember', activeToolFilter)) {
95
+ const rememberToolAllowed = isToolNameAllowed('remember', activeToolFilter);
96
+ const memoryProposeToolAllowed = isToolNameAllowed('memory_propose', activeToolFilter);
97
+ if (!rememberToolAllowed && !memoryProposeToolAllowed) {
93
98
  gates.includeMemoryDirective = false;
94
99
  }
95
100
  if (!['lessons_add', 'lessons_search', 'lessons_graph'].every(toolName => isToolNameAllowed(toolName, activeToolFilter))) {
@@ -188,7 +193,7 @@ export class PromptBuilder {
188
193
  });
189
194
  logger.debug(`Auto-selected prompt: ${autoId} (based on ${modelName})`);
190
195
  }
191
- else if (toolCfg.supportsToolCalls === false) {
196
+ else if (toolCfg.supportsToolCalls === false && !forceTools) {
192
197
  // Chat-only base prompt for models that can't dispatch tool calls.
193
198
  // The default body lists `bash`, `view_file`, `str_replace_editor`,
194
199
  // etc. by name — small Ollama models read those mentions and try to
@@ -223,6 +228,26 @@ export class PromptBuilder {
223
228
  systemPrompt = `# Role & Instructions (from intro_hook.txt)\n\n${introHookContent}\n\n---\n\n${systemPrompt}`;
224
229
  logger.debug("Prepended intro hook content to system prompt");
225
230
  }
231
+ // Inject execution-discipline guidance (tool-use enforcement, anti-stub
232
+ // completion, mandatory-tool, pre-finalize self-check). Borrowed from the
233
+ // Hermes Agent prompt audit — its highest-leverage agentic-reliability
234
+ // block. Placed HERE in the STABLE PREFIX (not near the footer) on
235
+ // purpose: the footer reminder section is shuffled by the variation
236
+ // injector (varySystemPrompt → extractBlocks splits on bullet lines), so
237
+ // a late placement fragmented this block — foreign bullets got interleaved
238
+ // between its lines. Up here it stays contiguous, cache-stable, and
239
+ // survives head-truncation. Gated off for trivial/lite + tool-callless
240
+ // models (see gating above).
241
+ if (gates.includeExecutionDiscipline) {
242
+ try {
243
+ const { getExecutionDisciplineBlock } = await import('../prompts/execution-discipline.js');
244
+ systemPrompt += '\n\n' + getExecutionDisciplineBlock();
245
+ logger.debug('Injected execution-discipline guidance into system prompt');
246
+ }
247
+ catch (err) {
248
+ logger.warn('Failed to inject execution-discipline block', { error: getErrorMessage(err) });
249
+ }
250
+ }
226
251
  // Inject bootstrap context files (BOOTSTRAP.md, AGENTS.md, SOUL.md, etc.)
227
252
  if (gates.includeBootstrap) {
228
253
  try {
@@ -361,17 +386,32 @@ export class PromptBuilder {
361
386
  }
362
387
  catch { /* fleet registry optional — module not loaded yet */ }
363
388
  }
364
- // Inject auto-memory directive — tells the LLM WHEN to call the
365
- // `remember` tool to auto-persist non-obvious facts to
389
+ // Inject auto-memory directive — tells the LLM WHEN to call `remember`
390
+ // or the review-gated `memory_propose` tool for non-obvious facts from
366
391
  // .codebuddy/CODEBUDDY_MEMORY.md (project) or ~/.codebuddy/memory.md
367
392
  // (user). Without this directive, the tool is registered but the LLM
368
393
  // has no instruction on when to use it, so the file stays empty.
369
- // Paired with `alwaysInclude: ['remember']` in tool-selection-strategy.
394
+ // Paired with `alwaysInclude: ['remember', 'memory_propose']` in
395
+ // tool-selection-strategy.
370
396
  if (this.config.memoryEnabled && this.persistentMemory && gates.includeMemoryDirective) {
397
+ const memoryWriteInstruction = memoryProposeToolAllowed
398
+ ? 'Use `remember` only for explicit, high-confidence durable facts. Use `memory_propose` for inferred, ambiguous, or model-derived facts so the user can review them before they become prompt-injected memory.'
399
+ : 'Use `remember` only for explicit, high-confidence durable facts. Do not store inferred or ambiguous facts silently.';
400
+ const inferredFactInstruction = memoryProposeToolAllowed
401
+ ? 'When to call `memory_propose`:'
402
+ : 'When to consider durable memory, but skip if it is only inferred or ambiguous:';
403
+ const closingMemoryInstruction = memoryProposeToolAllowed
404
+ ? 'Prefer `memory_propose` over `remember` when the fact came from your interpretation rather than an explicit user instruction.'
405
+ : 'Only call `remember` when the fact came from an explicit user instruction or correction.';
371
406
  systemPrompt += `\n\n<auto_memory_directive>
372
- You have a persistent memory system at .codebuddy/CODEBUDDY_MEMORY.md (project-scoped) and ~/.codebuddy/memory.md (user-scoped, all projects). Use the \`remember\` tool to save facts that will be useful in future sessions.
407
+ You have a persistent memory system at .codebuddy/CODEBUDDY_MEMORY.md (project-scoped) and ~/.codebuddy/memory.md (user-scoped, all projects).
408
+ ${memoryWriteInstruction}
373
409
 
374
410
  When to call \`remember\`:
411
+ - The user explicitly says to remember/store a fact or preference
412
+ - The user directly corrects an existing stable fact
413
+
414
+ ${inferredFactInstruction}
375
415
  - User preferences ("user prefers single quotes", "always use Vitest, not Jest")
376
416
  - Architectural decisions ("API uses JWT in HttpOnly cookies, not localStorage")
377
417
  - Non-obvious gotchas ("vi.hoisted() needed for mock factories in this repo")
@@ -388,7 +428,7 @@ Format:
388
428
  - \`scope\`: \`project\` (default — fact is specific to THIS repo) or \`user\` (preference across all projects)
389
429
  - \`category\`: \`preferences\`, \`decisions\`, \`patterns\`, \`project\`, or \`custom\`
390
430
 
391
- Call \`remember\` proactively when you learn something worth remembering — don't wait for the user to ask.
431
+ ${closingMemoryInstruction}
392
432
  </auto_memory_directive>`;
393
433
  logger.debug('Injected auto-memory directive into system prompt');
394
434
  }
@@ -642,6 +682,7 @@ export function gatesForComplexity(complexity) {
642
682
  includeWritingRules: true,
643
683
  includeCodingStyle: false,
644
684
  includeWorkflowRules: false,
685
+ includeExecutionDiscipline: false,
645
686
  includeVariation: false,
646
687
  };
647
688
  case 'simple':
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * @module shared/engine-types
9
9
  */
10
- export type EngineStreamEventType = 'content' | 'thinking' | 'tool_start' | 'tool_end' | 'tool_stream' | 'token_count' | 'cost' | 'done' | 'error' | 'ask_user' | 'plan_progress' | 'diff_preview';
10
+ export type EngineStreamEventType = 'content' | 'thinking' | 'tool_start' | 'tool_end' | 'tool_stream' | 'token_count' | 'cost' | 'done' | 'error' | 'ask_user' | 'plan_progress' | 'steer' | 'diff_preview' | 'goal_status';
11
11
  export interface EngineStreamEvent {
12
12
  type: EngineStreamEventType;
13
13
  /** Incremental text delta */
@@ -47,6 +47,11 @@ export interface EngineStreamEvent {
47
47
  completed: number;
48
48
  message?: string;
49
49
  };
50
+ /** User guidance delivered to an active run. */
51
+ steer?: {
52
+ content: string;
53
+ source: string;
54
+ };
50
55
  /** Diff preview */
51
56
  diffPreview?: {
52
57
  turnId: number;
@@ -59,6 +64,15 @@ export interface EngineStreamEvent {
59
64
  }>;
60
65
  plan?: string;
61
66
  };
67
+ /** Autonomous goal-loop progress (for host UIs like the Cowork goal banner). */
68
+ goalStatus?: {
69
+ goal: string;
70
+ status: 'active' | 'paused' | 'done' | 'cleared';
71
+ turnsUsed: number;
72
+ maxTurns: number;
73
+ lastVerdict?: 'done' | 'continue' | 'skipped';
74
+ lastReason?: string;
75
+ };
62
76
  }
63
77
  export interface EngineMessage {
64
78
  role: 'user' | 'assistant' | 'system';
@@ -46,6 +46,7 @@ export declare class SidecarBridge {
46
46
  * Find the sidecar binary
47
47
  */
48
48
  private findBinary;
49
+ private isExecutableCandidate;
49
50
  /**
50
51
  * Check if sidecar is available
51
52
  */
@@ -11,7 +11,7 @@
11
11
  import { spawn } from 'child_process';
12
12
  import { createInterface } from 'readline';
13
13
  import { join } from 'path';
14
- import { existsSync } from 'fs';
14
+ import { accessSync, constants, existsSync } from 'fs';
15
15
  import { logger } from '../utils/logger.js';
16
16
  // ============================================================================
17
17
  // Sidecar Bridge
@@ -27,23 +27,34 @@ export class SidecarBridge {
27
27
  * Find the sidecar binary
28
28
  */
29
29
  findBinary() {
30
+ const localBinary = process.platform === 'win32' ? 'codebuddy-sidecar.exe' : 'codebuddy-sidecar';
30
31
  const candidates = [
31
32
  // Development: built via `cargo build`
32
- join(process.cwd(), 'src-sidecar', 'target', 'release', 'codebuddy-sidecar.exe'),
33
- join(process.cwd(), 'src-sidecar', 'target', 'release', 'codebuddy-sidecar'),
34
- join(process.cwd(), 'src-sidecar', 'target', 'debug', 'codebuddy-sidecar.exe'),
35
- join(process.cwd(), 'src-sidecar', 'target', 'debug', 'codebuddy-sidecar'),
33
+ join(process.cwd(), 'src-sidecar', 'target', 'release', localBinary),
34
+ join(process.cwd(), 'src-sidecar', 'target', 'debug', localBinary),
36
35
  // Installed globally
37
- join(process.env.HOME || process.env.USERPROFILE || '', '.cargo', 'bin', 'codebuddy-sidecar.exe'),
38
- join(process.env.HOME || process.env.USERPROFILE || '', '.cargo', 'bin', 'codebuddy-sidecar'),
36
+ join(process.env.HOME || process.env.USERPROFILE || '', '.cargo', 'bin', localBinary),
39
37
  ];
40
- for (const path of candidates) {
41
- if (existsSync(path)) {
42
- return path;
38
+ for (const candidate of candidates) {
39
+ if (this.isExecutableCandidate(candidate)) {
40
+ return candidate;
43
41
  }
44
42
  }
45
43
  return null;
46
44
  }
45
+ isExecutableCandidate(filePath) {
46
+ if (!existsSync(filePath))
47
+ return false;
48
+ if (process.platform === 'win32')
49
+ return true;
50
+ try {
51
+ accessSync(filePath, constants.X_OK);
52
+ return true;
53
+ }
54
+ catch {
55
+ return false;
56
+ }
57
+ }
47
58
  /**
48
59
  * Check if sidecar is available
49
60
  */
@@ -58,6 +58,7 @@ export class SpecTransitionError extends Error {
58
58
  // Singleton registry (one store per working directory)
59
59
  // ============================================================================
60
60
  const registry = new Map();
61
+ let lastStoryCreatedAt = 0;
61
62
  export function getSpecStore(workDir = process.cwd()) {
62
63
  const key = path.resolve(workDir);
63
64
  if (!registry.has(key)) {
@@ -73,6 +74,7 @@ export function getSpecStore(workDir = process.cwd()) {
73
74
  /** Test helper: drop cached store instances. */
74
75
  export function resetSpecStores() {
75
76
  registry.clear();
77
+ lastStoryCreatedAt = 0;
76
78
  }
77
79
  /** Whether `from → to` is a legal story status transition. */
78
80
  export function isLegalTransition(from, to) {
@@ -211,7 +213,7 @@ export class SpecStore {
211
213
  const title = (input.title ?? '').trim();
212
214
  if (!title)
213
215
  throw new Error('Story title is required.');
214
- const now = Date.now();
216
+ const now = nextStoryCreatedAt();
215
217
  const allowedPaths = cleanList(input.allowedPaths);
216
218
  const verification = cleanList(input.verification);
217
219
  const story = {
@@ -479,6 +481,11 @@ function parseFrontmatter(content) {
479
481
  function randomId() {
480
482
  return `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 7)}`;
481
483
  }
484
+ function nextStoryCreatedAt() {
485
+ const now = Date.now();
486
+ lastStoryCreatedAt = now > lastStoryCreatedAt ? now : lastStoryCreatedAt + 1;
487
+ return lastStoryCreatedAt;
488
+ }
482
489
  /** Trim, drop blanks, and de-duplicate a string list (used for contract fields). */
483
490
  function cleanList(values) {
484
491
  if (!Array.isArray(values))
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * AudioReader TTS Provider
3
3
  *
4
- * Uses a local AudioReader API (Kokoro-82M engine) for high-quality,
4
+ * Uses a local AudioReader API for high-quality,
5
5
  * free text-to-speech synthesis via an OpenAI-compatible REST API.
6
6
  */
7
7
  import type { TTSProviderConfig, Voice, SynthesisOptions, SynthesisResult } from '../types.js';
@@ -1,14 +1,14 @@
1
1
  /**
2
2
  * AudioReader TTS Provider
3
3
  *
4
- * Uses a local AudioReader API (Kokoro-82M engine) for high-quality,
4
+ * Uses a local AudioReader API for high-quality,
5
5
  * free text-to-speech synthesis via an OpenAI-compatible REST API.
6
6
  */
7
7
  const DEFAULT_BASE_URL = 'http://localhost:8000';
8
- const DEFAULT_MODEL = 'kokoro';
8
+ const DEFAULT_MODEL = 'audioreader';
9
9
  const DEFAULT_VOICE = 'ff_siwis';
10
- /** Known Kokoro voices with metadata */
11
- const KOKORO_VOICES = {
10
+ /** Known AudioReader voices with metadata */
11
+ const AUDIOREADER_VOICES = {
12
12
  ff_siwis: { name: 'Siwis', language: 'fr-FR', gender: 'female' },
13
13
  af_bella: { name: 'Bella', language: 'en-US', gender: 'female' },
14
14
  af_heart: { name: 'Heart', language: 'en-US', gender: 'female' },
@@ -20,7 +20,7 @@ const KOKORO_VOICES = {
20
20
  bf_emma: { name: 'Emma', language: 'en-GB', gender: 'female' },
21
21
  bm_george: { name: 'George', language: 'en-GB', gender: 'male' },
22
22
  };
23
- /** OpenAI voice name to Kokoro voice mapping */
23
+ /** OpenAI voice name to AudioReader voice mapping */
24
24
  const OPENAI_VOICE_MAP = {
25
25
  alloy: 'af_bella',
26
26
  echo: 'am_adam',
@@ -74,7 +74,7 @@ export class AudioReaderTTSProvider {
74
74
  catch {
75
75
  // Fall back to known voices
76
76
  }
77
- return Object.keys(KOKORO_VOICES).map(id => this.buildVoice(id));
77
+ return Object.keys(AUDIOREADER_VOICES).map(id => this.buildVoice(id));
78
78
  }
79
79
  async synthesize(text, options) {
80
80
  if (!this.initialized) {
@@ -131,11 +131,11 @@ export class AudioReaderTTSProvider {
131
131
  return voice;
132
132
  }
133
133
  buildVoice(id) {
134
- const meta = KOKORO_VOICES[id];
134
+ const meta = AUDIOREADER_VOICES[id];
135
135
  return {
136
136
  id: `audioreader-${id}`,
137
137
  name: meta?.name ?? id,
138
- description: `AudioReader Kokoro voice - ${meta?.name ?? id}`,
138
+ description: `AudioReader voice - ${meta?.name ?? id}`,
139
139
  language: meta?.language ?? 'en-US',
140
140
  gender: meta?.gender ?? 'female',
141
141
  provider: 'audioreader',
@@ -12,6 +12,7 @@ import * as path from 'path';
12
12
  import { existsSync } from 'fs';
13
13
  import { EventEmitter } from 'events';
14
14
  import { spawn } from 'child_process';
15
+ import { performance } from 'perf_hooks';
15
16
  // ============================================================================
16
17
  // Built-in Templates
17
18
  // ============================================================================
@@ -641,7 +642,7 @@ export class TemplateEngine extends EventEmitter {
641
642
  * Generate project from template
642
643
  */
643
644
  async generate(options) {
644
- const startTime = Date.now();
645
+ const startTime = performance.now();
645
646
  const warnings = [];
646
647
  const filesCreated = [];
647
648
  // Get template
@@ -737,7 +738,7 @@ export class TemplateEngine extends EventEmitter {
737
738
  success: true,
738
739
  projectPath,
739
740
  filesCreated,
740
- duration: Date.now() - startTime,
741
+ duration: Math.max(1, Math.ceil(performance.now() - startTime)),
741
742
  warnings,
742
743
  nextSteps,
743
744
  };