@united-workforce/cli 0.3.0 → 0.5.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 (319) hide show
  1. package/README.md +45 -11
  2. package/dist/.build-fingerprint +1 -0
  3. package/dist/__tests__/adapter-json-roundtrip.test.js +17 -7
  4. package/dist/__tests__/adapter-json-roundtrip.test.js.map +1 -1
  5. package/dist/__tests__/agent-resolution-llm-free.test.d.ts +2 -0
  6. package/dist/__tests__/agent-resolution-llm-free.test.d.ts.map +1 -0
  7. package/dist/__tests__/agent-resolution-llm-free.test.js +30 -0
  8. package/dist/__tests__/agent-resolution-llm-free.test.js.map +1 -0
  9. package/dist/__tests__/build-step-entry.test.d.ts +2 -0
  10. package/dist/__tests__/build-step-entry.test.d.ts.map +1 -0
  11. package/dist/__tests__/build-step-entry.test.js +173 -0
  12. package/dist/__tests__/build-step-entry.test.js.map +1 -0
  13. package/dist/__tests__/clear-thread-failed-attempts.test.d.ts +2 -0
  14. package/dist/__tests__/clear-thread-failed-attempts.test.d.ts.map +1 -0
  15. package/dist/__tests__/clear-thread-failed-attempts.test.js +93 -0
  16. package/dist/__tests__/clear-thread-failed-attempts.test.js.map +1 -0
  17. package/dist/__tests__/concurrency.test.d.ts +2 -0
  18. package/dist/__tests__/concurrency.test.d.ts.map +1 -0
  19. package/dist/__tests__/concurrency.test.js +196 -0
  20. package/dist/__tests__/concurrency.test.js.map +1 -0
  21. package/dist/__tests__/config.test.js +26 -302
  22. package/dist/__tests__/config.test.js.map +1 -1
  23. package/dist/__tests__/current-role.test.js +7 -6
  24. package/dist/__tests__/current-role.test.js.map +1 -1
  25. package/dist/__tests__/e2e-mock-agent.test.js +43 -30
  26. package/dist/__tests__/e2e-mock-agent.test.js.map +1 -1
  27. package/dist/__tests__/format-text-default.test.d.ts +2 -0
  28. package/dist/__tests__/format-text-default.test.d.ts.map +1 -0
  29. package/dist/__tests__/format-text-default.test.js +43 -0
  30. package/dist/__tests__/format-text-default.test.js.map +1 -0
  31. package/dist/__tests__/format-text-registry.test.d.ts +2 -0
  32. package/dist/__tests__/format-text-registry.test.d.ts.map +1 -0
  33. package/dist/__tests__/format-text-registry.test.js +158 -0
  34. package/dist/__tests__/format-text-registry.test.js.map +1 -0
  35. package/dist/__tests__/issue-180-workflow-ref-removed.test.d.ts +2 -0
  36. package/dist/__tests__/issue-180-workflow-ref-removed.test.d.ts.map +1 -0
  37. package/dist/__tests__/issue-180-workflow-ref-removed.test.js +40 -0
  38. package/dist/__tests__/issue-180-workflow-ref-removed.test.js.map +1 -0
  39. package/dist/__tests__/log-text-renderer.test.d.ts +2 -0
  40. package/dist/__tests__/log-text-renderer.test.d.ts.map +1 -0
  41. package/dist/__tests__/log-text-renderer.test.js +265 -0
  42. package/dist/__tests__/log-text-renderer.test.js.map +1 -0
  43. package/dist/__tests__/moderator-evaluate.test.js +9 -50
  44. package/dist/__tests__/moderator-evaluate.test.js.map +1 -1
  45. package/dist/__tests__/output-mapper-thread-list-startedat.test.d.ts +2 -0
  46. package/dist/__tests__/output-mapper-thread-list-startedat.test.d.ts.map +1 -0
  47. package/dist/__tests__/output-mapper-thread-list-startedat.test.js +102 -0
  48. package/dist/__tests__/output-mapper-thread-list-startedat.test.js.map +1 -0
  49. package/dist/__tests__/output-mapper-workflow-add.test.d.ts +2 -0
  50. package/dist/__tests__/output-mapper-workflow-add.test.d.ts.map +1 -0
  51. package/dist/__tests__/output-mapper-workflow-add.test.js +22 -0
  52. package/dist/__tests__/output-mapper-workflow-add.test.js.map +1 -0
  53. package/dist/__tests__/pid-recycling.test.d.ts +2 -0
  54. package/dist/__tests__/pid-recycling.test.d.ts.map +1 -0
  55. package/dist/__tests__/pid-recycling.test.js +273 -0
  56. package/dist/__tests__/pid-recycling.test.js.map +1 -0
  57. package/dist/__tests__/prompt.test.js +365 -2
  58. package/dist/__tests__/prompt.test.js.map +1 -1
  59. package/dist/__tests__/resolve-head-hash.test.js +12 -4
  60. package/dist/__tests__/resolve-head-hash.test.js.map +1 -1
  61. package/dist/__tests__/setup-agent-discovery.test.js +21 -30
  62. package/dist/__tests__/setup-agent-discovery.test.js.map +1 -1
  63. package/dist/__tests__/setup-complexity.test.js +2 -168
  64. package/dist/__tests__/setup-complexity.test.js.map +1 -1
  65. package/dist/__tests__/setup-no-llm.test.d.ts +2 -0
  66. package/dist/__tests__/setup-no-llm.test.d.ts.map +1 -0
  67. package/dist/__tests__/setup-no-llm.test.js +52 -0
  68. package/dist/__tests__/setup-no-llm.test.js.map +1 -0
  69. package/dist/__tests__/solve-issue-tea-worktree.test.js +27 -28
  70. package/dist/__tests__/solve-issue-tea-worktree.test.js.map +1 -1
  71. package/dist/__tests__/step-ask.test.d.ts +2 -0
  72. package/dist/__tests__/step-ask.test.d.ts.map +1 -0
  73. package/dist/__tests__/step-ask.test.js +507 -0
  74. package/dist/__tests__/step-ask.test.js.map +1 -0
  75. package/dist/__tests__/step-show-json.test.js +1 -0
  76. package/dist/__tests__/step-show-json.test.js.map +1 -1
  77. package/dist/__tests__/step-timing.test.js +2 -0
  78. package/dist/__tests__/step-timing.test.js.map +1 -1
  79. package/dist/__tests__/store-global-cas.test.js +2 -2
  80. package/dist/__tests__/store-global-cas.test.js.map +1 -1
  81. package/dist/__tests__/store-unified-threads.test.js +28 -26
  82. package/dist/__tests__/store-unified-threads.test.js.map +1 -1
  83. package/dist/__tests__/thread-cancel-status.test.js +25 -19
  84. package/dist/__tests__/thread-cancel-status.test.js.map +1 -1
  85. package/dist/__tests__/thread-cancel-text-renderer.test.d.ts +2 -0
  86. package/dist/__tests__/thread-cancel-text-renderer.test.d.ts.map +1 -0
  87. package/dist/__tests__/thread-cancel-text-renderer.test.js +110 -0
  88. package/dist/__tests__/thread-cancel-text-renderer.test.js.map +1 -0
  89. package/dist/__tests__/thread-list-filters.test.js +354 -17
  90. package/dist/__tests__/thread-list-filters.test.js.map +1 -1
  91. package/dist/__tests__/thread-list-template-ms-date.test.d.ts +2 -0
  92. package/dist/__tests__/thread-list-template-ms-date.test.d.ts.map +1 -0
  93. package/dist/__tests__/thread-list-template-ms-date.test.js +102 -0
  94. package/dist/__tests__/thread-list-template-ms-date.test.js.map +1 -0
  95. package/dist/__tests__/thread-list-workflow-corrupt.test.d.ts +2 -0
  96. package/dist/__tests__/thread-list-workflow-corrupt.test.d.ts.map +1 -0
  97. package/dist/__tests__/thread-list-workflow-corrupt.test.js +157 -0
  98. package/dist/__tests__/thread-list-workflow-corrupt.test.js.map +1 -0
  99. package/dist/__tests__/thread-poke.test.d.ts +2 -0
  100. package/dist/__tests__/thread-poke.test.d.ts.map +1 -0
  101. package/dist/__tests__/thread-poke.test.js +422 -0
  102. package/dist/__tests__/thread-poke.test.js.map +1 -0
  103. package/dist/__tests__/thread-read-xml-tags.test.js +10 -9
  104. package/dist/__tests__/thread-read-xml-tags.test.js.map +1 -1
  105. package/dist/__tests__/thread-resume.test.js +21 -15
  106. package/dist/__tests__/thread-resume.test.js.map +1 -1
  107. package/dist/__tests__/thread-show-status.test.js +17 -28
  108. package/dist/__tests__/thread-show-status.test.js.map +1 -1
  109. package/dist/__tests__/thread-start-cwd-cli.test.js +15 -3
  110. package/dist/__tests__/thread-start-cwd-cli.test.js.map +1 -1
  111. package/dist/__tests__/thread-stop-text-renderer.test.d.ts +2 -0
  112. package/dist/__tests__/thread-stop-text-renderer.test.d.ts.map +1 -0
  113. package/dist/__tests__/thread-stop-text-renderer.test.js +148 -0
  114. package/dist/__tests__/thread-stop-text-renderer.test.js.map +1 -0
  115. package/dist/__tests__/thread-suspend-step.test.js +13 -16
  116. package/dist/__tests__/thread-suspend-step.test.js.map +1 -1
  117. package/dist/__tests__/thread-suspended-display.test.js +10 -22
  118. package/dist/__tests__/thread-suspended-display.test.js.map +1 -1
  119. package/dist/__tests__/thread-test-helpers.d.ts +7 -0
  120. package/dist/__tests__/thread-test-helpers.d.ts.map +1 -1
  121. package/dist/__tests__/thread-test-helpers.js +13 -0
  122. package/dist/__tests__/thread-test-helpers.js.map +1 -1
  123. package/dist/__tests__/thread.test.js +15 -13
  124. package/dist/__tests__/thread.test.js.map +1 -1
  125. package/dist/__tests__/validate-semantic.test.js +105 -23
  126. package/dist/__tests__/validate-semantic.test.js.map +1 -1
  127. package/dist/__tests__/workflow-list-recursive.test.d.ts +2 -0
  128. package/dist/__tests__/workflow-list-recursive.test.d.ts.map +1 -0
  129. package/dist/__tests__/workflow-list-recursive.test.js +286 -0
  130. package/dist/__tests__/workflow-list-recursive.test.js.map +1 -0
  131. package/dist/__tests__/workflow-resolution.test.js +46 -28
  132. package/dist/__tests__/workflow-resolution.test.js.map +1 -1
  133. package/dist/__tests__/workflow-show-resolution.test.d.ts +2 -0
  134. package/dist/__tests__/workflow-show-resolution.test.d.ts.map +1 -0
  135. package/dist/__tests__/workflow-show-resolution.test.js +213 -0
  136. package/dist/__tests__/workflow-show-resolution.test.js.map +1 -0
  137. package/dist/__tests__/workflow-validate.test.d.ts +2 -0
  138. package/dist/__tests__/workflow-validate.test.d.ts.map +1 -0
  139. package/dist/__tests__/workflow-validate.test.js +707 -0
  140. package/dist/__tests__/workflow-validate.test.js.map +1 -0
  141. package/dist/__tests__/write-envelope.test.d.ts +2 -0
  142. package/dist/__tests__/write-envelope.test.d.ts.map +1 -0
  143. package/dist/__tests__/write-envelope.test.js +201 -0
  144. package/dist/__tests__/write-envelope.test.js.map +1 -0
  145. package/dist/background/background.d.ts +22 -1
  146. package/dist/background/background.d.ts.map +1 -1
  147. package/dist/background/background.js +83 -6
  148. package/dist/background/background.js.map +1 -1
  149. package/dist/background/index.d.ts +1 -1
  150. package/dist/background/index.d.ts.map +1 -1
  151. package/dist/background/index.js +1 -1
  152. package/dist/background/index.js.map +1 -1
  153. package/dist/background/types.d.ts +1 -0
  154. package/dist/background/types.d.ts.map +1 -1
  155. package/dist/cli.js +120 -62
  156. package/dist/cli.js.map +1 -1
  157. package/dist/commands/config.d.ts +3 -1
  158. package/dist/commands/config.d.ts.map +1 -1
  159. package/dist/commands/config.js +17 -31
  160. package/dist/commands/config.js.map +1 -1
  161. package/dist/commands/prompt.d.ts.map +1 -1
  162. package/dist/commands/prompt.js +57 -31
  163. package/dist/commands/prompt.js.map +1 -1
  164. package/dist/commands/setup.d.ts +12 -39
  165. package/dist/commands/setup.d.ts.map +1 -1
  166. package/dist/commands/setup.js +72 -303
  167. package/dist/commands/setup.js.map +1 -1
  168. package/dist/commands/step.d.ts +44 -1
  169. package/dist/commands/step.d.ts.map +1 -1
  170. package/dist/commands/step.js +255 -11
  171. package/dist/commands/step.js.map +1 -1
  172. package/dist/commands/thread.d.ts +16 -3
  173. package/dist/commands/thread.d.ts.map +1 -1
  174. package/dist/commands/thread.js +423 -142
  175. package/dist/commands/thread.js.map +1 -1
  176. package/dist/commands/workflow.d.ts +9 -1
  177. package/dist/commands/workflow.d.ts.map +1 -1
  178. package/dist/commands/workflow.js +126 -6
  179. package/dist/commands/workflow.js.map +1 -1
  180. package/dist/concurrency/concurrency.d.ts +34 -0
  181. package/dist/concurrency/concurrency.d.ts.map +1 -0
  182. package/dist/concurrency/concurrency.js +216 -0
  183. package/dist/concurrency/concurrency.js.map +1 -0
  184. package/dist/concurrency/index.d.ts +3 -0
  185. package/dist/concurrency/index.d.ts.map +1 -0
  186. package/dist/concurrency/index.js +2 -0
  187. package/dist/concurrency/index.js.map +1 -0
  188. package/dist/concurrency/types.d.ts +19 -0
  189. package/dist/concurrency/types.d.ts.map +1 -0
  190. package/dist/concurrency/types.js +2 -0
  191. package/dist/concurrency/types.js.map +1 -0
  192. package/dist/format.d.ts +69 -2
  193. package/dist/format.d.ts.map +1 -1
  194. package/dist/format.js +198 -1
  195. package/dist/format.js.map +1 -1
  196. package/dist/moderator/__tests__/evaluate.test.js +31 -17
  197. package/dist/moderator/__tests__/evaluate.test.js.map +1 -1
  198. package/dist/moderator/evaluate.d.ts.map +1 -1
  199. package/dist/moderator/evaluate.js +4 -16
  200. package/dist/moderator/evaluate.js.map +1 -1
  201. package/dist/moderator/index.d.ts +1 -2
  202. package/dist/moderator/index.d.ts.map +1 -1
  203. package/dist/moderator/index.js +0 -1
  204. package/dist/moderator/index.js.map +1 -1
  205. package/dist/moderator/types.d.ts +6 -10
  206. package/dist/moderator/types.d.ts.map +1 -1
  207. package/dist/moderator/types.js +1 -3
  208. package/dist/moderator/types.js.map +1 -1
  209. package/dist/output-mappers.d.ts +122 -0
  210. package/dist/output-mappers.d.ts.map +1 -0
  211. package/dist/output-mappers.js +134 -0
  212. package/dist/output-mappers.js.map +1 -0
  213. package/dist/schemas.d.ts +6 -1
  214. package/dist/schemas.d.ts.map +1 -1
  215. package/dist/schemas.js +34 -5
  216. package/dist/schemas.js.map +1 -1
  217. package/dist/store.d.ts +28 -9
  218. package/dist/store.d.ts.map +1 -1
  219. package/dist/store.js +75 -16
  220. package/dist/store.js.map +1 -1
  221. package/dist/text-renderers.d.ts +30 -0
  222. package/dist/text-renderers.d.ts.map +1 -0
  223. package/dist/text-renderers.js +251 -0
  224. package/dist/text-renderers.js.map +1 -0
  225. package/dist/validate-semantic.d.ts.map +1 -1
  226. package/dist/validate-semantic.js +95 -61
  227. package/dist/validate-semantic.js.map +1 -1
  228. package/dist/validate.d.ts +6 -0
  229. package/dist/validate.d.ts.map +1 -1
  230. package/dist/validate.js +24 -0
  231. package/dist/validate.js.map +1 -1
  232. package/examples/brainstorm.yaml +130 -0
  233. package/examples/debate.yaml +169 -0
  234. package/examples/socratic-questioning.yaml +112 -0
  235. package/package.json +9 -10
  236. package/src/__tests__/adapter-json-roundtrip.test.ts +16 -7
  237. package/src/__tests__/agent-resolution-llm-free.test.ts +39 -0
  238. package/src/__tests__/build-step-entry.test.ts +203 -0
  239. package/src/__tests__/clear-thread-failed-attempts.test.ts +122 -0
  240. package/src/__tests__/concurrency.test.ts +266 -0
  241. package/src/__tests__/config.test.ts +33 -321
  242. package/src/__tests__/current-role.test.ts +7 -6
  243. package/src/__tests__/e2e-mock-agent.test.ts +65 -30
  244. package/src/__tests__/fixtures/e2e-count.workflow.yaml +1 -0
  245. package/src/__tests__/fixtures/e2e-linear.workflow.yaml +1 -0
  246. package/src/__tests__/fixtures/{e2e-mustache.workflow.yaml → e2e-liquid.workflow.yaml} +3 -2
  247. package/src/__tests__/fixtures/e2e-loop.workflow.yaml +1 -0
  248. package/src/__tests__/fixtures/e2e-suspend.mock.yaml +2 -2
  249. package/src/__tests__/fixtures/e2e-suspend.workflow.yaml +6 -10
  250. package/src/__tests__/format-text-default.test.ts +49 -0
  251. package/src/__tests__/format-text-registry.test.ts +173 -0
  252. package/src/__tests__/issue-180-workflow-ref-removed.test.ts +43 -0
  253. package/src/__tests__/log-text-renderer.test.ts +294 -0
  254. package/src/__tests__/moderator-evaluate.test.ts +9 -52
  255. package/src/__tests__/output-mapper-thread-list-startedat.test.ts +124 -0
  256. package/src/__tests__/output-mapper-workflow-add.test.ts +24 -0
  257. package/src/__tests__/pid-recycling.test.ts +329 -0
  258. package/src/__tests__/prompt.test.ts +443 -2
  259. package/src/__tests__/resolve-head-hash.test.ts +11 -4
  260. package/src/__tests__/setup-agent-discovery.test.ts +26 -51
  261. package/src/__tests__/setup-complexity.test.ts +1 -203
  262. package/src/__tests__/setup-no-llm.test.ts +68 -0
  263. package/src/__tests__/solve-issue-tea-worktree.test.ts +27 -31
  264. package/src/__tests__/step-ask.test.ts +677 -0
  265. package/src/__tests__/step-show-json.test.ts +1 -0
  266. package/src/__tests__/step-timing.test.ts +2 -0
  267. package/src/__tests__/store-global-cas.test.ts +2 -2
  268. package/src/__tests__/store-unified-threads.test.ts +30 -27
  269. package/src/__tests__/thread-cancel-status.test.ts +27 -20
  270. package/src/__tests__/thread-cancel-text-renderer.test.ts +125 -0
  271. package/src/__tests__/thread-list-filters.test.ts +443 -17
  272. package/src/__tests__/thread-list-template-ms-date.test.ts +110 -0
  273. package/src/__tests__/thread-list-workflow-corrupt.test.ts +198 -0
  274. package/src/__tests__/thread-poke.test.ts +554 -0
  275. package/src/__tests__/thread-read-xml-tags.test.ts +9 -11
  276. package/src/__tests__/thread-resume.test.ts +20 -15
  277. package/src/__tests__/thread-show-status.test.ts +17 -29
  278. package/src/__tests__/thread-start-cwd-cli.test.ts +15 -3
  279. package/src/__tests__/thread-stop-text-renderer.test.ts +168 -0
  280. package/src/__tests__/thread-suspend-step.test.ts +13 -16
  281. package/src/__tests__/thread-suspended-display.test.ts +10 -22
  282. package/src/__tests__/thread-test-helpers.ts +15 -1
  283. package/src/__tests__/thread.test.ts +14 -14
  284. package/src/__tests__/validate-semantic.test.ts +118 -33
  285. package/src/__tests__/workflow-list-recursive.test.ts +370 -0
  286. package/src/__tests__/workflow-resolution.test.ts +48 -29
  287. package/src/__tests__/workflow-show-resolution.test.ts +286 -0
  288. package/src/__tests__/workflow-validate.test.ts +828 -0
  289. package/src/__tests__/write-envelope.test.ts +257 -0
  290. package/src/background/background.ts +88 -6
  291. package/src/background/index.ts +2 -0
  292. package/src/background/types.ts +1 -0
  293. package/src/cli.ts +184 -77
  294. package/src/commands/config.ts +16 -33
  295. package/src/commands/prompt.ts +57 -31
  296. package/src/commands/setup.ts +80 -358
  297. package/src/commands/step.ts +339 -12
  298. package/src/commands/thread.ts +511 -171
  299. package/src/commands/workflow.ts +155 -4
  300. package/src/concurrency/concurrency.ts +245 -0
  301. package/src/concurrency/index.ts +10 -0
  302. package/src/concurrency/types.ts +19 -0
  303. package/src/format.ts +282 -2
  304. package/src/moderator/__tests__/evaluate.test.ts +34 -17
  305. package/src/moderator/evaluate.ts +5 -17
  306. package/src/moderator/index.ts +1 -6
  307. package/src/moderator/types.ts +6 -14
  308. package/src/output-mappers.ts +254 -0
  309. package/src/schemas.ts +51 -5
  310. package/src/store.ts +86 -20
  311. package/src/text-renderers.ts +355 -0
  312. package/src/validate-semantic.ts +125 -73
  313. package/src/validate.ts +27 -0
  314. package/dist/__tests__/setup-validate.test.d.ts +0 -2
  315. package/dist/__tests__/setup-validate.test.d.ts.map +0 -1
  316. package/dist/__tests__/setup-validate.test.js +0 -108
  317. package/dist/__tests__/setup-validate.test.js.map +0 -1
  318. package/src/__tests__/setup-validate.test.ts +0 -148
  319. /package/src/__tests__/fixtures/{e2e-mustache.mock.yaml → e2e-liquid.mock.yaml} +0 -0
@@ -1,90 +1,14 @@
1
1
  import { execFileSync } from "node:child_process";
2
2
  import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
3
- import { join } from "node:path";
3
+ import { dirname, join } from "node:path";
4
4
  import { stdin as input, stdout as output } from "node:process";
5
5
  import { createInterface } from "node:readline/promises";
6
- import type { Result } from "@united-workforce/util";
6
+ import { fileURLToPath } from "node:url";
7
7
  import { parse, stringify } from "yaml";
8
+ import { cmdWorkflowAdd } from "./workflow.js";
8
9
 
9
- /**
10
- * Send a minimal chat completion request to verify the model is reachable.
11
- * Returns ok on 2xx, error with reason string otherwise.
12
- */
13
- export async function validateModel(
14
- baseUrl: string,
15
- apiKey: string,
16
- model: string,
17
- ): Promise<Result<void, string>> {
18
- try {
19
- const url = `${baseUrl.replace(/\/+$/, "")}/chat/completions`;
20
- const res = await fetch(url, {
21
- method: "POST",
22
- headers: {
23
- Authorization: `Bearer ${apiKey}`,
24
- "Content-Type": "application/json",
25
- },
26
- body: JSON.stringify({
27
- model,
28
- messages: [{ role: "user", content: "hi" }],
29
- max_tokens: 1,
30
- }),
31
- signal: AbortSignal.timeout(15_000),
32
- });
33
- if (!res.ok) {
34
- return { ok: false, error: `HTTP ${res.status} ${res.statusText}` };
35
- }
36
- return { ok: true, value: undefined };
37
- } catch (err: unknown) {
38
- if (err instanceof DOMException && err.name === "AbortError") {
39
- return { ok: false, error: "Request timed out — model endpoint unreachable" };
40
- }
41
- return { ok: false, error: `Network error — could not reach endpoint (${String(err)})` };
42
- }
43
- }
44
-
45
- /**
46
- * Preset provider list — embedded to avoid runtime YAML loading dependency.
47
- * Keep in sync with providers.yaml in cli.
48
- */
49
- const PRESET_PROVIDERS = [
50
- // International
51
- { name: "openai", label: "OpenAI", baseUrl: "https://api.openai.com/v1" },
52
- { name: "xai", label: "xAI", baseUrl: "https://api.x.ai/v1" },
53
- { name: "openrouter", label: "OpenRouter", baseUrl: "https://openrouter.ai/api/v1" },
54
- { name: "venice", label: "Venice", baseUrl: "https://api.venice.ai/api/v1" },
55
- // China
56
- {
57
- name: "dashscope",
58
- label: "DashScope (Alibaba)",
59
- baseUrl: "https://dashscope.aliyuncs.com/compatible-mode/v1",
60
- },
61
- { name: "deepseek", label: "DeepSeek", baseUrl: "https://api.deepseek.com/v1" },
62
- { name: "siliconflow", label: "SiliconFlow", baseUrl: "https://api.siliconflow.cn/v1" },
63
- {
64
- name: "volcengine",
65
- label: "Volcengine (ByteDance)",
66
- baseUrl: "https://ark.cn-beijing.volces.com/api/v3",
67
- },
68
- { name: "kimi", label: "Kimi (Moonshot)", baseUrl: "https://api.moonshot.cn/v1" },
69
- { name: "glm", label: "GLM (Zhipu AI)", baseUrl: "https://open.bigmodel.cn/api/paas/v4" },
70
- { name: "stepfun", label: "StepFun", baseUrl: "https://api.stepfun.com/v1" },
71
- { name: "minimax", label: "MiniMax", baseUrl: "https://api.minimax.io/v1" },
72
- // Local
73
- { name: "ollama", label: "Ollama (local)", baseUrl: "http://localhost:11434/v1" },
74
- ] as const;
75
-
76
- /** Look up the base URL for a preset provider name. Returns null if not a preset. */
77
- export function resolvePresetBaseUrl(providerName: string): string | null {
78
- const preset = PRESET_PROVIDERS.find((p) => p.name === providerName);
79
- return preset !== undefined ? preset.baseUrl : null;
80
- }
81
-
82
- type SetupArgs = {
83
- provider: string;
84
- baseUrl: string;
85
- apiKey: string;
86
- model: string;
87
- agent?: string | undefined;
10
+ export type SetupArgs = {
11
+ agent: string;
88
12
  storageRoot: string;
89
13
  };
90
14
 
@@ -193,77 +117,17 @@ async function _tryWhichDiscovery(): Promise<string[] | null> {
193
117
  }
194
118
 
195
119
  // ──────────────────────────────────────────────────────────────────────────────
196
- // Extracted helpers onData closure (promptSecret)
120
+ // Terminator/backspace helpers (kept for reuse + test coverage)
197
121
  // ──────────────────────────────────────────────────────────────────────────────
198
122
 
199
123
  /** Returns true for newline, carriage return, or EOF (EOT). */
200
124
  export function _isTerminator(c: string): boolean {
201
- return c === "\n" || c === "\r" || c === "";
125
+ return c === "\n" || c === "\r" || c === "\u0004";
202
126
  }
203
127
 
204
128
  /** Returns true for DEL or backspace. */
205
129
  export function _isBackspace(c: string): boolean {
206
- return c === "" || c === "\b";
207
- }
208
-
209
- // ──────────────────────────────────────────────────────────────────────────────
210
- // Extracted helpers — cmdSetupInteractive
211
- // ──────────────────────────────────────────────────────────────────────────────
212
-
213
- type ProviderEntry = { name: string; label: string; baseUrl: string };
214
-
215
- /** Prints the numbered provider list and custom option to stdout. */
216
- export function _printProviderMenu(providers: readonly ProviderEntry[]): void {
217
- const numWidth = String(providers.length + 1).length;
218
- for (let i = 0; i < providers.length; i++) {
219
- const p = providers[i];
220
- if (!p) continue;
221
- const num = String(i + 1).padStart(numWidth);
222
- console.log(` ${num}) ${p.label.padEnd(28)} ${p.baseUrl}`);
223
- }
224
- const customNum = String(providers.length + 1).padStart(numWidth);
225
- console.log(` ${customNum}) Custom (enter name and URL manually)\n`);
226
- }
227
-
228
- /** Resolves a numeric choice string to a preset provider, or null for custom/invalid. */
229
- export function _resolveProviderChoice(
230
- choice: string,
231
- providers: readonly ProviderEntry[],
232
- ): { providerName: string; baseUrl: string } | null {
233
- const n = Number.parseInt(choice, 10);
234
- if (Number.isNaN(n) || n < 1 || n > providers.length) return null;
235
- const p = providers[n - 1];
236
- if (!p) return null;
237
- return { providerName: p.name, baseUrl: p.baseUrl };
238
- }
239
-
240
- /** Resolves numeric index or literal model name to a model string. */
241
- export function _resolveModelChoice(input: string, models: string[]): string {
242
- const n = Number.parseInt(input, 10);
243
- if (!Number.isNaN(n) && n >= 1 && n <= models.length) {
244
- return models[n - 1] ?? input;
245
- }
246
- return input;
247
- }
248
-
249
- /** Prints the multi-column model list to stdout. */
250
- export function _printModelMenu(models: string[], termCols: number): void {
251
- const nw = String(models.length).length;
252
- const maxLen = models.reduce((m, s) => Math.max(m, s.length), 0);
253
- const colWidth = nw + 2 + maxLen + 4;
254
- const cols = Math.max(1, Math.floor(termCols / colWidth));
255
- const rows = Math.ceil(models.length / cols);
256
- for (let r = 0; r < rows; r++) {
257
- let line = "";
258
- for (let c = 0; c < cols; c++) {
259
- const idx = c * rows + r;
260
- if (idx >= models.length) break;
261
- const num = String(idx + 1).padStart(nw);
262
- const name = (models[idx] ?? "").padEnd(maxLen);
263
- line += ` ${num}) ${name} `;
264
- }
265
- console.log(line.trimEnd());
266
- }
130
+ return c === "\u007f" || c === "\b";
267
131
  }
268
132
 
269
133
  // ──────────────────────────────────────────────────────────────────────────────
@@ -323,7 +187,6 @@ export async function _promptAgentSelection(
323
187
  console.log(` Found 1 agent: ${label} — auto-selected.\n`);
324
188
  return name;
325
189
  }
326
-
327
190
  console.log(` Found ${agents.length} agents:\n`);
328
191
  _printAgentMenu(agents);
329
192
  const choice = (await rl.question(`Choose default agent [1-${agents.length}]: `)).trim();
@@ -340,61 +203,33 @@ export async function _promptAgentSelection(
340
203
  return name;
341
204
  }
342
205
 
343
- type ValidationResult = { ok: boolean; error: string | null };
344
-
345
- /** Prints the model validation result to stdout. */
346
- export function _printValidationResult(validation: ValidationResult): void {
347
- if (validation.ok) {
348
- console.log("✓ Model verified — connection successful.\n");
349
- } else {
350
- console.log(`\n⚠ Warning: Could not reach model — ${validation.error}`);
351
- console.log(
352
- " Config saved, but you may want to try a different model or check your API key.\n",
353
- );
354
- }
355
- }
356
-
357
206
  // ──────────────────────────────────────────────────────────────────────────────
358
207
 
359
208
  /**
360
- * Merge setup args into config.yaml structure. Non-destructive — preserves existing entries.
209
+ * Merge setup args into config.yaml structure. Non-destructive — preserves
210
+ * existing entries (including agentOverrides). Engine config is LLM-free, so
211
+ * legacy provider/model fields are dropped on rewrite.
361
212
  */
362
213
  function mergeConfig(existing: Record<string, unknown>, args: SetupArgs): Record<string, unknown> {
363
- const providers = (
364
- typeof existing.providers === "object" && existing.providers !== null
365
- ? { ...(existing.providers as Record<string, unknown>) }
366
- : {}
367
- ) as Record<string, unknown>;
368
-
369
- providers[args.provider] = { baseUrl: args.baseUrl, apiKey: args.apiKey };
370
-
371
- const models = (
372
- typeof existing.models === "object" && existing.models !== null
373
- ? { ...(existing.models as Record<string, unknown>) }
374
- : {}
375
- ) as Record<string, unknown>;
376
- models.default = { provider: args.provider, name: args.model };
377
-
378
214
  const agents = (
379
215
  typeof existing.agents === "object" && existing.agents !== null
380
216
  ? { ...(existing.agents as Record<string, unknown>) }
381
217
  : {}
382
218
  ) as Record<string, unknown>;
383
219
 
384
- const agentName = _agentNameFromBinary(args.agent ?? "hermes");
385
- // Ensure the selected agent has an entry
220
+ const agentName = _agentNameFromBinary(args.agent);
386
221
  if (!agents[agentName]) {
387
222
  agents[agentName] = { command: `uwf-${agentName}`, args: [] };
388
223
  }
389
224
 
390
- return {
391
- ...existing,
392
- providers,
393
- models,
225
+ const merged: Record<string, unknown> = {
394
226
  agents,
395
227
  defaultAgent: agentName,
396
- defaultModel: existing.defaultModel ?? "default",
397
228
  };
229
+ if (existing.agentOverrides !== undefined) {
230
+ merged.agentOverrides = existing.agentOverrides;
231
+ }
232
+ return merged;
398
233
  }
399
234
 
400
235
  /**
@@ -428,8 +263,56 @@ export function _checkAdapterAvailability(agentName: string): string[] {
428
263
  return warnings;
429
264
  }
430
265
 
266
+ // ──────────────────────────────────────────────────────────────────────────────
267
+ // Bundled example workflows
268
+ // ──────────────────────────────────────────────────────────────────────────────
269
+
270
+ /** Resolve the examples/ directory bundled with the CLI package. */
271
+ function _findExamplesDir(): string | null {
272
+ // Walk up from this file (src/commands/ or dist/commands/) to the package root
273
+ let dir = dirname(fileURLToPath(import.meta.url));
274
+ for (let i = 0; i < 5; i++) {
275
+ const candidate = join(dir, "examples");
276
+ if (existsSync(candidate) && statSync(candidate).isDirectory()) {
277
+ return candidate;
278
+ }
279
+ dir = dirname(dir);
280
+ }
281
+ return null;
282
+ }
283
+
284
+ /**
285
+ * Register bundled example workflows. Non-destructive — silently skips
286
+ * any that fail (e.g. already registered with same hash).
287
+ * Returns list of successfully registered workflow names.
288
+ */
289
+ export async function _registerBundledExamples(storageRoot: string): Promise<string[]> {
290
+ const examplesDir = _findExamplesDir();
291
+ if (examplesDir === null) return [];
292
+
293
+ const registered: string[] = [];
294
+ const files = readdirSync(examplesDir)
295
+ .filter((f) => f.endsWith(".yaml"))
296
+ .sort();
297
+
298
+ for (const file of files) {
299
+ try {
300
+ const result = await cmdWorkflowAdd(storageRoot, join(examplesDir, file));
301
+ registered.push(result.name);
302
+ console.error(` ✓ ${result.name}`);
303
+ } catch {
304
+ // Skip silently — workflow may already exist or be invalid
305
+ }
306
+ }
307
+
308
+ return registered;
309
+ }
310
+
311
+ // ──────────────────────────────────────────────────────────────────────────────
312
+
431
313
  /**
432
- * Non-interactive setup. All required args provided via CLI flags.
314
+ * Non-interactive setup. Engine config is LLM-free only writes
315
+ * agents + defaultAgent. Each adapter owns its own LLM configuration.
433
316
  */
434
317
  export async function cmdSetup(args: SetupArgs): Promise<Record<string, unknown>> {
435
318
  const { storageRoot } = args;
@@ -445,203 +328,42 @@ export async function cmdSetup(args: SetupArgs): Promise<Record<string, unknown>
445
328
  // Print config path to stderr (stdout is reserved for JSON output)
446
329
  console.error(`Config saved to ${configPath} ✓`);
447
330
 
448
- // Validate model connectivity
449
- const validation = await validateModel(args.baseUrl, args.apiKey, args.model);
450
-
451
331
  // Check adapter availability
452
- const agentName = _agentNameFromBinary(args.agent ?? "hermes");
332
+ const agentName = _agentNameFromBinary(args.agent);
453
333
  const adapterWarnings = _checkAdapterAvailability(agentName);
454
334
  for (const w of adapterWarnings) {
455
335
  console.error(`⚠ ${w}`);
456
336
  }
457
337
 
338
+ // Auto-register bundled example workflows
339
+ const registeredExamples = await _registerBundledExamples(storageRoot);
340
+
458
341
  return {
459
342
  configPath,
460
- provider: args.provider,
461
- model: args.model,
462
343
  defaultAgent: merged.defaultAgent,
463
- validation,
464
344
  adapterWarnings,
345
+ registeredExamples,
465
346
  };
466
347
  }
467
348
 
468
- type SecretState = {
469
- buf: string;
470
- rawWasSet: boolean;
471
- resolve: (value: string) => void;
472
- onData: (chunk: string) => void;
473
- };
474
-
475
- function _handleSecretTerminator(state: SecretState): void {
476
- if (process.stdin.isTTY) process.stdin.setRawMode(state.rawWasSet);
477
- process.stdin.pause();
478
- process.stdin.removeListener("data", state.onData);
479
- process.stdout.write("\n");
480
- state.resolve(state.buf.trim());
481
- }
482
-
483
- function _handleSecretBackspace(state: SecretState): void {
484
- if (state.buf.length > 0) {
485
- state.buf = state.buf.slice(0, -1);
486
- process.stdout.write("\b \b");
487
- }
488
- }
489
-
490
- function _handleSecretChar(c: string, state: SecretState): boolean {
491
- if (_isTerminator(c)) {
492
- _handleSecretTerminator(state);
493
- return true;
494
- }
495
- if (_isBackspace(c)) {
496
- _handleSecretBackspace(state);
497
- return false;
498
- }
499
- if (c === "") {
500
- if (process.stdin.isTTY) process.stdin.setRawMode(state.rawWasSet);
501
- process.exit(130);
502
- }
503
- state.buf += c;
504
- process.stdout.write("*");
505
- return false;
506
- }
507
-
508
- /** Read a line with terminal echo disabled (for secrets). */
509
- async function promptSecret(label: string): Promise<string> {
510
- process.stdout.write(label);
511
- return new Promise((resolve) => {
512
- const rawWasSet = process.stdin.isRaw;
513
- if (process.stdin.isTTY) {
514
- process.stdin.setRawMode(true);
515
- }
516
- process.stdin.resume();
517
- process.stdin.setEncoding("utf8");
518
-
519
- const state: SecretState = { buf: "", rawWasSet, resolve, onData: () => {} };
520
- state.onData = (chunk: string) => {
521
- for (const c of chunk.toString()) {
522
- if (_handleSecretChar(c, state)) return;
523
- }
524
- };
525
- process.stdin.on("data", state.onData);
526
- });
527
- }
528
-
529
- /** Fetch available models from an OpenAI-compatible /models endpoint. */
530
- async function fetchModels(baseUrl: string, apiKey: string): Promise<string[]> {
531
- try {
532
- const url = `${baseUrl.replace(/\/+$/, "")}/models`;
533
- const res = await fetch(url, {
534
- headers: { Authorization: `Bearer ${apiKey}` },
535
- signal: AbortSignal.timeout(10_000),
536
- });
537
- if (!res.ok) return [];
538
- const body = (await res.json()) as { data?: { id: string }[] };
539
- if (!Array.isArray(body.data)) return [];
540
- const NON_CHAT =
541
- /speech|embed|image|video|audio|ocr|rerank|tts|asr|paraformer|sambert|cosyvoice|wordart|wanx|wan2|flux|stable-diffusion|gui-/i;
542
- return body.data
543
- .map((m) => m.id)
544
- .filter((id) => !NON_CHAT.test(id))
545
- .sort();
546
- } catch {
547
- return [];
548
- }
549
- }
550
-
551
- async function _promptProviderSelection(
552
- rl: ReturnType<typeof createInterface>,
553
- ): Promise<{ providerName: string; baseUrl: string }> {
554
- console.log("Select a provider:\n");
555
- _printProviderMenu(PRESET_PROVIDERS);
556
-
557
- const choice = (await rl.question(`Choose [1-${PRESET_PROVIDERS.length + 1}]: `)).trim();
558
- const choiceNum = Number.parseInt(choice, 10);
559
- if (Number.isNaN(choiceNum) || choiceNum < 1 || choiceNum > PRESET_PROVIDERS.length + 1) {
560
- throw new Error(`Invalid choice: ${choice}`);
561
- }
562
-
563
- const preset = _resolveProviderChoice(choice, PRESET_PROVIDERS);
564
- if (preset) {
565
- const selected = PRESET_PROVIDERS[choiceNum - 1];
566
- if (selected) {
567
- console.log(`\n → ${selected.label} (${selected.baseUrl})\n`);
568
- }
569
- return preset;
570
- }
571
-
572
- const providerName = (await rl.question("Provider name (e.g. my-proxy): ")).trim();
573
- if (!providerName) throw new Error("Provider name required");
574
- const baseUrl = (await rl.question("OpenAI-compatible API base URL: ")).trim();
575
- if (!baseUrl) throw new Error("Base URL required");
576
- return { providerName, baseUrl };
577
- }
578
-
579
- async function _promptModelSelection(
580
- rl: ReturnType<typeof createInterface>,
581
- baseUrl: string,
582
- apiKey: string,
583
- ): Promise<string> {
584
- console.log("\nFetching available models...");
585
- const models = await fetchModels(baseUrl, apiKey);
586
-
587
- if (models.length === 0) {
588
- console.log("Could not fetch models. Enter model name manually.");
589
- const model = (await rl.question("Default model (e.g. qwen-plus, gpt-4o): ")).trim();
590
- if (!model) throw new Error("Model required");
591
- return model;
592
- }
593
- console.log(`\nAvailable models (${models.length}):\n`);
594
- _printModelMenu(models, process.stdout.columns || 100);
595
- console.log(`\nChoose a number, or type a model name directly.`);
596
- const modelInput = (await rl.question(`Default model [1-${models.length}]: `)).trim();
597
- if (!modelInput) throw new Error("Model required");
598
- return _resolveModelChoice(modelInput, models);
599
- }
600
-
601
349
  /**
602
- * Interactive setup — prompts user for provider, API key, model.
350
+ * Interactive setup — prompts the user only for the default agent.
351
+ * Each adapter owns its own LLM configuration.
603
352
  */
604
353
  export async function cmdSetupInteractive(storageRoot: string): Promise<Record<string, unknown>> {
605
354
  const rl = createInterface({ input, output });
606
-
607
355
  try {
608
- console.log("Configure LLM provider for uwf workflow agents.\n");
356
+ console.log("Configure default agent for uwf workflow.\n");
609
357
 
610
- const { providerName, baseUrl } = await _promptProviderSelection(rl);
611
-
612
- // 2. API key
358
+ const agentName = await _promptAgentSelection(rl);
613
359
  rl.close();
614
- const apiKey = await promptSecret("API key: ");
615
- if (!apiKey) throw new Error("API key required");
616
-
617
- // 3. Model selection
618
- const rl2 = createInterface({ input, output });
619
- const model = await _promptModelSelection(rl2, baseUrl, apiKey);
620
- rl2.close();
621
- console.log(` → ${providerName}/${model}\n`);
622
-
623
- // 4. Agent discovery & selection
624
- const rl3 = createInterface({ input, output });
625
- const agentName = await _promptAgentSelection(rl3);
626
- rl3.close();
627
-
628
- const setupResult = await cmdSetup({
629
- provider: providerName,
630
- baseUrl,
631
- apiKey,
632
- model,
633
- agent: agentName,
634
- storageRoot,
635
- });
636
360
 
637
- // Show validation result
638
- if (setupResult.validation && typeof setupResult.validation === "object") {
639
- _printValidationResult(setupResult.validation as ValidationResult);
640
- }
361
+ await cmdSetup({ agent: agentName, storageRoot });
641
362
  console.log("Setup complete! Get started:\n");
642
- console.log(" uwf workflow put <workflow.yaml> Register a workflow");
643
- console.log(' uwf thread start <name> -p "..." Start a thread');
644
- console.log(" uwf thread step <thread-id> Execute next step");
363
+ console.log(" uwf workflow list List available workflows");
364
+ console.log(" uwf workflow add <workflow.yaml> Register a workflow");
365
+ console.log(' uwf thread start <name> -p "..." Start a thread');
366
+ console.log(" uwf thread exec <thread-id> Execute next step");
645
367
  console.log("");
646
368
 
647
369
  return null as unknown as Record<string, unknown>;