@tyvm/knowhow 0.0.108 → 0.0.109-dev.2b94ba2

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 (236) hide show
  1. package/README.md +45 -0
  2. package/package.json +9 -4
  3. package/scripts/build-for-node.sh +10 -24
  4. package/scripts/publish.sh +86 -0
  5. package/src/agents/base/base.ts +10 -0
  6. package/src/agents/tools/execCommand.ts +49 -6
  7. package/src/agents/tools/index.ts +0 -1
  8. package/src/agents/tools/list.ts +2 -4
  9. package/src/chat/CliChatService.ts +11 -2
  10. package/src/chat/modules/AgentModule.ts +61 -31
  11. package/src/chat/modules/SessionsModule.ts +47 -3
  12. package/src/chat/modules/SystemModule.ts +2 -2
  13. package/src/chat/renderer/CompactRenderer.ts +20 -0
  14. package/src/chat/renderer/ConsoleRenderer.ts +19 -0
  15. package/src/chat/renderer/FancyRenderer.ts +19 -0
  16. package/src/chat/renderer/types.ts +11 -0
  17. package/src/cli.ts +91 -659
  18. package/src/clients/anthropic.ts +18 -17
  19. package/src/clients/index.ts +31 -11
  20. package/src/clients/openai.ts +8 -5
  21. package/src/clients/types.ts +48 -10
  22. package/src/clients/withRetry.ts +89 -0
  23. package/src/cloudWorker.ts +175 -113
  24. package/src/commands/agent.ts +246 -0
  25. package/src/commands/misc.ts +174 -0
  26. package/src/commands/modules.ts +552 -0
  27. package/src/commands/services.ts +77 -0
  28. package/src/commands/workers.ts +168 -0
  29. package/src/config.ts +38 -1
  30. package/src/fileSync.ts +70 -29
  31. package/src/hashes.ts +35 -13
  32. package/src/index.ts +18 -0
  33. package/src/logger.ts +197 -0
  34. package/src/plugins/embedding.ts +11 -6
  35. package/src/plugins/plugins.ts +0 -21
  36. package/src/plugins/vim.ts +5 -16
  37. package/src/processors/JsonCompressor.ts +6 -6
  38. package/src/services/EventService.ts +61 -1
  39. package/src/services/KnowhowClient.ts +34 -4
  40. package/src/services/MediaProcessorService.ts +79 -10
  41. package/src/services/modules/index.ts +102 -53
  42. package/src/services/modules/types.ts +6 -0
  43. package/src/tunnel.ts +216 -0
  44. package/src/types.ts +0 -1
  45. package/src/worker.ts +105 -312
  46. package/src/workers/auth/WsMiddleware.ts +99 -0
  47. package/src/workers/auth/authMiddleware.ts +104 -0
  48. package/src/workers/auth/types.ts +14 -2
  49. package/src/workers/tools/index.ts +2 -0
  50. package/src/workers/tools/reloadConfig.ts +84 -0
  51. package/tests/services/WorkerReloadConfig.test.ts +141 -0
  52. package/tests/unit/clients/AIClient.test.ts +446 -0
  53. package/tests/unit/clients/withRetry.test.ts +319 -0
  54. package/tests/unit/commands/github-credentials.test.ts +210 -0
  55. package/tests/unit/modules/moduleLoading.test.ts +39 -37
  56. package/tests/unit/plugins/pluginLoading.test.ts +0 -85
  57. package/ts_build/package.json +9 -4
  58. package/ts_build/src/agents/base/base.js +11 -0
  59. package/ts_build/src/agents/base/base.js.map +1 -1
  60. package/ts_build/src/agents/tools/execCommand.d.ts +1 -1
  61. package/ts_build/src/agents/tools/execCommand.js +39 -5
  62. package/ts_build/src/agents/tools/execCommand.js.map +1 -1
  63. package/ts_build/src/agents/tools/index.d.ts +0 -1
  64. package/ts_build/src/agents/tools/index.js +0 -1
  65. package/ts_build/src/agents/tools/index.js.map +1 -1
  66. package/ts_build/src/agents/tools/list.js +2 -4
  67. package/ts_build/src/agents/tools/list.js.map +1 -1
  68. package/ts_build/src/chat/CliChatService.js +14 -2
  69. package/ts_build/src/chat/CliChatService.js.map +1 -1
  70. package/ts_build/src/chat/modules/AgentModule.d.ts +1 -1
  71. package/ts_build/src/chat/modules/AgentModule.js +43 -20
  72. package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
  73. package/ts_build/src/chat/modules/SessionsModule.js +37 -3
  74. package/ts_build/src/chat/modules/SessionsModule.js.map +1 -1
  75. package/ts_build/src/chat/modules/SystemModule.js +2 -2
  76. package/ts_build/src/chat/modules/SystemModule.js.map +1 -1
  77. package/ts_build/src/chat/renderer/CompactRenderer.d.ts +4 -0
  78. package/ts_build/src/chat/renderer/CompactRenderer.js +16 -0
  79. package/ts_build/src/chat/renderer/CompactRenderer.js.map +1 -1
  80. package/ts_build/src/chat/renderer/ConsoleRenderer.d.ts +4 -0
  81. package/ts_build/src/chat/renderer/ConsoleRenderer.js +16 -0
  82. package/ts_build/src/chat/renderer/ConsoleRenderer.js.map +1 -1
  83. package/ts_build/src/chat/renderer/FancyRenderer.d.ts +4 -0
  84. package/ts_build/src/chat/renderer/FancyRenderer.js +16 -0
  85. package/ts_build/src/chat/renderer/FancyRenderer.js.map +1 -1
  86. package/ts_build/src/chat/renderer/types.d.ts +2 -0
  87. package/ts_build/src/cli.js +47 -519
  88. package/ts_build/src/cli.js.map +1 -1
  89. package/ts_build/src/clients/anthropic.d.ts +5 -5
  90. package/ts_build/src/clients/anthropic.js +18 -17
  91. package/ts_build/src/clients/anthropic.js.map +1 -1
  92. package/ts_build/src/clients/index.js +9 -10
  93. package/ts_build/src/clients/index.js.map +1 -1
  94. package/ts_build/src/clients/openai.js +4 -4
  95. package/ts_build/src/clients/openai.js.map +1 -1
  96. package/ts_build/src/clients/types.d.ts +15 -8
  97. package/ts_build/src/clients/withRetry.d.ts +2 -0
  98. package/ts_build/src/clients/withRetry.js +60 -0
  99. package/ts_build/src/clients/withRetry.js.map +1 -0
  100. package/ts_build/src/cloudWorker.d.ts +14 -0
  101. package/ts_build/src/cloudWorker.js +105 -66
  102. package/ts_build/src/cloudWorker.js.map +1 -1
  103. package/ts_build/src/commands/agent.d.ts +6 -0
  104. package/ts_build/src/commands/agent.js +229 -0
  105. package/ts_build/src/commands/agent.js.map +1 -0
  106. package/ts_build/src/commands/misc.d.ts +10 -0
  107. package/ts_build/src/commands/misc.js +197 -0
  108. package/ts_build/src/commands/misc.js.map +1 -0
  109. package/ts_build/src/commands/modules.d.ts +3 -0
  110. package/ts_build/src/commands/modules.js +487 -0
  111. package/ts_build/src/commands/modules.js.map +1 -0
  112. package/ts_build/src/commands/services.d.ts +5 -0
  113. package/ts_build/src/commands/services.js +87 -0
  114. package/ts_build/src/commands/services.js.map +1 -0
  115. package/ts_build/src/commands/workers.d.ts +6 -0
  116. package/ts_build/src/commands/workers.js +168 -0
  117. package/ts_build/src/commands/workers.js.map +1 -0
  118. package/ts_build/src/config.d.ts +1 -0
  119. package/ts_build/src/config.js +33 -1
  120. package/ts_build/src/config.js.map +1 -1
  121. package/ts_build/src/fileSync.d.ts +6 -0
  122. package/ts_build/src/fileSync.js +50 -23
  123. package/ts_build/src/fileSync.js.map +1 -1
  124. package/ts_build/src/hashes.d.ts +2 -2
  125. package/ts_build/src/hashes.js +35 -9
  126. package/ts_build/src/hashes.js.map +1 -1
  127. package/ts_build/src/index.d.ts +1 -0
  128. package/ts_build/src/index.js +17 -1
  129. package/ts_build/src/index.js.map +1 -1
  130. package/ts_build/src/logger.d.ts +21 -0
  131. package/ts_build/src/logger.js +106 -0
  132. package/ts_build/src/logger.js.map +1 -0
  133. package/ts_build/src/plugins/embedding.js +4 -3
  134. package/ts_build/src/plugins/embedding.js.map +1 -1
  135. package/ts_build/src/plugins/plugins.d.ts +0 -2
  136. package/ts_build/src/plugins/plugins.js +0 -11
  137. package/ts_build/src/plugins/plugins.js.map +1 -1
  138. package/ts_build/src/plugins/vim.js +3 -9
  139. package/ts_build/src/plugins/vim.js.map +1 -1
  140. package/ts_build/src/processors/JsonCompressor.js +4 -4
  141. package/ts_build/src/processors/JsonCompressor.js.map +1 -1
  142. package/ts_build/src/services/EventService.d.ts +6 -1
  143. package/ts_build/src/services/EventService.js +29 -0
  144. package/ts_build/src/services/EventService.js.map +1 -1
  145. package/ts_build/src/services/KnowhowClient.d.ts +13 -1
  146. package/ts_build/src/services/KnowhowClient.js +19 -2
  147. package/ts_build/src/services/KnowhowClient.js.map +1 -1
  148. package/ts_build/src/services/MediaProcessorService.d.ts +5 -4
  149. package/ts_build/src/services/MediaProcessorService.js +53 -8
  150. package/ts_build/src/services/MediaProcessorService.js.map +1 -1
  151. package/ts_build/src/services/modules/index.d.ts +33 -0
  152. package/ts_build/src/services/modules/index.js +73 -49
  153. package/ts_build/src/services/modules/index.js.map +1 -1
  154. package/ts_build/src/services/modules/types.d.ts +6 -0
  155. package/ts_build/src/tunnel.d.ts +27 -0
  156. package/ts_build/src/tunnel.js +112 -0
  157. package/ts_build/src/tunnel.js.map +1 -0
  158. package/ts_build/src/types.d.ts +0 -1
  159. package/ts_build/src/types.js.map +1 -1
  160. package/ts_build/src/worker.d.ts +1 -4
  161. package/ts_build/src/worker.js +59 -227
  162. package/ts_build/src/worker.js.map +1 -1
  163. package/ts_build/src/workers/auth/WsMiddleware.d.ts +8 -0
  164. package/ts_build/src/workers/auth/WsMiddleware.js +65 -0
  165. package/ts_build/src/workers/auth/WsMiddleware.js.map +1 -0
  166. package/ts_build/src/workers/auth/authMiddleware.d.ts +3 -0
  167. package/ts_build/src/workers/auth/authMiddleware.js +60 -0
  168. package/ts_build/src/workers/auth/authMiddleware.js.map +1 -0
  169. package/ts_build/src/workers/auth/types.d.ts +8 -1
  170. package/ts_build/src/workers/tools/index.d.ts +2 -0
  171. package/ts_build/src/workers/tools/index.js +4 -1
  172. package/ts_build/src/workers/tools/index.js.map +1 -1
  173. package/ts_build/src/workers/tools/reloadConfig.d.ts +14 -0
  174. package/ts_build/src/workers/tools/reloadConfig.js +48 -0
  175. package/ts_build/src/workers/tools/reloadConfig.js.map +1 -0
  176. package/ts_build/tests/services/WorkerReloadConfig.test.d.ts +1 -0
  177. package/ts_build/tests/services/WorkerReloadConfig.test.js +86 -0
  178. package/ts_build/tests/services/WorkerReloadConfig.test.js.map +1 -0
  179. package/ts_build/tests/unit/clients/AIClient.test.d.ts +1 -0
  180. package/ts_build/tests/unit/clients/AIClient.test.js +339 -0
  181. package/ts_build/tests/unit/clients/AIClient.test.js.map +1 -0
  182. package/ts_build/tests/unit/clients/withRetry.test.d.ts +1 -0
  183. package/ts_build/tests/unit/clients/withRetry.test.js +225 -0
  184. package/ts_build/tests/unit/clients/withRetry.test.js.map +1 -0
  185. package/ts_build/tests/unit/commands/github-credentials.test.d.ts +1 -0
  186. package/ts_build/tests/unit/commands/github-credentials.test.js +145 -0
  187. package/ts_build/tests/unit/commands/github-credentials.test.js.map +1 -0
  188. package/ts_build/tests/unit/modules/moduleLoading.test.js +20 -26
  189. package/ts_build/tests/unit/modules/moduleLoading.test.js.map +1 -1
  190. package/ts_build/tests/unit/plugins/pluginLoading.test.js +0 -65
  191. package/ts_build/tests/unit/plugins/pluginLoading.test.js.map +1 -1
  192. package/src/agents/tools/executeScript/README.md +0 -94
  193. package/src/agents/tools/executeScript/definition.ts +0 -79
  194. package/src/agents/tools/executeScript/examples/dependency-injection-validation.ts +0 -272
  195. package/src/agents/tools/executeScript/examples/quick-test.ts +0 -74
  196. package/src/agents/tools/executeScript/examples/serialization-test.ts +0 -321
  197. package/src/agents/tools/executeScript/examples/test-runner.ts +0 -197
  198. package/src/agents/tools/executeScript/index.ts +0 -98
  199. package/src/services/script-execution/SandboxContext.ts +0 -282
  200. package/src/services/script-execution/ScriptExecutor.ts +0 -441
  201. package/src/services/script-execution/ScriptPolicy.ts +0 -194
  202. package/src/services/script-execution/ScriptTracer.ts +0 -249
  203. package/src/services/script-execution/types.ts +0 -134
  204. package/ts_build/src/agents/tools/executeScript/definition.d.ts +0 -2
  205. package/ts_build/src/agents/tools/executeScript/definition.js +0 -76
  206. package/ts_build/src/agents/tools/executeScript/definition.js.map +0 -1
  207. package/ts_build/src/agents/tools/executeScript/examples/dependency-injection-validation.d.ts +0 -18
  208. package/ts_build/src/agents/tools/executeScript/examples/dependency-injection-validation.js +0 -192
  209. package/ts_build/src/agents/tools/executeScript/examples/dependency-injection-validation.js.map +0 -1
  210. package/ts_build/src/agents/tools/executeScript/examples/quick-test.d.ts +0 -3
  211. package/ts_build/src/agents/tools/executeScript/examples/quick-test.js +0 -64
  212. package/ts_build/src/agents/tools/executeScript/examples/quick-test.js.map +0 -1
  213. package/ts_build/src/agents/tools/executeScript/examples/serialization-test.d.ts +0 -15
  214. package/ts_build/src/agents/tools/executeScript/examples/serialization-test.js +0 -266
  215. package/ts_build/src/agents/tools/executeScript/examples/serialization-test.js.map +0 -1
  216. package/ts_build/src/agents/tools/executeScript/examples/test-runner.d.ts +0 -4
  217. package/ts_build/src/agents/tools/executeScript/examples/test-runner.js +0 -208
  218. package/ts_build/src/agents/tools/executeScript/examples/test-runner.js.map +0 -1
  219. package/ts_build/src/agents/tools/executeScript/index.d.ts +0 -28
  220. package/ts_build/src/agents/tools/executeScript/index.js +0 -72
  221. package/ts_build/src/agents/tools/executeScript/index.js.map +0 -1
  222. package/ts_build/src/services/script-execution/SandboxContext.d.ts +0 -34
  223. package/ts_build/src/services/script-execution/SandboxContext.js +0 -189
  224. package/ts_build/src/services/script-execution/SandboxContext.js.map +0 -1
  225. package/ts_build/src/services/script-execution/ScriptExecutor.d.ts +0 -19
  226. package/ts_build/src/services/script-execution/ScriptExecutor.js +0 -269
  227. package/ts_build/src/services/script-execution/ScriptExecutor.js.map +0 -1
  228. package/ts_build/src/services/script-execution/ScriptPolicy.d.ts +0 -28
  229. package/ts_build/src/services/script-execution/ScriptPolicy.js +0 -115
  230. package/ts_build/src/services/script-execution/ScriptPolicy.js.map +0 -1
  231. package/ts_build/src/services/script-execution/ScriptTracer.d.ts +0 -19
  232. package/ts_build/src/services/script-execution/ScriptTracer.js +0 -186
  233. package/ts_build/src/services/script-execution/ScriptTracer.js.map +0 -1
  234. package/ts_build/src/services/script-execution/types.d.ts +0 -108
  235. package/ts_build/src/services/script-execution/types.js +0 -3
  236. package/ts_build/src/services/script-execution/types.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WsMiddleware.js","sourceRoot":"","sources":["../../../../src/workers/auth/WsMiddleware.ts"],"names":[],"mappings":";;;;;;AACA,oDAAkC;AAgBlC,MAAa,iBAAiB;IACpB,GAAG,GAAqB,EAAE,CAAC;IAEnC,GAAG,CAAC,EAAkB;QACpB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAClB,OAAO,IAAI,CAAC;IACd,CAAC;IAOD,MAAM,CAAC,EAAa,EAAE,SAA0C;QAC9D,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,IAAqB,EAAE,EAAE;YAC/C,IAAI,CAAC,GAAG,CAAC,CAAC;YACV,MAAM,IAAI,GAAG,KAAK,EAAE,GAAW,EAAiB,EAAE;gBAChD,IAAI,GAAG,EAAE,CAAC;oBACR,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;oBAC9D,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;oBAC5B,OAAO;gBACT,CAAC;gBACD,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;gBACzB,IAAI,EAAE,EAAE,CAAC;oBACP,MAAM,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;gBAC3B,CAAC;qBAAM,CAAC;oBACN,SAAS,CAAC,IAAI,CAAC,CAAC;gBAClB,CAAC;YACH,CAAC,CAAC;YACF,MAAM,IAAI,EAAE,CAAC;QACf,CAAC,CAAC,CAAC;IACL,CAAC;IAeD,UAAU,CAAC,EAAa;QACtB,MAAM,YAAY,GAAG,IAAI,gBAAY,EAAE,CAAC;QAGxC,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,IAAqB,EAAE,EAAE;YAC/C,IAAI,CAAC,GAAG,CAAC,CAAC;YACV,MAAM,IAAI,GAAG,KAAK,EAAE,GAAW,EAAiB,EAAE;gBAChD,IAAI,GAAG,EAAE,CAAC;oBACR,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;oBAC9D,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;oBAC5B,OAAO;gBACT,CAAC;gBACD,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;gBACzB,IAAI,EAAE,EAAE,CAAC;oBACP,MAAM,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;gBAC3B,CAAC;qBAAM,CAAC;oBAEN,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;gBACrC,CAAC;YACH,CAAC,CAAC;YACF,MAAM,IAAI,EAAE,CAAC;QACf,CAAC,CAAC,CAAC;QAKH,MAAM,UAAU,GAAG,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjC,EAAU,CAAC,EAAE,GAAG,CAAC,KAAa,EAAE,QAAkC,EAAE,EAAE;YACrE,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;gBACrC,OAAO,EAAE,CAAC;YACZ,CAAC;YACD,OAAO,UAAU,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACrC,CAAC,CAAC;IACJ,CAAC;CACF;AAjFD,8CAiFC"}
@@ -0,0 +1,3 @@
1
+ import { WorkerPasskeyAuthService } from "./WorkerPasskeyAuth";
2
+ import { WsMiddlewareFn } from "./WsMiddleware";
3
+ export declare function makeAuthMiddleware(authService: WorkerPasskeyAuthService): WsMiddlewareFn;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeAuthMiddleware = makeAuthMiddleware;
4
+ function makeAuthMiddleware(authService) {
5
+ return async (ws, data, next) => {
6
+ if (!authService.isLocked()) {
7
+ return next();
8
+ }
9
+ let parsed;
10
+ try {
11
+ const raw = typeof data === "string" ? data : data.toString("utf-8");
12
+ parsed = JSON.parse(raw);
13
+ }
14
+ catch {
15
+ ws.send(JSON.stringify({
16
+ type: "auth:locked",
17
+ message: "Worker is locked. Send { type: 'auth:getChallenge' } first.",
18
+ }));
19
+ return;
20
+ }
21
+ if (parsed.type === "auth:getChallenge") {
22
+ const challenge = authService.generateChallenge();
23
+ ws.send(JSON.stringify({
24
+ type: "auth:challenge",
25
+ challenge,
26
+ credentialId: authService.getCredentialId(),
27
+ timestamp: Math.floor(Date.now() / 1000),
28
+ }));
29
+ return;
30
+ }
31
+ if (parsed.type === "auth:response") {
32
+ const result = await authService.unlock({
33
+ signature: parsed.signature,
34
+ credentialId: parsed.credentialId,
35
+ authenticatorData: parsed.authenticatorData,
36
+ clientDataJSON: parsed.clientDataJSON,
37
+ challenge: parsed.challenge,
38
+ });
39
+ if (result.success) {
40
+ const info = authService.getSessionInfo();
41
+ ws.send(JSON.stringify({
42
+ type: "auth:success",
43
+ expiresAt: info.expiresAt,
44
+ }));
45
+ }
46
+ else {
47
+ ws.send(JSON.stringify({
48
+ type: "auth:failure",
49
+ reason: result.reason ?? "unknown",
50
+ }));
51
+ }
52
+ return;
53
+ }
54
+ ws.send(JSON.stringify({
55
+ type: "auth:locked",
56
+ message: "Worker is locked. Send { type: 'auth:getChallenge' } to authenticate.",
57
+ }));
58
+ };
59
+ }
60
+ //# sourceMappingURL=authMiddleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authMiddleware.js","sourceRoot":"","sources":["../../../../src/workers/auth/authMiddleware.ts"],"names":[],"mappings":";;AAyBA,gDA8EC;AA9ED,SAAgB,kBAAkB,CAChC,WAAqC;IAErC,OAAO,KAAK,EAAE,EAAa,EAAE,IAAqB,EAAE,IAAI,EAAE,EAAE;QAE1D,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,EAAE,CAAC;YAC5B,OAAO,IAAI,EAAE,CAAC;QAChB,CAAC;QAGD,IAAI,MAAW,CAAC;QAChB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YACrE,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,EAAE,CAAC,IAAI,CACL,IAAI,CAAC,SAAS,CAAC;gBACb,IAAI,EAAE,aAAa;gBACnB,OAAO,EAAE,6DAA6D;aACvE,CAAC,CACH,CAAC;YACF,OAAO;QACT,CAAC;QAGD,IAAI,MAAM,CAAC,IAAI,KAAK,mBAAmB,EAAE,CAAC;YACxC,MAAM,SAAS,GAAG,WAAW,CAAC,iBAAiB,EAAE,CAAC;YAClD,EAAE,CAAC,IAAI,CACL,IAAI,CAAC,SAAS,CAAC;gBACb,IAAI,EAAE,gBAAgB;gBACtB,SAAS;gBACT,YAAY,EAAE,WAAW,CAAC,eAAe,EAAE;gBAC3C,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;aACzC,CAAC,CACH,CAAC;YACF,OAAO;QACT,CAAC;QAGD,IAAI,MAAM,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;YACpC,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC;gBACtC,SAAS,EAAE,MAAM,CAAC,SAAS;gBAC3B,YAAY,EAAE,MAAM,CAAC,YAAY;gBACjC,iBAAiB,EAAE,MAAM,CAAC,iBAAiB;gBAC3C,cAAc,EAAE,MAAM,CAAC,cAAc;gBACrC,SAAS,EAAE,MAAM,CAAC,SAAS;aAC5B,CAAC,CAAC;YAEH,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBACnB,MAAM,IAAI,GAAG,WAAW,CAAC,cAAc,EAAE,CAAC;gBAC1C,EAAE,CAAC,IAAI,CACL,IAAI,CAAC,SAAS,CAAC;oBACb,IAAI,EAAE,cAAc;oBACpB,SAAS,EAAE,IAAI,CAAC,SAAS;iBAC1B,CAAC,CACH,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,EAAE,CAAC,IAAI,CACL,IAAI,CAAC,SAAS,CAAC;oBACb,IAAI,EAAE,cAAc;oBACpB,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,SAAS;iBACnC,CAAC,CACH,CAAC;YACJ,CAAC;YAED,OAAO;QACT,CAAC;QAGD,EAAE,CAAC,IAAI,CACL,IAAI,CAAC,SAAS,CAAC;YACb,IAAI,EAAE,aAAa;YACnB,OAAO,EACL,uEAAuE;SAC1E,CAAC,CACH,CAAC;IAEJ,CAAC,CAAC;AACJ,CAAC"}
@@ -1,3 +1,10 @@
1
+ export interface AuthGetChallengeMessage {
2
+ type: "auth:getChallenge";
3
+ }
4
+ export interface AuthLockedMessage {
5
+ type: "auth:locked";
6
+ message: string;
7
+ }
1
8
  export interface AuthChallengeMessage {
2
9
  type: "auth:challenge";
3
10
  challenge: string;
@@ -20,7 +27,7 @@ export interface AuthFailureMessage {
20
27
  type: "auth:failure";
21
28
  reason: "invalid_signature" | "expired" | "unknown_credential";
22
29
  }
23
- export type AuthMessage = AuthChallengeMessage | AuthResponseMessage | AuthSuccessMessage | AuthFailureMessage;
30
+ export type AuthMessage = AuthGetChallengeMessage | AuthChallengeMessage | AuthResponseMessage | AuthSuccessMessage | AuthFailureMessage | AuthLockedMessage;
24
31
  export interface PasskeyCredential {
25
32
  publicKey: string;
26
33
  credentialId: string;
@@ -2,10 +2,12 @@ export * from "./listAllowedPorts";
2
2
  export * from "./getChallenge";
3
3
  export * from "./unlock";
4
4
  export * from "./lock";
5
+ export * from "./reloadConfig";
5
6
  import { listAllowedPorts } from "./listAllowedPorts";
6
7
  export { makeGetChallengeTool } from "./getChallenge";
7
8
  export { makeUnlockTool } from "./unlock";
8
9
  export { makeLockTool } from "./lock";
10
+ export { makeReloadConfigTool } from "./reloadConfig";
9
11
  declare const _default: {
10
12
  tools: {
11
13
  listAllowedPorts: typeof listAllowedPorts;
@@ -14,11 +14,12 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.makeLockTool = exports.makeUnlockTool = exports.makeGetChallengeTool = void 0;
17
+ exports.makeReloadConfigTool = exports.makeLockTool = exports.makeUnlockTool = exports.makeGetChallengeTool = void 0;
18
18
  __exportStar(require("./listAllowedPorts"), exports);
19
19
  __exportStar(require("./getChallenge"), exports);
20
20
  __exportStar(require("./unlock"), exports);
21
21
  __exportStar(require("./lock"), exports);
22
+ __exportStar(require("./reloadConfig"), exports);
22
23
  const listAllowedPorts_1 = require("./listAllowedPorts");
23
24
  var getChallenge_1 = require("./getChallenge");
24
25
  Object.defineProperty(exports, "makeGetChallengeTool", { enumerable: true, get: function () { return getChallenge_1.makeGetChallengeTool; } });
@@ -26,6 +27,8 @@ var unlock_1 = require("./unlock");
26
27
  Object.defineProperty(exports, "makeUnlockTool", { enumerable: true, get: function () { return unlock_1.makeUnlockTool; } });
27
28
  var lock_1 = require("./lock");
28
29
  Object.defineProperty(exports, "makeLockTool", { enumerable: true, get: function () { return lock_1.makeLockTool; } });
30
+ var reloadConfig_1 = require("./reloadConfig");
31
+ Object.defineProperty(exports, "makeReloadConfigTool", { enumerable: true, get: function () { return reloadConfig_1.makeReloadConfigTool; } });
29
32
  exports.default = {
30
33
  tools: { listAllowedPorts: listAllowedPorts_1.listAllowedPorts },
31
34
  definitions: [listAllowedPorts_1.listAllowedPortsDefinition],
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/workers/tools/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,qDAAmC;AACnC,iDAA+B;AAC/B,2CAAyB;AACzB,yCAAuB;AAEvB,yDAG4B;AAE5B,+CAAsD;AAA7C,oHAAA,oBAAoB,OAAA;AAC7B,mCAA0C;AAAjC,wGAAA,cAAc,OAAA;AACvB,+BAAsC;AAA7B,oGAAA,YAAY,OAAA;AAErB,kBAAe;IACb,KAAK,EAAE,EAAE,gBAAgB,EAAhB,mCAAgB,EAAE;IAC3B,WAAW,EAAE,CAAC,6CAA0B,CAAC;CAC1C,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/workers/tools/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,qDAAmC;AACnC,iDAA+B;AAC/B,2CAAyB;AACzB,yCAAuB;AACvB,iDAA+B;AAE/B,yDAG4B;AAE5B,+CAAsD;AAA7C,oHAAA,oBAAoB,OAAA;AAC7B,mCAA0C;AAAjC,wGAAA,cAAc,OAAA;AACvB,+BAAsC;AAA7B,oGAAA,YAAY,OAAA;AACrB,+CAAsD;AAA7C,oHAAA,oBAAoB,OAAA;AAE7B,kBAAe;IACb,KAAK,EAAE,EAAE,gBAAgB,EAAhB,mCAAgB,EAAE;IAC3B,WAAW,EAAE,CAAC,6CAA0B,CAAC;CAC1C,CAAC"}
@@ -0,0 +1,14 @@
1
+ import { Tool } from "../../clients/types";
2
+ import { McpServerService } from "../../services/McpServer";
3
+ import { McpService } from "../../services/Mcp";
4
+ import { ToolsService } from "../../services/Tools";
5
+ export interface ReloadConfigResult {
6
+ success: boolean;
7
+ toolCount: number;
8
+ mcpCount: number;
9
+ message: string;
10
+ }
11
+ export declare function makeReloadConfigTool(Mcp: McpService, Tools: ToolsService, mcpServer: McpServerService, setToolsToUse: (tools: ReturnType<typeof Tools.getToolsByNames>) => void): {
12
+ reloadConfig: () => Promise<ReloadConfigResult>;
13
+ reloadConfigDefinition: Tool;
14
+ };
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeReloadConfigTool = makeReloadConfigTool;
4
+ const config_1 = require("../../config");
5
+ function makeReloadConfigTool(Mcp, Tools, mcpServer, setToolsToUse) {
6
+ const reloadConfig = async () => {
7
+ try {
8
+ const freshConfig = await (0, config_1.getConfig)();
9
+ await Mcp.closeAll();
10
+ await Mcp.connectToConfigured(Tools);
11
+ const allowedToolNames = freshConfig.worker?.allowedTools ?? Tools.getToolNames();
12
+ const newToolsToUse = Tools.getToolsByNames(allowedToolNames);
13
+ setToolsToUse(newToolsToUse);
14
+ mcpServer.withTools(newToolsToUse);
15
+ const mcpCount = freshConfig.mcps?.length ?? 0;
16
+ return {
17
+ success: true,
18
+ toolCount: newToolsToUse.length,
19
+ mcpCount,
20
+ message: `Config reloaded: ${newToolsToUse.length} tools active, ${mcpCount} MCP(s) configured`,
21
+ };
22
+ }
23
+ catch (err) {
24
+ const message = err instanceof Error ? err.message : String(err);
25
+ return {
26
+ success: false,
27
+ toolCount: 0,
28
+ mcpCount: 0,
29
+ message: `Failed to reload config: ${message}`,
30
+ };
31
+ }
32
+ };
33
+ const reloadConfigDefinition = {
34
+ type: "function",
35
+ function: {
36
+ name: "reloadConfig",
37
+ description: "Reload the worker config from disk, reconnect all MCPs, and rebuild the active tool list. " +
38
+ "Call this after running `knowhow cloudworker --pull <id>` to apply updated MCPs without restarting the worker.",
39
+ parameters: {
40
+ type: "object",
41
+ properties: {},
42
+ required: [],
43
+ },
44
+ },
45
+ };
46
+ return { reloadConfig, reloadConfigDefinition };
47
+ }
48
+ //# sourceMappingURL=reloadConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reloadConfig.js","sourceRoot":"","sources":["../../../../src/workers/tools/reloadConfig.ts"],"names":[],"mappings":";;AAsBA,oDA6DC;AAlFD,yCAAyC;AAqBzC,SAAgB,oBAAoB,CAClC,GAAe,EACf,KAAmB,EACnB,SAA2B,EAC3B,aAAwE;IAExE,MAAM,YAAY,GAAG,KAAK,IAAiC,EAAE;QAC3D,IAAI,CAAC;YAEH,MAAM,WAAW,GAAG,MAAM,IAAA,kBAAS,GAAE,CAAC;YAGtC,MAAM,GAAG,CAAC,QAAQ,EAAE,CAAC;YAGrB,MAAM,GAAG,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;YAGrC,MAAM,gBAAgB,GACpB,WAAW,CAAC,MAAM,EAAE,YAAY,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;YAC3D,MAAM,aAAa,GAAG,KAAK,CAAC,eAAe,CAAC,gBAAgB,CAAC,CAAC;YAC9D,aAAa,CAAC,aAAa,CAAC,CAAC;YAG7B,SAAS,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;YAEnC,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC,CAAC;YAE/C,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,SAAS,EAAE,aAAa,CAAC,MAAM;gBAC/B,QAAQ;gBACR,OAAO,EAAE,oBAAoB,aAAa,CAAC,MAAM,kBAAkB,QAAQ,oBAAoB;aAChG,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjE,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,SAAS,EAAE,CAAC;gBACZ,QAAQ,EAAE,CAAC;gBACX,OAAO,EAAE,4BAA4B,OAAO,EAAE;aAC/C,CAAC;QACJ,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,sBAAsB,GAAS;QACnC,IAAI,EAAE,UAAmB;QACzB,QAAQ,EAAE;YACR,IAAI,EAAE,cAAc;YACpB,WAAW,EACT,4FAA4F;gBAC5F,gHAAgH;YAClH,UAAU,EAAE;gBACV,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE,EAAE;gBACd,QAAQ,EAAE,EAAE;aACb;SACF;KACF,CAAC;IAEF,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,CAAC;AAClD,CAAC"}
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const Mcp_1 = require("../../src/services/Mcp");
4
+ const Tools_1 = require("../../src/services/Tools");
5
+ async function simulateReloadConfig(Mcp, Tools, mcpServer, getConfig, toolsToUseRef) {
6
+ const freshConfig = await getConfig();
7
+ await Mcp.closeAll();
8
+ await Mcp.connectToConfigured(Tools);
9
+ const allowedToolNames = freshConfig.worker?.allowedTools ?? Tools.getToolNames();
10
+ toolsToUseRef.value = Tools.getToolsByNames(allowedToolNames);
11
+ mcpServer.withTools(toolsToUseRef.value);
12
+ }
13
+ describe("Worker reloadConfig handler", () => {
14
+ let Mcp;
15
+ let Tools;
16
+ let mcpServer;
17
+ let toolsToUseRef;
18
+ beforeEach(() => {
19
+ Mcp = new Mcp_1.McpService();
20
+ Tools = new Tools_1.ToolsService();
21
+ mcpServer = { withTools: jest.fn() };
22
+ toolsToUseRef = { value: [] };
23
+ jest.spyOn(Mcp, "closeAll").mockResolvedValue(undefined);
24
+ jest.spyOn(Mcp, "connectToConfigured").mockResolvedValue(undefined);
25
+ });
26
+ afterEach(() => {
27
+ jest.restoreAllMocks();
28
+ });
29
+ it("should call closeAll() to tear down existing MCP connections", async () => {
30
+ const getConfig = jest
31
+ .fn()
32
+ .mockResolvedValue({ worker: { allowedTools: [] } });
33
+ await simulateReloadConfig(Mcp, Tools, mcpServer, getConfig, toolsToUseRef);
34
+ expect(Mcp.closeAll).toHaveBeenCalledTimes(1);
35
+ });
36
+ it("should call connectToConfigured() to reconnect MCPs from fresh config", async () => {
37
+ const getConfig = jest
38
+ .fn()
39
+ .mockResolvedValue({ worker: { allowedTools: [] } });
40
+ await simulateReloadConfig(Mcp, Tools, mcpServer, getConfig, toolsToUseRef);
41
+ expect(Mcp.connectToConfigured).toHaveBeenCalledWith(Tools);
42
+ });
43
+ it("should rebuild the tool list from allowedTools in fresh config", async () => {
44
+ const toolsByNamesSpy = jest
45
+ .spyOn(Tools, "getToolsByNames")
46
+ .mockReturnValue([{ function: { name: "execCommand" } }]);
47
+ const getConfig = jest
48
+ .fn()
49
+ .mockResolvedValue({ worker: { allowedTools: ["execCommand"] } });
50
+ await simulateReloadConfig(Mcp, Tools, mcpServer, getConfig, toolsToUseRef);
51
+ expect(toolsByNamesSpy).toHaveBeenCalledWith(["execCommand"]);
52
+ expect(toolsToUseRef.value).toHaveLength(1);
53
+ });
54
+ it("should fall back to all tool names when allowedTools is not set", async () => {
55
+ const allNames = ["execCommand", "readFile", "writeFileChunk"];
56
+ jest.spyOn(Tools, "getToolNames").mockReturnValue(allNames);
57
+ const toolsByNamesSpy = jest
58
+ .spyOn(Tools, "getToolsByNames")
59
+ .mockReturnValue([]);
60
+ const getConfig = jest.fn().mockResolvedValue({});
61
+ await simulateReloadConfig(Mcp, Tools, mcpServer, getConfig, toolsToUseRef);
62
+ expect(toolsByNamesSpy).toHaveBeenCalledWith(allNames);
63
+ });
64
+ it("should call mcpServer.withTools() with the rebuilt tool list", async () => {
65
+ const fakeTools = [{ function: { name: "readFile" } }];
66
+ jest.spyOn(Tools, "getToolsByNames").mockReturnValue(fakeTools);
67
+ const getConfig = jest
68
+ .fn()
69
+ .mockResolvedValue({ worker: { allowedTools: ["readFile"] } });
70
+ await simulateReloadConfig(Mcp, Tools, mcpServer, getConfig, toolsToUseRef);
71
+ expect(mcpServer.withTools).toHaveBeenCalledWith(fakeTools);
72
+ });
73
+ it("should re-read the config on every reload (not use stale config)", async () => {
74
+ const getConfig = jest
75
+ .fn()
76
+ .mockResolvedValueOnce({ worker: { allowedTools: ["execCommand"] } })
77
+ .mockResolvedValueOnce({ worker: { allowedTools: ["readFile", "writeFileChunk"] } });
78
+ jest.spyOn(Tools, "getToolsByNames").mockReturnValue([]);
79
+ await simulateReloadConfig(Mcp, Tools, mcpServer, getConfig, toolsToUseRef);
80
+ await simulateReloadConfig(Mcp, Tools, mcpServer, getConfig, toolsToUseRef);
81
+ expect(getConfig).toHaveBeenCalledTimes(2);
82
+ expect(Mcp.closeAll).toHaveBeenCalledTimes(2);
83
+ expect(Mcp.connectToConfigured).toHaveBeenCalledTimes(2);
84
+ });
85
+ });
86
+ //# sourceMappingURL=WorkerReloadConfig.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WorkerReloadConfig.test.js","sourceRoot":"","sources":["../../../tests/services/WorkerReloadConfig.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,339 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ jest.mock("../../../src/config", () => ({
4
+ getConfig: jest.fn().mockResolvedValue({ modules: [] }),
5
+ getGlobalConfig: jest.fn().mockResolvedValue({ modules: [] }),
6
+ getConfigSync: jest.fn().mockReturnValue({}),
7
+ }));
8
+ jest.mock("../../../src/services/KnowhowClient", () => ({
9
+ loadKnowhowJwt: jest.fn().mockReturnValue(null),
10
+ KNOWHOW_API_URL: "https://mock.local",
11
+ }));
12
+ const index_1 = require("../../../src/clients/index");
13
+ const mockCompletion = () => ({
14
+ choices: [{ message: { role: "assistant", content: "hello" } }],
15
+ model: "mock-model",
16
+ usage: { prompt_tokens: 10, completion_tokens: 5 },
17
+ });
18
+ const mockImage = () => ({
19
+ created: Date.now(),
20
+ data: [{ url: "https://mock.local/image.png" }],
21
+ });
22
+ const mockEmbedding = () => ({
23
+ data: [{ object: "embedding", embedding: [0.1, 0.2], index: 0 }],
24
+ model: "mock-embed",
25
+ usage: { prompt_tokens: 5, total_tokens: 5 },
26
+ });
27
+ const mockAudio = () => ({
28
+ audio: Buffer.from("fake-audio"),
29
+ format: "mp3",
30
+ });
31
+ function setupClient(overrides = {}) {
32
+ const mockGenericClient = {
33
+ setKey: jest.fn(),
34
+ createChatCompletion: jest
35
+ .fn()
36
+ .mockResolvedValue(mockCompletion()),
37
+ createEmbedding: jest.fn().mockResolvedValue(mockEmbedding()),
38
+ createImageGeneration: jest.fn().mockResolvedValue(mockImage()),
39
+ createAudioGeneration: jest.fn().mockResolvedValue(mockAudio()),
40
+ createAudioTranscription: jest
41
+ .fn()
42
+ .mockResolvedValue({ text: "transcribed" }),
43
+ createVideoGeneration: jest.fn().mockResolvedValue({
44
+ created: Date.now(),
45
+ data: [{ url: "https://mock.local/video.mp4" }],
46
+ }),
47
+ getModels: jest.fn().mockResolvedValue([]),
48
+ ...overrides,
49
+ };
50
+ const aiClient = new index_1.AIClient();
51
+ aiClient.registerClient("mock", mockGenericClient);
52
+ aiClient.registerModels("mock", ["mock-model", "mock-embed"]);
53
+ return { aiClient, mockGenericClient };
54
+ }
55
+ describe("AIClient — retry / timeout / AbortSignal", () => {
56
+ afterEach(() => {
57
+ jest.useRealTimers();
58
+ });
59
+ describe("createCompletion", () => {
60
+ it("returns a completion on success", async () => {
61
+ const { aiClient } = setupClient();
62
+ const result = await aiClient.createCompletion("mock", {
63
+ model: "mock-model",
64
+ messages: [{ role: "user", content: "hi" }],
65
+ });
66
+ expect(result.choices[0].message.content).toBe("hello");
67
+ });
68
+ it("forwards the AbortSignal to createChatCompletion", async () => {
69
+ const { aiClient, mockGenericClient } = setupClient();
70
+ const controller = new AbortController();
71
+ await aiClient.createCompletion("mock", {
72
+ model: "mock-model",
73
+ messages: [],
74
+ signal: controller.signal,
75
+ });
76
+ const callArgs = mockGenericClient.createChatCompletion
77
+ .mock.calls[0][0];
78
+ expect(callArgs.signal).toBeInstanceOf(AbortSignal);
79
+ });
80
+ it("retries on 429 and succeeds", async () => {
81
+ jest.useFakeTimers();
82
+ const { aiClient, mockGenericClient } = setupClient({
83
+ createChatCompletion: jest
84
+ .fn()
85
+ .mockRejectedValueOnce(new Error("429 rate limited"))
86
+ .mockResolvedValueOnce(mockCompletion()),
87
+ });
88
+ const promise = aiClient.createCompletion("mock", {
89
+ model: "mock-model",
90
+ messages: [],
91
+ maxRetries: 2,
92
+ backoffMs: 50,
93
+ });
94
+ await jest.runAllTimersAsync();
95
+ const result = await promise;
96
+ expect(result.choices[0].message.content).toBe("hello");
97
+ expect(mockGenericClient.createChatCompletion).toHaveBeenCalledTimes(2);
98
+ });
99
+ it("aborts immediately when external signal is pre-aborted", async () => {
100
+ const controller = new AbortController();
101
+ controller.abort();
102
+ const { aiClient, mockGenericClient } = setupClient();
103
+ await expect(aiClient.createCompletion("mock", {
104
+ model: "mock-model",
105
+ messages: [],
106
+ signal: controller.signal,
107
+ })).rejects.toMatchObject({ name: "AbortError" });
108
+ expect(mockGenericClient.createChatCompletion).not.toHaveBeenCalled();
109
+ });
110
+ it("cancels in-flight request when external signal is aborted", async () => {
111
+ const controller = new AbortController();
112
+ const { aiClient, mockGenericClient } = setupClient({
113
+ createChatCompletion: jest.fn().mockImplementation((opts) => {
114
+ return new Promise((_, reject) => {
115
+ opts.signal?.addEventListener("abort", () => reject(opts.signal.reason));
116
+ });
117
+ }),
118
+ });
119
+ const promise = aiClient.createCompletion("mock", {
120
+ model: "mock-model",
121
+ messages: [],
122
+ signal: controller.signal,
123
+ });
124
+ setImmediate(() => controller.abort(new DOMException("User cancelled", "AbortError")));
125
+ await expect(promise).rejects.toMatchObject({ name: "AbortError" });
126
+ expect(mockGenericClient.createChatCompletion).toHaveBeenCalledTimes(1);
127
+ });
128
+ it("times out per-attempt and retries", async () => {
129
+ jest.useFakeTimers();
130
+ const { aiClient, mockGenericClient } = setupClient({
131
+ createChatCompletion: jest
132
+ .fn()
133
+ .mockImplementationOnce((opts) => {
134
+ return new Promise((_, reject) => {
135
+ opts.signal?.addEventListener("abort", () => reject(opts.signal.reason));
136
+ });
137
+ })
138
+ .mockResolvedValueOnce(mockCompletion()),
139
+ });
140
+ const promise = aiClient.createCompletion("mock", {
141
+ model: "mock-model",
142
+ messages: [],
143
+ timeout: 1000,
144
+ maxRetries: 2,
145
+ backoffMs: 10,
146
+ });
147
+ await jest.runAllTimersAsync();
148
+ const result = await promise;
149
+ expect(result.choices[0].message.content).toBe("hello");
150
+ expect(mockGenericClient.createChatCompletion).toHaveBeenCalledTimes(2);
151
+ });
152
+ });
153
+ describe("createEmbedding", () => {
154
+ it("forwards the AbortSignal to createEmbedding on the client", async () => {
155
+ const { aiClient, mockGenericClient } = setupClient();
156
+ const controller = new AbortController();
157
+ await aiClient.createEmbedding("mock", {
158
+ input: "test text",
159
+ model: "mock-embed",
160
+ signal: controller.signal,
161
+ });
162
+ const callArgs = mockGenericClient.createEmbedding.mock
163
+ .calls[0][0];
164
+ expect(callArgs.signal).toBeInstanceOf(AbortSignal);
165
+ });
166
+ it("retries on 500 and succeeds", async () => {
167
+ jest.useFakeTimers();
168
+ const { aiClient, mockGenericClient } = setupClient({
169
+ createEmbedding: jest
170
+ .fn()
171
+ .mockRejectedValueOnce(new Error("500 Internal Server Error"))
172
+ .mockResolvedValueOnce(mockEmbedding()),
173
+ });
174
+ const promise = aiClient.createEmbedding("mock", {
175
+ input: "test",
176
+ model: "mock-embed",
177
+ maxRetries: 2,
178
+ backoffMs: 50,
179
+ });
180
+ await jest.runAllTimersAsync();
181
+ const result = await promise;
182
+ expect(result.data[0].embedding).toEqual([0.1, 0.2]);
183
+ expect(mockGenericClient.createEmbedding).toHaveBeenCalledTimes(2);
184
+ });
185
+ });
186
+ describe("createImageGeneration", () => {
187
+ it("forwards the AbortSignal to createImageGeneration on the client", async () => {
188
+ const { aiClient, mockGenericClient } = setupClient();
189
+ const controller = new AbortController();
190
+ await aiClient.createImageGeneration("mock", {
191
+ model: "mock-model",
192
+ prompt: "a cat",
193
+ signal: controller.signal,
194
+ });
195
+ const callArgs = mockGenericClient.createImageGeneration
196
+ .mock.calls[0][0];
197
+ expect(callArgs.signal).toBeInstanceOf(AbortSignal);
198
+ });
199
+ it("retries on 429 and succeeds", async () => {
200
+ jest.useFakeTimers();
201
+ const { aiClient, mockGenericClient } = setupClient({
202
+ createImageGeneration: jest
203
+ .fn()
204
+ .mockRejectedValueOnce(new Error("429 Too Many Requests"))
205
+ .mockResolvedValueOnce(mockImage()),
206
+ });
207
+ const promise = aiClient.createImageGeneration("mock", {
208
+ model: "mock-model",
209
+ prompt: "a cat",
210
+ maxRetries: 2,
211
+ backoffMs: 50,
212
+ });
213
+ await jest.runAllTimersAsync();
214
+ const result = await promise;
215
+ expect(result.data[0].url).toBe("https://mock.local/image.png");
216
+ expect(mockGenericClient.createImageGeneration).toHaveBeenCalledTimes(2);
217
+ });
218
+ it("aborts when external signal fires mid-request", async () => {
219
+ const controller = new AbortController();
220
+ const { aiClient, mockGenericClient } = setupClient({
221
+ createImageGeneration: jest.fn().mockImplementation((opts) => {
222
+ return new Promise((_, reject) => {
223
+ opts.signal?.addEventListener("abort", () => reject(opts.signal.reason));
224
+ });
225
+ }),
226
+ });
227
+ const promise = aiClient.createImageGeneration("mock", {
228
+ model: "mock-model",
229
+ prompt: "a cat",
230
+ signal: controller.signal,
231
+ });
232
+ setImmediate(() => controller.abort(new DOMException("User cancelled", "AbortError")));
233
+ await expect(promise).rejects.toMatchObject({ name: "AbortError" });
234
+ expect(mockGenericClient.createImageGeneration).toHaveBeenCalledTimes(1);
235
+ });
236
+ });
237
+ describe("createAudioGeneration", () => {
238
+ it("forwards the AbortSignal to createAudioGeneration on the client", async () => {
239
+ const { aiClient, mockGenericClient } = setupClient();
240
+ const controller = new AbortController();
241
+ await aiClient.createAudioGeneration("mock", {
242
+ model: "mock-model",
243
+ input: "Hello world",
244
+ voice: "alloy",
245
+ signal: controller.signal,
246
+ });
247
+ const callArgs = mockGenericClient.createAudioGeneration
248
+ .mock.calls[0][0];
249
+ expect(callArgs.signal).toBeInstanceOf(AbortSignal);
250
+ });
251
+ it("retries on ECONNRESET and succeeds", async () => {
252
+ jest.useFakeTimers();
253
+ const { aiClient, mockGenericClient } = setupClient({
254
+ createAudioGeneration: jest
255
+ .fn()
256
+ .mockRejectedValueOnce(new Error("ECONNRESET"))
257
+ .mockResolvedValueOnce(mockAudio()),
258
+ });
259
+ const promise = aiClient.createAudioGeneration("mock", {
260
+ model: "mock-model",
261
+ input: "Hello",
262
+ voice: "alloy",
263
+ maxRetries: 2,
264
+ backoffMs: 50,
265
+ });
266
+ await jest.runAllTimersAsync();
267
+ const result = await promise;
268
+ expect(result.format).toBe("mp3");
269
+ expect(mockGenericClient.createAudioGeneration).toHaveBeenCalledTimes(2);
270
+ });
271
+ });
272
+ describe("createVideoGeneration", () => {
273
+ it("forwards the AbortSignal to createVideoGeneration on the client", async () => {
274
+ const { aiClient, mockGenericClient } = setupClient();
275
+ const controller = new AbortController();
276
+ await aiClient.createVideoGeneration("mock", {
277
+ model: "mock-model",
278
+ prompt: "a sunset",
279
+ signal: controller.signal,
280
+ });
281
+ const callArgs = mockGenericClient.createVideoGeneration
282
+ .mock.calls[0][0];
283
+ expect(callArgs.signal).toBeInstanceOf(AbortSignal);
284
+ });
285
+ it("retries on 503 and succeeds", async () => {
286
+ jest.useFakeTimers();
287
+ const { aiClient, mockGenericClient } = setupClient({
288
+ createVideoGeneration: jest
289
+ .fn()
290
+ .mockRejectedValueOnce(new Error("503 Service Unavailable"))
291
+ .mockResolvedValueOnce({
292
+ created: Date.now(),
293
+ data: [{ url: "https://mock.local/video.mp4" }],
294
+ }),
295
+ });
296
+ const promise = aiClient.createVideoGeneration("mock", {
297
+ model: "mock-model",
298
+ prompt: "a sunset",
299
+ maxRetries: 2,
300
+ backoffMs: 50,
301
+ });
302
+ await jest.runAllTimersAsync();
303
+ const result = await promise;
304
+ expect(result.data[0].url).toBe("https://mock.local/video.mp4");
305
+ expect(mockGenericClient.createVideoGeneration).toHaveBeenCalledTimes(2);
306
+ });
307
+ });
308
+ describe("createAudioTranscription", () => {
309
+ it("forwards the AbortSignal to createAudioTranscription on the client", async () => {
310
+ const { aiClient, mockGenericClient } = setupClient();
311
+ const controller = new AbortController();
312
+ await aiClient.createAudioTranscription("mock", {
313
+ file: Buffer.from("fake-audio"),
314
+ signal: controller.signal,
315
+ });
316
+ const callArgs = mockGenericClient.createAudioTranscription.mock.calls[0][0];
317
+ expect(callArgs.signal).toBeInstanceOf(AbortSignal);
318
+ });
319
+ it("retries on timeout error and succeeds", async () => {
320
+ jest.useFakeTimers();
321
+ const { aiClient, mockGenericClient } = setupClient({
322
+ createAudioTranscription: jest
323
+ .fn()
324
+ .mockRejectedValueOnce(new Error("timeout"))
325
+ .mockResolvedValueOnce({ text: "hello world" }),
326
+ });
327
+ const promise = aiClient.createAudioTranscription("mock", {
328
+ file: Buffer.from("fake-audio"),
329
+ maxRetries: 2,
330
+ backoffMs: 50,
331
+ });
332
+ await jest.runAllTimersAsync();
333
+ const result = await promise;
334
+ expect(result.text).toBe("hello world");
335
+ expect(mockGenericClient.createAudioTranscription).toHaveBeenCalledTimes(2);
336
+ });
337
+ });
338
+ });
339
+ //# sourceMappingURL=AIClient.test.js.map