@pikku/core 0.12.84 → 0.12.86

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 (564) hide show
  1. package/CHANGELOG.md +424 -0
  2. package/dist/bootstrap-compat/root.d.ts +20 -0
  3. package/dist/classification/column-form.d.ts +32 -0
  4. package/dist/classification/column-form.js +42 -0
  5. package/dist/classification/data-classification.d.ts +64 -0
  6. package/dist/classification/index.d.ts +13 -0
  7. package/dist/classification/index.js +2 -0
  8. package/dist/classification/secret-value.d.ts +56 -0
  9. package/dist/classification/secret-value.js +46 -0
  10. package/dist/crypto-utils.d.ts +1 -1
  11. package/dist/ecosystem/agent-scorer.d.ts +1 -1
  12. package/dist/ecosystem/agent.d.ts +4 -1
  13. package/dist/ecosystem/channel/local.d.ts +2 -0
  14. package/dist/ecosystem/channel/local.js +2 -0
  15. package/dist/ecosystem/channel/serverless.d.ts +9 -0
  16. package/dist/ecosystem/channel/serverless.js +1 -0
  17. package/dist/ecosystem/channel.d.ts +3 -0
  18. package/dist/ecosystem/channel.js +1 -0
  19. package/dist/ecosystem/cli.d.ts +1 -1
  20. package/dist/ecosystem/cli.js +1 -1
  21. package/dist/ecosystem/credential.d.ts +1 -0
  22. package/dist/ecosystem/crypto-utils.d.ts +8 -0
  23. package/dist/ecosystem/crypto-utils.js +1 -0
  24. package/dist/ecosystem/dev.d.ts +1 -0
  25. package/dist/ecosystem/dev.js +1 -0
  26. package/{src/ecosystem/testing.ts → dist/ecosystem/errors.d.ts} +3 -3
  27. package/dist/ecosystem/errors.js +2 -0
  28. package/dist/ecosystem/function.d.ts +1 -1
  29. package/dist/ecosystem/function.js +1 -1
  30. package/dist/ecosystem/http.d.ts +3 -1
  31. package/dist/ecosystem/http.js +3 -1
  32. package/dist/ecosystem/mcp.d.ts +1 -1
  33. package/dist/ecosystem/mcp.js +1 -1
  34. package/dist/ecosystem/middleware.d.ts +2 -1
  35. package/dist/ecosystem/middleware.js +2 -1
  36. package/dist/ecosystem/persona.d.ts +4 -2
  37. package/dist/ecosystem/persona.js +2 -1
  38. package/dist/ecosystem/queue.d.ts +4 -2
  39. package/dist/ecosystem/queue.js +3 -1
  40. package/dist/ecosystem/role.d.ts +1 -0
  41. package/dist/ecosystem/rpc.d.ts +2 -0
  42. package/dist/ecosystem/rpc.js +1 -0
  43. package/dist/ecosystem/scenario.d.ts +8 -2
  44. package/dist/ecosystem/scenario.js +5 -1
  45. package/dist/ecosystem/scheduler.d.ts +2 -1
  46. package/dist/ecosystem/scheduler.js +2 -1
  47. package/dist/ecosystem/schema.d.ts +2 -1
  48. package/dist/ecosystem/schema.js +1 -1
  49. package/dist/ecosystem/scope.d.ts +2 -0
  50. package/dist/ecosystem/scope.js +1 -0
  51. package/dist/ecosystem/secret.d.ts +2 -0
  52. package/dist/ecosystem/secret.js +1 -0
  53. package/dist/ecosystem/services/local-content.d.ts +2 -1
  54. package/dist/ecosystem/services/local-content.js +1 -1
  55. package/dist/ecosystem/services/local-meta.d.ts +1 -0
  56. package/dist/ecosystem/services/local-meta.js +1 -0
  57. package/dist/ecosystem/services.d.ts +11 -3
  58. package/dist/ecosystem/services.js +7 -1
  59. package/dist/ecosystem/trigger.d.ts +1 -0
  60. package/dist/ecosystem/trigger.js +1 -1
  61. package/dist/ecosystem/types.d.ts +6 -3
  62. package/dist/ecosystem/types.js +3 -2
  63. package/dist/ecosystem/variable.d.ts +1 -0
  64. package/dist/ecosystem/virtual-user.d.ts +3 -0
  65. package/dist/ecosystem/virtual-user.js +3 -0
  66. package/dist/ecosystem/workflow.d.ts +2 -0
  67. package/dist/ecosystem/workflow.js +2 -0
  68. package/dist/errors/index.d.ts +8 -0
  69. package/dist/errors/index.js +1 -0
  70. package/dist/errors/serialized-error.d.ts +7 -0
  71. package/dist/function/function-meta.types.d.ts +139 -0
  72. package/dist/function/function-runner.d.ts +3 -2
  73. package/dist/function/function-runner.js +31 -11
  74. package/dist/function/functions.types.d.ts +10 -2
  75. package/dist/function/index.d.ts +8 -2
  76. package/dist/function/index.js +4 -1
  77. package/dist/function/pikku-request.d.ts +8 -0
  78. package/dist/function/pikku-request.js +15 -0
  79. package/dist/middleware/auth-apikey.d.ts +2 -10
  80. package/dist/middleware/auth-apikey.js +1 -1
  81. package/dist/middleware/auth-bearer.d.ts +2 -10
  82. package/dist/middleware/auth-bearer.js +1 -1
  83. package/dist/middleware/auth-cookie.d.ts +2 -10
  84. package/dist/middleware/auth-cookie.js +1 -1
  85. package/dist/middleware/cors.d.ts +5 -11
  86. package/dist/middleware/cors.js +13 -6
  87. package/dist/middleware/index.d.ts +3 -0
  88. package/dist/middleware/index.js +1 -0
  89. package/dist/middleware/middleware-factories.d.ts +9 -0
  90. package/dist/middleware/middleware-factories.js +19 -0
  91. package/dist/middleware/middleware.types.d.ts +27 -0
  92. package/dist/middleware/middleware.types.js +1 -0
  93. package/dist/middleware/remote-auth.d.ts +2 -2
  94. package/dist/middleware/remote-auth.js +1 -1
  95. package/dist/middleware/telemetry.d.ts +4 -20
  96. package/dist/middleware/telemetry.js +1 -1
  97. package/dist/middleware-runner.d.ts +2 -1
  98. package/dist/pikku-state.d.ts +2 -1
  99. package/dist/services/audit-service.d.ts +1 -1
  100. package/dist/services/credential-wire-service.js +1 -1
  101. package/dist/services/email-service.d.ts +1 -1
  102. package/dist/services/file-scenario-run-store.d.ts +58 -0
  103. package/dist/services/file-scenario-run-store.js +214 -0
  104. package/dist/services/in-memory-workflow-service.d.ts +1 -1
  105. package/dist/services/index.d.ts +5 -1
  106. package/dist/services/index.js +3 -0
  107. package/dist/services/local-secrets.d.ts +1 -1
  108. package/dist/services/local-secrets.js +1 -1
  109. package/dist/services/logger.d.ts +7 -7
  110. package/dist/services/meta-service.d.ts +2 -1
  111. package/dist/services/scoped-secret-service.d.ts +1 -1
  112. package/dist/services/secret-service.d.ts +1 -1
  113. package/dist/services/typed-secret-service.d.ts +16 -2
  114. package/dist/services/typed-secret-service.js +5 -0
  115. package/dist/services/webhook-service.d.ts +1 -1
  116. package/dist/services/workflow-service.d.ts +2 -1
  117. package/dist/testing/index.d.ts +1 -0
  118. package/dist/testing/index.js +1 -0
  119. package/dist/types/core.types.d.ts +30 -180
  120. package/dist/types/core.types.js +1 -19
  121. package/dist/types/index.d.ts +2 -0
  122. package/dist/types/index.js +1 -0
  123. package/dist/types/state.types.d.ts +3 -1
  124. package/dist/utils.d.ts +13 -0
  125. package/dist/utils.js +2 -0
  126. package/dist/wirings/addon/addon-runner.d.ts +17 -0
  127. package/dist/wirings/addon/addon-runner.js +150 -0
  128. package/dist/wirings/addon/index.d.ts +6 -0
  129. package/dist/wirings/addon/index.js +3 -0
  130. package/dist/wirings/addon/remote-addon-auth.d.ts +16 -0
  131. package/dist/wirings/addon/remote-addon-auth.js +39 -0
  132. package/dist/wirings/addon/wire-addon.d.ts +80 -0
  133. package/dist/wirings/addon/wire-addon.js +120 -0
  134. package/dist/wirings/addon/wire-remote-addon.d.ts +22 -0
  135. package/dist/wirings/addon/wire-remote-addon.js +11 -0
  136. package/dist/wirings/agent/agent-prepare.d.ts +3 -3
  137. package/dist/wirings/agent/agent-prepare.js +1 -1
  138. package/dist/wirings/agent/agent-runner.js +2 -3
  139. package/dist/wirings/agent/agent-stream.js +2 -2
  140. package/dist/wirings/agent/agent-utils.d.ts +13 -0
  141. package/dist/wirings/agent/agent-utils.js +16 -0
  142. package/dist/wirings/agent/agent.types.d.ts +3 -2
  143. package/dist/wirings/agent/index.d.ts +1 -1
  144. package/dist/wirings/agent/voice-input.js +1 -1
  145. package/dist/wirings/agent/voice-output.js +1 -1
  146. package/dist/wirings/agent-scorer/agent-scorer-judge.d.ts +13 -0
  147. package/dist/wirings/agent-scorer/agent-scorer-judge.js +52 -0
  148. package/dist/wirings/agent-scorer/agent-scorer.d.ts +7 -1
  149. package/dist/wirings/agent-scorer/agent-scorer.js +1 -0
  150. package/dist/wirings/agent-scorer/agent-scorer.types.d.ts +20 -0
  151. package/dist/wirings/agent-scorer/index.d.ts +1 -1
  152. package/dist/wirings/channel/channel-common.d.ts +2 -1
  153. package/dist/wirings/channel/channel-middleware-runner.d.ts +2 -1
  154. package/dist/wirings/channel/channel.types.d.ts +1 -1
  155. package/dist/wirings/channel/serverless/index.d.ts +1 -0
  156. package/dist/wirings/cli/channel/index.d.ts +1 -1
  157. package/dist/wirings/cli/cli-runner.d.ts +2 -1
  158. package/dist/wirings/cli/cli-runner.js +4 -1
  159. package/dist/wirings/cli/cli.types.d.ts +21 -2
  160. package/dist/wirings/cli/command-parser.js +157 -23
  161. package/dist/wirings/gateway/gateway.types.d.ts +2 -1
  162. package/dist/wirings/http/http-runner.d.ts +1 -1
  163. package/dist/wirings/http/http-runner.js +1 -1
  164. package/dist/wirings/http/http.types.d.ts +2 -2
  165. package/dist/wirings/http/index.d.ts +1 -0
  166. package/dist/wirings/http/index.js +1 -0
  167. package/dist/wirings/http/pikku-fetch-http-request.js +1 -1
  168. package/dist/wirings/http/pikku-fetch-http-response.js +2 -2
  169. package/dist/wirings/mcp/mcp.types.d.ts +1 -1
  170. package/dist/wirings/persona/index.d.ts +1 -1
  171. package/dist/wirings/persona/index.js +1 -1
  172. package/dist/wirings/persona/persona.types.d.ts +14 -0
  173. package/dist/wirings/queue/queue.types.d.ts +1 -1
  174. package/dist/wirings/queue/signed-queue-service.d.ts +1 -1
  175. package/dist/wirings/rpc/index.d.ts +0 -6
  176. package/dist/wirings/rpc/index.js +0 -3
  177. package/dist/wirings/rpc/remote-addon-auth.d.ts +1 -1
  178. package/dist/wirings/rpc/remote-addon-auth.js +1 -1
  179. package/dist/wirings/rpc/rpc-runner.js +2 -2
  180. package/dist/wirings/rpc/wire-addon.d.ts +1 -1
  181. package/dist/wirings/scheduler/scheduler.types.d.ts +2 -1
  182. package/dist/wirings/scope/index.d.ts +1 -0
  183. package/dist/wirings/scope/index.js +1 -0
  184. package/dist/wirings/secret/secret.types.d.ts +10 -0
  185. package/dist/wirings/secret/validate-secret-definitions.js +2 -0
  186. package/dist/wirings/variable/validate-variable-definitions.js +2 -0
  187. package/dist/wirings/variable/variable.types.d.ts +9 -0
  188. package/dist/wirings/virtual-user/index.d.ts +1 -1
  189. package/dist/wirings/virtual-user/index.js +1 -1
  190. package/dist/wirings/virtual-user/prepare-virtual-user-run.d.ts +1 -1
  191. package/dist/wirings/virtual-user/virtual-user-derive.d.ts +1 -1
  192. package/dist/wirings/workflow/graph/graph-node.d.ts +9 -2
  193. package/dist/wirings/workflow/graph/graph-node.js +2 -0
  194. package/dist/wirings/workflow/graph/graph-runner.d.ts +1 -0
  195. package/dist/wirings/workflow/graph/graph-runner.js +283 -31
  196. package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +16 -1
  197. package/dist/wirings/workflow/index.d.ts +2 -0
  198. package/dist/wirings/workflow/index.js +1 -0
  199. package/dist/wirings/workflow/pikku-scenario-service.d.ts +14 -4
  200. package/dist/wirings/workflow/pikku-scenario-service.js +44 -16
  201. package/dist/wirings/workflow/pikku-workflow-service.d.ts +2 -1
  202. package/dist/wirings/workflow/pikku-workflow-service.js +3 -4
  203. package/dist/wirings/workflow/run-timeline.d.ts +1 -1
  204. package/dist/wirings/workflow/scenario-run.types.d.ts +171 -0
  205. package/dist/wirings/workflow/scenario-run.types.js +1 -0
  206. package/dist/wirings/workflow/scenario-step-guards.d.ts +1 -2
  207. package/dist/wirings/workflow/scenario-step-guards.js +0 -8
  208. package/dist/wirings/workflow/scenario-step.types.d.ts +27 -12
  209. package/dist/wirings/workflow/workflow-approval.js +2 -1
  210. package/dist/wirings/workflow/workflow-run-engine.types.d.ts +2 -1
  211. package/dist/wirings/workflow/workflow.types.d.ts +2 -1
  212. package/knowledge/decisions/index.md +2 -0
  213. package/knowledge/decisions/internals/a-scenario-step-template-is-offered-unfilled.md +1 -1
  214. package/knowledge/decisions/internals/a-secret-that-fails-to-decrypt-fails-the-whole-read.md +1 -1
  215. package/knowledge/decisions/internals/a-virtual-user-run-is-not-a-workflow-and-not-a-queued-job.md +2 -2
  216. package/knowledge/decisions/internals/a-wall-clock-threshold-is-a-load-test-in-disguise.md +6 -6
  217. package/knowledge/decisions/internals/a-workflow-wire-is-built-from-the-run-not-from-the-rpc-service.md +1 -1
  218. package/knowledge/decisions/internals/addon-singleton-services-are-cached-per-namespace-not-per-package.md +1 -1
  219. package/knowledge/decisions/internals/addon-workflow-names-are-prefixed-with-the-consumer-namespace.md +1 -1
  220. package/knowledge/decisions/internals/an-addon-scope-root-loses-to-a-root-the-host-already-declares.md +1 -1
  221. package/knowledge/decisions/internals/channel-message-handlers-accept-three-config-shapes.md +1 -1
  222. package/knowledge/decisions/internals/channel-middleware-caches-only-statically-resolved-middleware.md +1 -1
  223. package/knowledge/decisions/internals/channel-user-id-is-persisted-after-onconnect-middleware-runs.md +1 -1
  224. package/knowledge/decisions/internals/cli-option-names-are-camelcase-in-state-and-kebab-on-the-command-line.md +1 -1
  225. package/knowledge/decisions/internals/cli-parse-errors-are-routed-by-message-prefix.md +1 -1
  226. package/knowledge/decisions/internals/cli-unknown-long-options-warn-instead-of-failing.md +1 -1
  227. package/knowledge/decisions/internals/core-column-form-is-an-axis-of-its-own.md +4 -4
  228. package/knowledge/decisions/internals/core-data-classification-brand-is-an-optional-property.md +1 -1
  229. package/knowledge/decisions/internals/core-function-runner-restores-the-wire-fields-it-overwrites.md +3 -3
  230. package/knowledge/decisions/internals/core-hot-reload-merges-generated-meta-never-replaces-it.md +2 -2
  231. package/knowledge/decisions/internals/core-hot-reload-owns-its-module-registry.md +2 -2
  232. package/knowledge/decisions/internals/core-middleware-order-is-scope-then-priority.md +1 -1
  233. package/knowledge/decisions/internals/core-schema-defaults-apply-on-every-transport.md +1 -1
  234. package/knowledge/decisions/internals/core-scopes-are-an-and-gate-separate-from-permissions.md +3 -3
  235. package/knowledge/decisions/internals/gateway-adapters-resolve-lazily-and-are-promise-cached.md +1 -1
  236. package/knowledge/decisions/internals/gateway-listener-middleware-runs-without-an-rpc-on-the-wire.md +4 -4
  237. package/knowledge/decisions/internals/gateway-wiring-is-a-meta-wiring-over-http-and-channels.md +1 -1
  238. package/knowledge/decisions/internals/http-router-matches-normalized-paths-but-returns-registered-ones.md +1 -1
  239. package/knowledge/decisions/internals/in-a-scenario-a-4xx-is-data-not-an-exception.md +1 -1
  240. package/knowledge/decisions/internals/in-memory-workflow-history-aliases-the-live-step-object.md +1 -1
  241. package/knowledge/decisions/internals/index.md +4 -2
  242. package/knowledge/decisions/internals/istanbul-statement-counts-attach-to-the-start-line-only.md +1 -1
  243. package/knowledge/decisions/internals/one-project-shape-check-two-validators.md +1 -1
  244. package/knowledge/decisions/internals/queue-jobs-always-carry-an-explicit-attempts-count.md +1 -1
  245. package/knowledge/decisions/internals/remote-addons-dispatch-over-http-instead-of-local-meta.md +1 -1
  246. package/knowledge/decisions/internals/rpc-names-resolve-through-package-scope-before-root.md +1 -1
  247. package/knowledge/decisions/internals/scenario-given-and-when-are-sugar-but-then-is-not.md +2 -2
  248. package/knowledge/decisions/internals/scenarios-live-in-files-named-for-them.md +5 -5
  249. package/knowledge/decisions/internals/the-actor-prompt-says-json-because-of-json-object-mode.md +1 -1
  250. package/knowledge/decisions/internals/the-api-report-pins-members-not-just-names.md +1 -1
  251. package/knowledge/decisions/internals/the-ecosystem-entry-point-carries-the-adapter-surface.md +43 -19
  252. package/knowledge/decisions/internals/the-persona-runtime-is-exported-from-the-persona-entry-point.md +1 -1
  253. package/knowledge/decisions/internals/the-worker-disposition-is-the-one-that-is-not-testing.md +1 -1
  254. package/knowledge/decisions/internals/validate-runs-checks-by-precondition.md +3 -3
  255. package/knowledge/decisions/internals/virtual-user-step-order-comes-from-insertion-order.md +1 -1
  256. package/knowledge/decisions/internals/webhook-delivery-history-records-every-attempt-best-effort.md +1 -1
  257. package/knowledge/decisions/internals/wiring-registries-erase-the-generics-their-wire-functions-capture.md +1 -1
  258. package/knowledge/decisions/internals/workflow-approval-expiry-is-decided-from-a-recorded-deadline.md +1 -1
  259. package/knowledge/decisions/internals/workflow-core-never-imports-a-browser-driver.md +1 -1
  260. package/knowledge/decisions/internals/workflow-inline-runs-report-their-run-id-before-they-can-fail.md +1 -1
  261. package/knowledge/decisions/internals/workflow-invocation-id-is-the-dedupe-key-not-step-id.md +1 -1
  262. package/knowledge/decisions/internals/workflow-run-capabilities-are-extensions-not-subclasses.md +1 -1
  263. package/knowledge/decisions/internals/workflow-run-timeline-is-a-pure-fold-over-durable-history.md +1 -1
  264. package/knowledge/decisions/internals/workflow-scenario-assertions-never-retry-and-record-one-step.md +9 -5
  265. package/knowledge/decisions/internals/workflow-scenario-hooks-are-a-scenario-only-affordance.md +1 -1
  266. package/knowledge/decisions/internals/workflow-step-rpc-name-is-provenance-only.md +2 -2
  267. package/knowledge/decisions/internals/workflow-suspend-and-approval-reasons-are-durable-step-identities.md +1 -1
  268. package/knowledge/decisions/security/a-function-never-receives-the-secret-service.md +1 -1
  269. package/knowledge/decisions/security/a-step-runs-the-function-the-workflow-dispatched-it-with.md +1 -1
  270. package/knowledge/decisions/security/a-virtual-user-is-never-offered-a-step-that-would-forge-its-own-oracle.md +2 -3
  271. package/knowledge/decisions/security/actor-sign-in-is-proven-by-set-cookie-not-a-non-empty-jar.md +1 -1
  272. package/knowledge/decisions/security/addon-auth-and-tags-only-tighten.md +1 -1
  273. package/knowledge/decisions/security/agent-gate-requires-a-session-only-when-auth-is-true.md +1 -1
  274. package/knowledge/decisions/security/an-approval-answer-outlives-the-run-it-answered.md +3 -3
  275. package/knowledge/decisions/security/console-addon-privileged-functions-gate-themselves.md +4 -3
  276. package/knowledge/decisions/security/core-safe-fetch-blocks-ssrf-by-host-literal-not-dns.md +1 -1
  277. package/knowledge/decisions/security/gateway-middleware-sessions-must-be-bridged-onto-the-wire.md +2 -2
  278. package/knowledge/decisions/security/global-permissions-and-function-permissions-are-independent-gates.md +1 -1
  279. package/knowledge/decisions/security/http-request-bodies-are-bounded-before-they-are-buffered.md +2 -2
  280. package/knowledge/decisions/security/index.md +2 -0
  281. package/knowledge/decisions/security/mcp-internal-error-details-are-double-gated-on-production.md +1 -1
  282. package/knowledge/decisions/security/queue-jobs-carry-the-producers-pikku-user-id.md +3 -3
  283. package/knowledge/decisions/security/remote-addon-tokens-are-client-credentials-not-mesh-trust.md +1 -1
  284. package/knowledge/decisions/security/scaffold-features-are-authenticated-unless-opted-out.md +2 -2
  285. package/knowledge/decisions/security/self-authentication-is-declared-not-detected.md +1 -1
  286. package/knowledge/decisions/security/signed-content-urls-bind-the-request-path.md +2 -2
  287. package/knowledge/index.md +3 -1
  288. package/knowledge/questions/channel-middleware-accepts-bare-factories-that-nothing-resolves.md +1 -1
  289. package/knowledge/questions/index.md +2 -0
  290. package/package.json +14 -13
  291. package/run-tests.sh +0 -0
  292. package/scripts/generate-api-report.d.mts +1 -1
  293. package/scripts/generate-api-report.mjs +67 -60
  294. package/scripts/generate-api-report.mts +11 -45
  295. package/src/api-report.test.ts +70 -1
  296. package/src/app-alias-surface.test.ts +179 -0
  297. package/src/app-leaf-surface.test.ts +217 -0
  298. package/src/bootstrap-compat/root.ts +20 -0
  299. package/src/{column-form.test.ts → classification/column-form.test.ts} +2 -2
  300. package/src/{data-classification.ts → classification/data-classification.ts} +1 -5
  301. package/src/classification/index.ts +38 -0
  302. package/src/{secret-value.test.ts → classification/secret-value.test.ts} +2 -2
  303. package/src/{secret-value.ts → classification/secret-value.ts} +1 -6
  304. package/src/crypto-utils.ts +1 -1
  305. package/src/dev/reload-meta.test.ts +1 -1
  306. package/src/errors/index.ts +9 -0
  307. package/src/errors/serialized-error.ts +9 -0
  308. package/src/factory-functions.test.ts +4 -2
  309. package/src/function/function-meta.types.ts +151 -0
  310. package/src/function/function-runner.test.ts +4 -2
  311. package/src/function/function-runner.ts +47 -14
  312. package/src/function/functions.types.ts +11 -7
  313. package/src/function/index.ts +24 -5
  314. package/src/function/list.types.ts +1 -3
  315. package/src/middleware/auth-apikey.ts +4 -2
  316. package/src/middleware/auth-bearer.test.ts +1 -1
  317. package/src/middleware/auth-bearer.ts +4 -2
  318. package/src/middleware/auth-cookie.ts +4 -1
  319. package/src/middleware/cors.test.ts +126 -5
  320. package/src/middleware/cors.ts +19 -8
  321. package/src/middleware/index.ts +16 -0
  322. package/src/middleware/middleware-factories.ts +62 -0
  323. package/src/middleware/middleware.types.ts +61 -0
  324. package/src/middleware/remote-auth.test.ts +1 -1
  325. package/src/middleware/remote-auth.ts +1 -1
  326. package/src/middleware/telemetry.ts +4 -2
  327. package/src/middleware-runner.test.ts +2 -3
  328. package/src/middleware-runner.ts +2 -2
  329. package/src/no-root-barrel.test.ts +114 -0
  330. package/src/permissions.ts +2 -4
  331. package/src/pikku-state.ts +3 -1
  332. package/src/public-surface.json +56 -241
  333. package/src/public-surface.json.README +3 -3
  334. package/src/public-surface.test.ts +5 -5
  335. package/src/remote.test.ts +1 -1
  336. package/src/removed-legacy-exports.test.ts +5 -1
  337. package/src/services/audit-service.ts +1 -1
  338. package/src/services/credential-wire-service.ts +1 -1
  339. package/src/services/email-service.ts +2 -4
  340. package/src/services/file-scenario-run-store.test.ts +349 -0
  341. package/src/services/file-scenario-run-store.ts +282 -0
  342. package/src/services/in-memory-workflow-service.ts +1 -1
  343. package/src/services/index.ts +13 -1
  344. package/src/services/local-content-request-handler.ts +1 -2
  345. package/src/services/local-secrets.ts +1 -1
  346. package/src/services/logger.ts +7 -7
  347. package/src/services/meta-service.ts +2 -2
  348. package/src/services/queue-webhook-service.test.ts +1 -1
  349. package/src/services/scoped-secret-service.ts +1 -1
  350. package/src/services/secret-service.ts +1 -1
  351. package/src/services/typed-secret-service.test.ts +52 -0
  352. package/src/services/typed-secret-service.ts +26 -2
  353. package/src/services/webhook-service.ts +1 -1
  354. package/src/services/workflow-service.ts +2 -1
  355. package/src/template-alias-surface.test.ts +240 -0
  356. package/src/testing/index.ts +1 -0
  357. package/src/types/core.types.ts +33 -285
  358. package/src/types/index.ts +2 -0
  359. package/src/types/state.types.ts +6 -5
  360. package/src/types/typed-credentials.test.ts +72 -0
  361. package/src/utils.ts +34 -0
  362. package/src/wirings/{rpc → addon}/addon-scopes.test.ts +1 -1
  363. package/src/wirings/addon/index.ts +12 -0
  364. package/src/wirings/{rpc → addon}/remote-addon-auth.ts +4 -1
  365. package/src/wirings/{rpc → addon}/wire-addon.ts +37 -2
  366. package/src/wirings/agent/agent-prepare.ts +8 -5
  367. package/src/wirings/agent/agent-runner.ts +2 -3
  368. package/src/wirings/agent/agent-stream.ts +2 -3
  369. package/src/wirings/agent/agent-utils.ts +19 -0
  370. package/src/wirings/agent/agent.types.ts +3 -3
  371. package/src/wirings/agent/index.ts +1 -0
  372. package/src/wirings/agent/voice-input.ts +1 -1
  373. package/src/wirings/agent/voice-output.test.ts +2 -4
  374. package/src/wirings/agent/voice-output.ts +1 -1
  375. package/src/wirings/agent-scorer/agent-scorer-judge.test.ts +145 -0
  376. package/src/wirings/agent-scorer/agent-scorer-judge.ts +60 -0
  377. package/src/wirings/agent-scorer/agent-scorer.ts +8 -0
  378. package/src/wirings/agent-scorer/agent-scorer.types.ts +21 -0
  379. package/src/wirings/agent-scorer/index.ts +1 -0
  380. package/src/wirings/channel/channel-common.ts +4 -2
  381. package/src/wirings/channel/channel-handler.ts +2 -5
  382. package/src/wirings/channel/channel-middleware-runner.ts +1 -1
  383. package/src/wirings/channel/channel-rpc.types.ts +1 -5
  384. package/src/wirings/channel/channel.types.ts +1 -2
  385. package/src/wirings/channel/local/local-channel-runner.test.ts +1 -2
  386. package/src/wirings/channel/serverless/index.ts +1 -0
  387. package/src/wirings/cli/channel/cli-raw-channel-runner.ts +2 -2
  388. package/src/wirings/cli/channel/index.ts +1 -3
  389. package/src/wirings/cli/cli-runner.test.ts +1 -1
  390. package/src/wirings/cli/cli-runner.ts +11 -7
  391. package/src/wirings/cli/cli.types.ts +24 -3
  392. package/src/wirings/cli/command-parser.test.ts +388 -1
  393. package/src/wirings/cli/command-parser.ts +189 -29
  394. package/src/wirings/gateway/gateway-runner.test.ts +1 -2
  395. package/src/wirings/gateway/gateway-runner.ts +3 -6
  396. package/src/wirings/gateway/gateway.types.ts +5 -4
  397. package/src/wirings/http/http-routes.test.ts +1 -2
  398. package/src/wirings/http/http-runner-addon-ref.test.ts +238 -0
  399. package/src/wirings/http/http-runner.test.ts +3 -8
  400. package/src/wirings/http/http-runner.ts +9 -3
  401. package/src/wirings/http/http.types.ts +3 -12
  402. package/src/wirings/http/index.ts +1 -0
  403. package/src/wirings/http/pikku-fetch-http-request.ts +1 -1
  404. package/src/wirings/http/pikku-fetch-http-response.ts +3 -2
  405. package/src/wirings/mcp/mcp-runner.test.ts +1 -1
  406. package/src/wirings/mcp/mcp.types.ts +1 -2
  407. package/src/wirings/persona/index.ts +1 -4
  408. package/src/wirings/persona/persona.types.ts +14 -0
  409. package/src/wirings/queue/queue-identity.test.ts +1 -1
  410. package/src/wirings/queue/queue.types.ts +2 -6
  411. package/src/wirings/queue/signed-queue-service.ts +1 -1
  412. package/src/wirings/rpc/index.ts +0 -12
  413. package/src/wirings/rpc/rpc-runner.test.ts +4 -4
  414. package/src/wirings/rpc/rpc-runner.ts +3 -3
  415. package/src/wirings/scheduler/scheduler.types.ts +2 -5
  416. package/src/wirings/scope/index.ts +1 -0
  417. package/src/wirings/secret/secret.types.ts +10 -0
  418. package/src/wirings/secret/validate-secret-definitions.test.ts +23 -0
  419. package/src/wirings/secret/validate-secret-definitions.ts +2 -0
  420. package/src/wirings/variable/validate-variable-definitions.test.ts +23 -0
  421. package/src/wirings/variable/validate-variable-definitions.ts +2 -0
  422. package/src/wirings/variable/variable.types.ts +9 -0
  423. package/src/wirings/virtual-user/index.ts +1 -4
  424. package/src/wirings/virtual-user/prepare-virtual-user-run.ts +1 -1
  425. package/src/wirings/virtual-user/virtual-user-derive.test.ts +1 -1
  426. package/src/wirings/virtual-user/virtual-user-derive.ts +1 -1
  427. package/src/wirings/virtual-user/virtual-user.types.ts +1 -5
  428. package/src/wirings/workflow/graph/graph-foreach-queued.test.ts +569 -0
  429. package/src/wirings/workflow/graph/graph-foreach.test.ts +626 -0
  430. package/src/wirings/workflow/graph/graph-node.ts +16 -1
  431. package/src/wirings/workflow/graph/graph-runner.ts +387 -61
  432. package/src/wirings/workflow/graph/workflow-graph.types.ts +28 -3
  433. package/src/wirings/workflow/index.ts +8 -0
  434. package/src/wirings/workflow/pikku-scenario-service.ts +59 -19
  435. package/src/wirings/workflow/pikku-workflow-service.ts +6 -8
  436. package/src/wirings/workflow/run-timeline.ts +1 -1
  437. package/src/wirings/workflow/scenario-expectations.test.ts +28 -13
  438. package/src/wirings/workflow/scenario-run.types.ts +186 -0
  439. package/src/wirings/workflow/scenario-step-guards.ts +1 -14
  440. package/src/wirings/workflow/scenario-step.test.ts +32 -7
  441. package/src/wirings/workflow/scenario-step.types.ts +27 -12
  442. package/src/wirings/workflow/workflow-approval-policy.test.ts +117 -47
  443. package/src/wirings/workflow/workflow-approval.ts +2 -1
  444. package/src/wirings/workflow/workflow-run-engine.types.ts +2 -1
  445. package/src/wirings/workflow/workflow.types.ts +4 -12
  446. package/src/wirings-stay-decoupled.test.ts +6 -3
  447. package/tsconfig.tsbuildinfo +1 -1
  448. package/tsconfig.type-tests.json +3 -2
  449. package/dist/ecosystem/ai-agent.d.ts +0 -18
  450. package/dist/ecosystem/ai-agent.js +0 -6
  451. package/dist/ecosystem/ai-scorer.d.ts +0 -12
  452. package/dist/ecosystem/ai-scorer.js +0 -5
  453. package/dist/services/ai-agent-runner-service.d.ts +0 -220
  454. package/dist/services/ai-run-state-service.d.ts +0 -29
  455. package/dist/services/ai-storage-service.d.ts +0 -18
  456. package/dist/services/in-memory-ai-run-state-service.d.ts +0 -19
  457. package/dist/services/in-memory-ai-run-state-service.js +0 -54
  458. package/dist/testing/service-tests/ai-storage-service-tests.d.ts +0 -3
  459. package/dist/testing/service-tests/ai-storage-service-tests.js +0 -302
  460. package/dist/wirings/ai-agent/agent-rpc.d.ts +0 -15
  461. package/dist/wirings/ai-agent/agent-rpc.js +0 -53
  462. package/dist/wirings/ai-agent/ai-agent-agui.d.ts +0 -79
  463. package/dist/wirings/ai-agent/ai-agent-agui.js +0 -327
  464. package/dist/wirings/ai-agent/ai-agent-finalize.d.ts +0 -58
  465. package/dist/wirings/ai-agent/ai-agent-finalize.js +0 -138
  466. package/dist/wirings/ai-agent/ai-agent-helpers.d.ts +0 -35
  467. package/dist/wirings/ai-agent/ai-agent-helpers.js +0 -47
  468. package/dist/wirings/ai-agent/ai-agent-interrupt.d.ts +0 -153
  469. package/dist/wirings/ai-agent/ai-agent-interrupt.js +0 -257
  470. package/dist/wirings/ai-agent/ai-agent-memory.d.ts +0 -42
  471. package/dist/wirings/ai-agent/ai-agent-memory.js +0 -333
  472. package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +0 -16
  473. package/dist/wirings/ai-agent/ai-agent-model-config.js +0 -51
  474. package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +0 -117
  475. package/dist/wirings/ai-agent/ai-agent-prepare.js +0 -728
  476. package/dist/wirings/ai-agent/ai-agent-registry.d.ts +0 -16
  477. package/dist/wirings/ai-agent/ai-agent-registry.js +0 -51
  478. package/dist/wirings/ai-agent/ai-agent-runner.d.ts +0 -7
  479. package/dist/wirings/ai-agent/ai-agent-runner.js +0 -580
  480. package/dist/wirings/ai-agent/ai-agent-stream.d.ts +0 -41
  481. package/dist/wirings/ai-agent/ai-agent-stream.js +0 -1029
  482. package/dist/wirings/ai-agent/ai-agent-turn.d.ts +0 -57
  483. package/dist/wirings/ai-agent/ai-agent-turn.js +0 -82
  484. package/dist/wirings/ai-agent/ai-agent-utils.d.ts +0 -1
  485. package/dist/wirings/ai-agent/ai-agent-utils.js +0 -1
  486. package/dist/wirings/ai-agent/ai-agent.types.d.ts +0 -625
  487. package/dist/wirings/ai-agent/index.d.ts +0 -12
  488. package/dist/wirings/ai-agent/index.js +0 -10
  489. package/dist/wirings/ai-agent/voice-input.d.ts +0 -69
  490. package/dist/wirings/ai-agent/voice-input.js +0 -150
  491. package/dist/wirings/ai-agent/voice-output.d.ts +0 -85
  492. package/dist/wirings/ai-agent/voice-output.js +0 -196
  493. package/dist/wirings/ai-scorer/ai-scorer-grade.d.ts +0 -26
  494. package/dist/wirings/ai-scorer/ai-scorer-grade.js +0 -33
  495. package/dist/wirings/ai-scorer/ai-scorer-judge.d.ts +0 -17
  496. package/dist/wirings/ai-scorer/ai-scorer-judge.js +0 -92
  497. package/dist/wirings/ai-scorer/ai-scorer-live.d.ts +0 -15
  498. package/dist/wirings/ai-scorer/ai-scorer-live.js +0 -38
  499. package/dist/wirings/ai-scorer/ai-scorer-registry.d.ts +0 -18
  500. package/dist/wirings/ai-scorer/ai-scorer-registry.js +0 -46
  501. package/dist/wirings/ai-scorer/ai-scorer-sampling.d.ts +0 -8
  502. package/dist/wirings/ai-scorer/ai-scorer-sampling.js +0 -31
  503. package/dist/wirings/ai-scorer/ai-scorer-snapshots.d.ts +0 -10
  504. package/dist/wirings/ai-scorer/ai-scorer-snapshots.js +0 -40
  505. package/dist/wirings/ai-scorer/ai-scorer-worker.d.ts +0 -15
  506. package/dist/wirings/ai-scorer/ai-scorer-worker.js +0 -58
  507. package/dist/wirings/ai-scorer/ai-scorer.d.ts +0 -39
  508. package/dist/wirings/ai-scorer/ai-scorer.js +0 -40
  509. package/dist/wirings/ai-scorer/ai-scorer.types.d.ts +0 -90
  510. package/dist/wirings/ai-scorer/ai-scorer.types.js +0 -4
  511. package/dist/wirings/ai-scorer/index.d.ts +0 -6
  512. package/dist/wirings/ai-scorer/index.js +0 -5
  513. package/src/ecosystem/agent-scorer.ts +0 -24
  514. package/src/ecosystem/agent.ts +0 -42
  515. package/src/ecosystem/channel/local.ts +0 -1
  516. package/src/ecosystem/channel.ts +0 -22
  517. package/src/ecosystem/cli/channel.ts +0 -25
  518. package/src/ecosystem/cli.ts +0 -21
  519. package/src/ecosystem/credential.ts +0 -10
  520. package/src/ecosystem/dev.ts +0 -12
  521. package/src/ecosystem/function.ts +0 -28
  522. package/src/ecosystem/gateway.ts +0 -4
  523. package/src/ecosystem/hmac.ts +0 -1
  524. package/src/ecosystem/http.ts +0 -22
  525. package/src/ecosystem/mcp.ts +0 -17
  526. package/src/ecosystem/middleware.ts +0 -27
  527. package/src/ecosystem/node-host-resolver.ts +0 -1
  528. package/src/ecosystem/node.ts +0 -5
  529. package/src/ecosystem/oauth2.ts +0 -4
  530. package/src/ecosystem/persona.ts +0 -30
  531. package/src/ecosystem/queue.ts +0 -23
  532. package/src/ecosystem/remote.ts +0 -9
  533. package/src/ecosystem/role.ts +0 -13
  534. package/src/ecosystem/rpc.ts +0 -7
  535. package/src/ecosystem/safe-fetch.ts +0 -8
  536. package/src/ecosystem/scenario.ts +0 -32
  537. package/src/ecosystem/scheduler.ts +0 -5
  538. package/src/ecosystem/schema.ts +0 -1
  539. package/src/ecosystem/scope.ts +0 -14
  540. package/src/ecosystem/secret.ts +0 -15
  541. package/src/ecosystem/services/istanbul-coverage.ts +0 -1
  542. package/src/ecosystem/services/local-content-request-handler.ts +0 -16
  543. package/src/ecosystem/services/local-content.ts +0 -1
  544. package/src/ecosystem/services/v8-coverage.ts +0 -1
  545. package/src/ecosystem/services.ts +0 -82
  546. package/src/ecosystem/trigger.ts +0 -7
  547. package/src/ecosystem/types.ts +0 -34
  548. package/src/ecosystem/variable.ts +0 -14
  549. package/src/ecosystem/virtual-user.ts +0 -48
  550. package/src/ecosystem/workflow.ts +0 -68
  551. package/src/ecosystem.ts +0 -40
  552. package/src/index.ts +0 -244
  553. /package/dist/{services/ai-agent-runner-service.js → bootstrap-compat/root.js} +0 -0
  554. /package/dist/{services/ai-run-state-service.js → classification/data-classification.js} +0 -0
  555. /package/dist/{services/ai-storage-service.js → errors/serialized-error.js} +0 -0
  556. /package/dist/{wirings/ai-agent/ai-agent.types.js → function/function-meta.types.js} +0 -0
  557. /package/src/{column-form.ts → classification/column-form.ts} +0 -0
  558. /package/src/{pikku-request.test.ts → function/pikku-request.test.ts} +0 -0
  559. /package/src/{pikku-request.ts → function/pikku-request.ts} +0 -0
  560. /package/src/wirings/{rpc → addon}/addon-auth-tags.test.ts +0 -0
  561. /package/src/wirings/{rpc → addon}/addon-runner.ts +0 -0
  562. /package/src/wirings/{rpc → addon}/addon-secrets.test.ts +0 -0
  563. /package/src/wirings/{rpc → addon}/wire-addon.test.ts +0 -0
  564. /package/src/wirings/{rpc → addon}/wire-remote-addon.ts +0 -0
@@ -1,7 +1,8 @@
1
- import { test, describe } from 'node:test'
1
+ import { test, describe, beforeEach, after } from 'node:test'
2
2
  import * as assert from 'assert'
3
3
  import { parseCLIArguments, generateCommandHelp } from './command-parser.js'
4
4
  import type { CLIMeta } from './cli.types.js'
5
+ import { pikkuState, resetPikkuState } from '../../pikku-state.js'
5
6
 
6
7
  const testMeta: CLIMeta = {
7
8
  programs: {
@@ -88,6 +89,30 @@ const testMeta: CLIMeta = {
88
89
  },
89
90
  },
90
91
  },
92
+ deploy: {
93
+ pikkuFuncId: 'deployFunc',
94
+ positionals: [],
95
+ options: {
96
+ token: {
97
+ description: 'Auth token',
98
+ short: 'k',
99
+ type: 'string',
100
+ },
101
+ tags: {
102
+ description: 'Tags to include',
103
+ short: 't',
104
+ type: 'string[]',
105
+ },
106
+ note: {
107
+ description: 'Free-form note',
108
+ },
109
+ dryRun: {
110
+ description: 'Report without deploying',
111
+ type: 'boolean',
112
+ short: 'D',
113
+ },
114
+ },
115
+ },
91
116
  },
92
117
  },
93
118
  },
@@ -730,4 +755,366 @@ describe('Command Parser', () => {
730
755
  })
731
756
  })
732
757
  })
758
+
759
+ describe('the option spec drives value consumption', () => {
760
+ test('a string option takes the next token verbatim, even leading with a dash', () => {
761
+ const result = parseCLIArguments(
762
+ ['deploy', '--token', '-PumP_kL0'],
763
+ 'test-cli',
764
+ testMeta
765
+ )
766
+
767
+ assert.deepStrictEqual(result.errors, [])
768
+ assert.strictEqual(result.options.token, '-PumP_kL0')
769
+ })
770
+
771
+ test('an untyped option with no boolean/number default is still a string', () => {
772
+ const result = parseCLIArguments(
773
+ ['deploy', '--note', '-dash-leading'],
774
+ 'test-cli',
775
+ testMeta
776
+ )
777
+
778
+ assert.deepStrictEqual(result.errors, [])
779
+ assert.strictEqual(result.options.note, '-dash-leading')
780
+ })
781
+
782
+ test('a comma list into an array option splits, trims and drops blanks', () => {
783
+ const result = parseCLIArguments(
784
+ ['deploy', '--tags', 'alpha, beta ,,gamma'],
785
+ 'test-cli',
786
+ testMeta
787
+ )
788
+
789
+ assert.deepStrictEqual(result.errors, [])
790
+ assert.deepStrictEqual(result.options.tags, ['alpha', 'beta', 'gamma'])
791
+ })
792
+
793
+ test('an array option splits the --opt=value form too', () => {
794
+ const result = parseCLIArguments(
795
+ ['deploy', '--tags=alpha,beta'],
796
+ 'test-cli',
797
+ testMeta
798
+ )
799
+
800
+ assert.deepStrictEqual(result.options.tags, ['alpha', 'beta'])
801
+ })
802
+
803
+ test('an array option consumes exactly one token', () => {
804
+ const result = parseCLIArguments(
805
+ ['deploy', '--tags', 'alpha,beta', '--note', 'hello'],
806
+ 'test-cli',
807
+ testMeta
808
+ )
809
+
810
+ assert.deepStrictEqual(result.errors, [])
811
+ assert.deepStrictEqual(result.options.tags, ['alpha', 'beta'])
812
+ assert.strictEqual(result.options.note, 'hello')
813
+ })
814
+
815
+ /**
816
+ * `--tags alpha beta` cannot be told apart from an option followed by a
817
+ * positional, so consuming tokens until the next flag would make an array
818
+ * option swallow arguments meant for the command. The two unambiguous
819
+ * forms — one comma list, or the flag repeated — cover the same ground,
820
+ * and the leftover token is reported rather than silently dropped.
821
+ */
822
+ test('an array option leaves a following bare token to the positionals', () => {
823
+ const result = parseCLIArguments(
824
+ ['deploy', '--tags', 'alpha', 'beta'],
825
+ 'test-cli',
826
+ testMeta
827
+ )
828
+
829
+ assert.deepStrictEqual(result.options.tags, ['alpha'])
830
+ assert.ok(
831
+ result.errors.some((error) => error.includes('Unexpected arguments')),
832
+ `expected the stray token to be reported, got: ${JSON.stringify(result.errors)}`
833
+ )
834
+ })
835
+
836
+ test('a repeated array option accumulates', () => {
837
+ const result = parseCLIArguments(
838
+ ['deploy', '--tags', 'alpha', '--tags', 'beta,gamma'],
839
+ 'test-cli',
840
+ testMeta
841
+ )
842
+
843
+ assert.deepStrictEqual(result.options.tags, ['alpha', 'beta', 'gamma'])
844
+ })
845
+
846
+ test('an array option takes a dash-leading value verbatim', () => {
847
+ const result = parseCLIArguments(
848
+ ['deploy', '--tags', '-alpha,beta'],
849
+ 'test-cli',
850
+ testMeta
851
+ )
852
+
853
+ assert.deepStrictEqual(result.errors, [])
854
+ assert.deepStrictEqual(result.options.tags, ['-alpha', 'beta'])
855
+ })
856
+
857
+ test('a comma in a non-array option stays one literal string', () => {
858
+ const result = parseCLIArguments(
859
+ ['deploy', '--note', 'alpha,beta'],
860
+ 'test-cli',
861
+ testMeta
862
+ )
863
+
864
+ assert.strictEqual(result.options.note, 'alpha,beta')
865
+ })
866
+
867
+ test('a boolean option does not consume a following non-literal token', () => {
868
+ const result = parseCLIArguments(
869
+ ['deploy', '--dry-run', '--note', 'hello'],
870
+ 'test-cli',
871
+ testMeta
872
+ )
873
+
874
+ assert.deepStrictEqual(result.errors, [])
875
+ assert.strictEqual(result.options.dryRun, true)
876
+ assert.strictEqual(result.options.note, 'hello')
877
+ })
878
+
879
+ test('boolean options take an explicit literal instead of leaking positionals', () => {
880
+ const devMeta: CLIMeta = {
881
+ programs: {
882
+ 'dev-cli': {
883
+ program: 'dev-cli',
884
+ options: {},
885
+ commands: {
886
+ dev: {
887
+ pikkuFuncId: 'devFunc',
888
+ positionals: [],
889
+ options: {
890
+ port: {
891
+ description: 'Port for the dev server',
892
+ default: '3000',
893
+ short: 'p',
894
+ },
895
+ watch: {
896
+ description: 'Watch for file changes and regenerate',
897
+ default: true,
898
+ },
899
+ hmr: {
900
+ description: 'Enable hot module reload',
901
+ default: true,
902
+ },
903
+ },
904
+ },
905
+ },
906
+ },
907
+ },
908
+ renderers: {},
909
+ }
910
+
911
+ const result = parseCLIArguments(
912
+ ['dev', '--port', '4077', '--watch', 'false', '--hmr', 'false'],
913
+ 'dev-cli',
914
+ devMeta
915
+ )
916
+
917
+ assert.deepStrictEqual(result.errors, [])
918
+ assert.deepStrictEqual(result.positionals, {})
919
+ assert.strictEqual(result.options.port, '4077')
920
+ assert.strictEqual(result.options.watch, false)
921
+ assert.strictEqual(result.options.hmr, false)
922
+ })
923
+
924
+ test('a typed boolean option with no default takes an explicit literal', () => {
925
+ const result = parseCLIArguments(
926
+ ['deploy', '--dry-run', 'false', '--note', 'hello'],
927
+ 'test-cli',
928
+ testMeta
929
+ )
930
+
931
+ assert.deepStrictEqual(result.errors, [])
932
+ assert.strictEqual(result.options.dryRun, false)
933
+ assert.strictEqual(result.options.note, 'hello')
934
+ })
935
+
936
+ test('a boolean option takes an explicit literal in the --opt=value form', () => {
937
+ const result = parseCLIArguments(
938
+ ['deploy', '--dry-run=false'],
939
+ 'test-cli',
940
+ testMeta
941
+ )
942
+
943
+ assert.deepStrictEqual(result.errors, [])
944
+ assert.strictEqual(result.options.dryRun, false)
945
+ })
946
+
947
+ test('a boolean short flag takes an explicit literal', () => {
948
+ const result = parseCLIArguments(
949
+ ['deploy', '-D', 'false'],
950
+ 'test-cli',
951
+ testMeta
952
+ )
953
+
954
+ assert.deepStrictEqual(result.errors, [])
955
+ assert.strictEqual(result.options.dryRun, false)
956
+ })
957
+
958
+ test('a short flag follows its option spec for dash-leading values', () => {
959
+ const result = parseCLIArguments(
960
+ ['deploy', '-k', '-PumP_kL0'],
961
+ 'test-cli',
962
+ testMeta
963
+ )
964
+
965
+ assert.deepStrictEqual(result.errors, [])
966
+ assert.strictEqual(result.options.token, '-PumP_kL0')
967
+ })
968
+
969
+ test('a short flag for an array option splits on commas', () => {
970
+ const result = parseCLIArguments(
971
+ ['deploy', '-t', 'alpha,beta'],
972
+ 'test-cli',
973
+ testMeta
974
+ )
975
+
976
+ assert.deepStrictEqual(result.options.tags, ['alpha', 'beta'])
977
+ })
978
+
979
+ test('a boolean short flag in a cluster does not swallow the next token', () => {
980
+ const result = parseCLIArguments(
981
+ ['deploy', '-D', 'positional-would-error'],
982
+ 'test-cli',
983
+ testMeta
984
+ )
985
+
986
+ assert.strictEqual(result.options.dryRun, true)
987
+ assert.ok(
988
+ result.errors.some((e) => e.includes('Unexpected arguments')),
989
+ `expected the token to stay a positional, got: ${result.errors.join(', ')}`
990
+ )
991
+ })
992
+ })
993
+ })
994
+
995
+ describe('option types come from the function input schema', () => {
996
+ const schemaMeta: CLIMeta = {
997
+ programs: {
998
+ 'test-cli': {
999
+ program: 'test-cli',
1000
+ options: {},
1001
+ commands: {
1002
+ serve: {
1003
+ pikkuFuncId: 'serveFunc',
1004
+ positionals: [],
1005
+ options: {
1006
+ console: { description: 'Serve the console' },
1007
+ port: { description: 'Port to listen on' },
1008
+ tags: { description: 'Tags to include' },
1009
+ retries: { description: 'How many times to retry' },
1010
+ declared: {
1011
+ description: 'Declared against the schema',
1012
+ type: 'string',
1013
+ },
1014
+ },
1015
+ },
1016
+ },
1017
+ },
1018
+ },
1019
+ renderers: {},
1020
+ }
1021
+
1022
+ const registerSchema = (properties: Record<string, unknown>) => {
1023
+ pikkuState(null, 'function', 'meta')['serveFunc'] = {
1024
+ pikkuFuncId: 'serveFunc',
1025
+ inputSchemaName: 'ServeInput',
1026
+ outputSchemaName: null,
1027
+ sessionless: true,
1028
+ } as any
1029
+ pikkuState(null, 'misc', 'schemas').set('ServeInput', {
1030
+ type: 'object',
1031
+ properties,
1032
+ })
1033
+ }
1034
+
1035
+ beforeEach(() => {
1036
+ resetPikkuState()
1037
+ registerSchema({
1038
+ console: { type: 'boolean' },
1039
+ port: { type: 'string' },
1040
+ tags: { type: 'array', items: { type: 'string' } },
1041
+ retries: { type: 'number' },
1042
+ declared: { type: 'boolean' },
1043
+ })
1044
+ })
1045
+
1046
+ after(() => {
1047
+ resetPikkuState()
1048
+ })
1049
+
1050
+ test('a boolean in the schema is a flag, and does not swallow the next option', () => {
1051
+ const result = parseCLIArguments(
1052
+ ['serve', '--console', '--port', '4077'],
1053
+ 'test-cli',
1054
+ schemaMeta
1055
+ )
1056
+
1057
+ assert.deepStrictEqual(result.errors, [])
1058
+ assert.strictEqual(result.options.console, true)
1059
+ assert.strictEqual(result.options.port, '4077')
1060
+ })
1061
+
1062
+ test('an array in the schema splits on commas without being declared', () => {
1063
+ const result = parseCLIArguments(
1064
+ ['serve', '--tags', 'alpha,beta'],
1065
+ 'test-cli',
1066
+ schemaMeta
1067
+ )
1068
+
1069
+ assert.deepStrictEqual(result.options.tags, ['alpha', 'beta'])
1070
+ })
1071
+
1072
+ test('a number in the schema is coerced', () => {
1073
+ const result = parseCLIArguments(
1074
+ ['serve', '--retries', '3'],
1075
+ 'test-cli',
1076
+ schemaMeta
1077
+ )
1078
+
1079
+ assert.strictEqual(result.options.retries, 3)
1080
+ })
1081
+
1082
+ test('a declared type wins over the schema', () => {
1083
+ const result = parseCLIArguments(
1084
+ ['serve', '--declared', 'true'],
1085
+ 'test-cli',
1086
+ schemaMeta
1087
+ )
1088
+
1089
+ assert.strictEqual(result.options.declared, 'true')
1090
+ })
1091
+
1092
+ test('an option the schema does not mention keeps the string default', () => {
1093
+ registerSchema({ console: { type: 'boolean' } })
1094
+
1095
+ const result = parseCLIArguments(
1096
+ ['serve', '--port', '4077'],
1097
+ 'test-cli',
1098
+ schemaMeta
1099
+ )
1100
+
1101
+ assert.strictEqual(result.options.port, '4077')
1102
+ })
1103
+
1104
+ test('a command with no input schema parses as it did before', () => {
1105
+ pikkuState(null, 'function', 'meta')['serveFunc'] = {
1106
+ pikkuFuncId: 'serveFunc',
1107
+ inputSchemaName: null,
1108
+ outputSchemaName: null,
1109
+ sessionless: true,
1110
+ } as any
1111
+
1112
+ const result = parseCLIArguments(
1113
+ ['serve', '--tags', 'alpha,beta'],
1114
+ 'test-cli',
1115
+ schemaMeta
1116
+ )
1117
+
1118
+ assert.strictEqual(result.options.tags, 'alpha,beta')
1119
+ })
733
1120
  })
@@ -4,7 +4,9 @@ import type {
4
4
  CLICommandMeta,
5
5
  CLIPositional,
6
6
  CLIOption,
7
+ CLIOptionType,
7
8
  } from './cli.types.js'
9
+ import { pikkuState } from '../../pikku-state.js'
8
10
 
9
11
  /** "from-plan" → "fromPlan"; the parser accepts either spelling. */
10
12
  function toCamelCase(str: string): string {
@@ -95,6 +97,136 @@ export interface ParsedCommand {
95
97
  */
96
98
  const MAX_SHORT_FLAG_CLUSTER = 64
97
99
 
100
+ function resolveOptionType(optionDef: CLIOption): CLIOptionType {
101
+ if (optionDef.type) {
102
+ return optionDef.type
103
+ }
104
+ if (typeof optionDef.default === 'boolean') {
105
+ return 'boolean'
106
+ }
107
+ if (typeof optionDef.default === 'number') {
108
+ return 'number'
109
+ }
110
+ return 'string'
111
+ }
112
+
113
+ /**
114
+ * A command's options are keys of its function's input, so the input schema
115
+ * already records what each one is. Reading it here means a declaration never
116
+ * has to repeat — or contradict — the type the function is validated against.
117
+ * An explicit `type` still wins, and an option the schema says nothing about
118
+ * (a program-level option belonging to no function input) is left untouched.
119
+ */
120
+ function applySchemaOptionTypes(
121
+ options: Record<string, CLIOption>,
122
+ commandMeta: CLICommandMeta
123
+ ): Record<string, CLIOption> {
124
+ const properties = commandInputProperties(commandMeta)
125
+ if (!properties) {
126
+ return options
127
+ }
128
+
129
+ const typed: Record<string, CLIOption> = { ...options }
130
+ for (const [name, option] of Object.entries(typed)) {
131
+ if (option.type) {
132
+ continue
133
+ }
134
+ const schemaType = optionTypeFromSchema(properties[name])
135
+ if (schemaType) {
136
+ typed[name] = { ...option, type: schemaType }
137
+ }
138
+ }
139
+ return typed
140
+ }
141
+
142
+ function commandInputProperties(
143
+ commandMeta: CLICommandMeta
144
+ ): Record<string, any> | null {
145
+ const funcMeta = pikkuState(null, 'function', 'meta')[commandMeta.pikkuFuncId]
146
+ const schemaName = funcMeta?.inputSchemaName
147
+ if (!schemaName) {
148
+ return null
149
+ }
150
+ const schema = pikkuState(
151
+ funcMeta.packageName ?? null,
152
+ 'misc',
153
+ 'schemas'
154
+ ).get(schemaName)
155
+ const properties = schema?.properties
156
+ return properties && typeof properties === 'object' ? properties : null
157
+ }
158
+
159
+ function optionTypeFromSchema(property: any): CLIOptionType | undefined {
160
+ if (!property || typeof property !== 'object') {
161
+ return undefined
162
+ }
163
+ // An optional field can arrive as a union with null, which is not a shape the
164
+ // parser can read a value into.
165
+ const declared = Array.isArray(property.type)
166
+ ? property.type.filter((entry: unknown) => entry !== 'null')[0]
167
+ : property.type
168
+
169
+ switch (declared) {
170
+ case 'array':
171
+ return 'string[]'
172
+ case 'number':
173
+ case 'integer':
174
+ return 'number'
175
+ case 'boolean':
176
+ return 'boolean'
177
+ case 'string':
178
+ return 'string'
179
+ default:
180
+ return undefined
181
+ }
182
+ }
183
+
184
+ const BOOLEAN_LITERALS = new Map<string, boolean>([
185
+ ['true', true],
186
+ ['false', false],
187
+ ['1', true],
188
+ ['0', false],
189
+ ['yes', true],
190
+ ['no', false],
191
+ ])
192
+
193
+ /**
194
+ * A boolean option is a flag, but `--watch false` is how scripts and CI turn a
195
+ * default-on flag off, so an explicit literal directly after the flag is taken
196
+ * as its value rather than left behind as a positional.
197
+ */
198
+ function readBooleanLiteral(raw: string | undefined): boolean | undefined {
199
+ if (raw === undefined) {
200
+ return undefined
201
+ }
202
+ return BOOLEAN_LITERALS.get(raw.toLowerCase())
203
+ }
204
+
205
+ function readOptionValue(raw: string, optionDef: CLIOption): any {
206
+ if (resolveOptionType(optionDef) !== 'string[]') {
207
+ return parseOptionValue(raw, optionDef)
208
+ }
209
+ return raw
210
+ .split(',')
211
+ .map((item) => item.trim())
212
+ .filter((item) => item.length > 0)
213
+ .map((item) => parseOptionValue(item, optionDef))
214
+ }
215
+
216
+ /** A repeated array option accumulates rather than replacing. */
217
+ function assignOptionValue(
218
+ optionArgs: Record<string, any>,
219
+ key: string,
220
+ value: any
221
+ ) {
222
+ const existing = optionArgs[key]
223
+ if (Array.isArray(existing) && Array.isArray(value)) {
224
+ optionArgs[key] = [...existing, ...value]
225
+ } else {
226
+ optionArgs[key] = value
227
+ }
228
+ }
229
+
98
230
  export function parseCLIArguments(
99
231
  args: string[],
100
232
  programName: string,
@@ -186,7 +318,10 @@ export function parseCLIArguments(
186
318
  return result
187
319
  }
188
320
 
189
- const availableOptions = collectAvailableOptions(meta, result.commandPath)
321
+ const availableOptions = applySchemaOptionTypes(
322
+ collectAvailableOptions(meta, result.commandPath),
323
+ commandMeta
324
+ )
190
325
 
191
326
  const positionalArgs: string[] = []
192
327
  const optionArgs: Record<string, any> = {}
@@ -201,9 +336,10 @@ export function parseCLIArguments(
201
336
  const equalIndex = arg.indexOf('=')
202
337
  if (
203
338
  negatedKey &&
204
- typeof availableOptions[negatedKey]?.default === 'boolean'
339
+ availableOptions[negatedKey] &&
340
+ resolveOptionType(availableOptions[negatedKey]) === 'boolean'
205
341
  ) {
206
- // Requiring a boolean default keeps a literal `--no-something` option
342
+ // Requiring a boolean type keeps a literal `--no-something` option
207
343
  // name parsing as itself rather than as a negation.
208
344
  optionArgs[negatedKey] = false
209
345
  } else if (equalIndex > 0) {
@@ -215,7 +351,11 @@ export function parseCLIArguments(
215
351
  }
216
352
 
217
353
  const value = arg.slice(equalIndex + 1)
218
- optionArgs[key] = parseOptionValue(value, optionDef)
354
+ if (optionDef) {
355
+ assignOptionValue(optionArgs, key, readOptionValue(value, optionDef))
356
+ } else {
357
+ optionArgs[key] = parseOptionValue(value, optionDef)
358
+ }
219
359
  } else {
220
360
  const key = toCamelCase(arg.slice(2))
221
361
  const optionDef = availableOptions[key]
@@ -224,24 +364,33 @@ export function parseCLIArguments(
224
364
  warnUnknownOption(arg.slice(2), availableOptions, result)
225
365
  }
226
366
 
227
- if (optionDef && optionDef.array) {
228
- currentIndex++
229
- const values: any[] = []
230
- while (
231
- currentIndex < args.length &&
232
- !args[currentIndex].startsWith('-')
367
+ if (!optionDef) {
368
+ // With no spec there is nothing to consult, so the lookahead
369
+ // heuristic is all that is left.
370
+ if (
371
+ currentIndex + 1 < args.length &&
372
+ !args[currentIndex + 1].startsWith('-')
233
373
  ) {
234
- values.push(parseOptionValue(args[currentIndex], optionDef))
235
374
  currentIndex++
375
+ optionArgs[key] = parseOptionValue(args[currentIndex], optionDef)
376
+ } else {
377
+ optionArgs[key] = true
236
378
  }
237
- currentIndex-- // Back up one since we'll increment at loop end
238
- optionArgs[key] = values
239
- } else if (
240
- currentIndex + 1 < args.length &&
241
- !args[currentIndex + 1].startsWith('-')
242
- ) {
379
+ } else if (resolveOptionType(optionDef) === 'boolean') {
380
+ const literal = readBooleanLiteral(args[currentIndex + 1])
381
+ if (literal === undefined) {
382
+ optionArgs[key] = true
383
+ } else {
384
+ currentIndex++
385
+ optionArgs[key] = literal
386
+ }
387
+ } else if (currentIndex + 1 < args.length) {
243
388
  currentIndex++
244
- optionArgs[key] = parseOptionValue(args[currentIndex], optionDef)
389
+ assignOptionValue(
390
+ optionArgs,
391
+ key,
392
+ readOptionValue(args[currentIndex], optionDef)
393
+ )
245
394
  } else {
246
395
  optionArgs[key] = true
247
396
  }
@@ -259,16 +408,27 @@ export function parseCLIArguments(
259
408
 
260
409
  const longOption = findLongOption(shortFlag, availableOptions)
261
410
  if (longOption) {
411
+ const optionDef = availableOptions[longOption]
262
412
  // Only the last flag in a cluster can take a value.
263
- if (
264
- i === arg.length - 1 &&
265
- currentIndex + 1 < args.length &&
266
- !args[currentIndex + 1].startsWith('-')
413
+ const isLast = i === arg.length - 1
414
+ const literal =
415
+ isLast && resolveOptionType(optionDef) === 'boolean'
416
+ ? readBooleanLiteral(args[currentIndex + 1])
417
+ : undefined
418
+
419
+ if (literal !== undefined) {
420
+ currentIndex++
421
+ optionArgs[longOption] = literal
422
+ } else if (
423
+ isLast &&
424
+ resolveOptionType(optionDef) !== 'boolean' &&
425
+ currentIndex + 1 < args.length
267
426
  ) {
268
427
  currentIndex++
269
- optionArgs[longOption] = parseOptionValue(
270
- args[currentIndex],
271
- availableOptions[longOption]
428
+ assignOptionValue(
429
+ optionArgs,
430
+ longOption,
431
+ readOptionValue(args[currentIndex], optionDef)
272
432
  )
273
433
  } else {
274
434
  optionArgs[longOption] = true
@@ -362,11 +522,11 @@ function parseOptionValue(value: string, optionDef?: CLIOption): any {
362
522
  return value
363
523
  }
364
524
 
365
- const defaultValue = optionDef.default
366
- if (typeof defaultValue === 'boolean') {
367
- return value === 'true' || value === '1' || value === 'yes'
525
+ const optionType = resolveOptionType(optionDef)
526
+ if (optionType === 'boolean') {
527
+ return readBooleanLiteral(value) ?? false
368
528
  }
369
- if (typeof defaultValue === 'number') {
529
+ if (optionType === 'number') {
370
530
  return parseFloat(value)
371
531
  }
372
532
 
@@ -9,8 +9,7 @@ import type {
9
9
  GatewayInboundMessage,
10
10
  GatewayOutboundMessage,
11
11
  } from './gateway.types.js'
12
- import type { CorePikkuMiddleware } from '../../types/core.types.js'
13
-
12
+ import type { CorePikkuMiddleware } from '../../middleware/middleware.types.js'
14
13
  const createMockAdapter = (
15
14
  opts: {
16
15
  name?: string