@pikku/cli 0.12.22 → 0.12.24

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 (79) hide show
  1. package/console-app/assets/{index-CAk106ji.js → index-BDOqBctb.js} +2 -2
  2. package/console-app/index.html +1 -1
  3. package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
  4. package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
  5. package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
  6. package/dist/.pikku/cli/pikku-cli-channel.js +1 -1
  7. package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
  8. package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
  9. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
  10. package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
  11. package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
  12. package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
  13. package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
  14. package/dist/.pikku/console/pikku-node-types.gen.d.ts +1 -1
  15. package/dist/.pikku/function/pikku-function-types.gen.d.ts +1 -1
  16. package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
  17. package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
  18. package/dist/.pikku/function/pikku-functions-meta.gen.json +161 -269
  19. package/dist/.pikku/function/pikku-functions.gen.js +1 -1
  20. package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
  21. package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
  22. package/dist/.pikku/http/pikku-http-wirings-meta.gen.js +1 -1
  23. package/dist/.pikku/http/pikku-http-wirings-meta.gen.json +1 -62
  24. package/dist/.pikku/http/pikku-http-wirings.gen.d.ts +1 -2
  25. package/dist/.pikku/http/pikku-http-wirings.gen.js +1 -2
  26. package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
  27. package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
  28. package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
  29. package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
  30. package/dist/.pikku/pikku-meta-service.gen.d.ts +1 -1
  31. package/dist/.pikku/pikku-meta-service.gen.js +1 -1
  32. package/dist/.pikku/pikku-services.gen.d.ts +1 -1
  33. package/dist/.pikku/pikku-types.gen.d.ts +1 -1
  34. package/dist/.pikku/pikku-types.gen.js +1 -1
  35. package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
  36. package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
  37. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
  38. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
  39. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
  40. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
  41. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +14 -20
  42. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
  43. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
  44. package/dist/.pikku/schemas/register.gen.js +5 -23
  45. package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
  46. package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
  47. package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
  48. package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
  49. package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
  50. package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
  51. package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
  52. package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
  53. package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
  54. package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
  55. package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
  56. package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
  57. package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
  58. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
  59. package/dist/bin/pikku-bin.mjs +2 -2
  60. package/dist/src/functions/commands/dev.js +17 -2
  61. package/dist/src/functions/db/local-db.js +1 -0
  62. package/dist/src/functions/runtimes/tanstack-start/pikku-command-tanstack-start.js +1 -1
  63. package/dist/src/scaffold/rpc-remote.gen.js +1 -1
  64. package/dist/src/utils/serialize-schemas.js +1 -1
  65. package/dist/tsconfig.tsbuildinfo +1 -1
  66. package/package.json +2 -2
  67. package/skills/pikku-i18n/SKILL.md +15 -6
  68. package/skills/pikku-rtl/SKILL.md +38 -24
  69. package/dist/.pikku/schemas/schemas/GraphStarterInput.schema.json +0 -1
  70. package/dist/.pikku/schemas/schemas/GraphStarterOutput.schema.json +0 -1
  71. package/dist/.pikku/schemas/schemas/WorkflowRunStatus.schema.json +0 -1
  72. package/dist/.pikku/schemas/schemas/WorkflowRunnerInput.schema.json +0 -1
  73. package/dist/.pikku/schemas/schemas/WorkflowStarterInput.schema.json +0 -1
  74. package/dist/.pikku/schemas/schemas/WorkflowStarterOutput.schema.json +0 -1
  75. package/dist/.pikku/schemas/schemas/WorkflowStatusCheckerInput.schema.json +0 -1
  76. package/dist/.pikku/schemas/schemas/WorkflowStatusStreamFullInput.schema.json +0 -1
  77. package/dist/.pikku/schemas/schemas/WorkflowStatusStreamInput.schema.json +0 -1
  78. package/dist/src/scaffold/workflow-routes.gen.d.ts +0 -84
  79. package/dist/src/scaffold/workflow-routes.gen.js +0 -229
@@ -14,7 +14,7 @@ registered `satisfies typeof en`) plus the document being told it is `rtl`.
14
14
  ## The one idea
15
15
 
16
16
  Set `dir` **once at the document root** from the active locale, then let the
17
- browser and Mantine mirror everything — *provided* every custom style is written
17
+ browser and Mantine mirror everything — _provided_ every custom style is written
18
18
  **flow-relative** (start/end), never **physical** (left/right). Get those two
19
19
  things right and Arabic, Hebrew, Farsi and Urdu all work with zero per-component
20
20
  RTL code.
@@ -46,22 +46,22 @@ RTL code.
46
46
 
47
47
  Use the **inline-axis logical** property; never the physical one:
48
48
 
49
- | Don't (physical) | Do (flow-relative) |
50
- | --------------------------- | ------------------------------------------- |
51
- | `margin-left` / `marginLeft`| `margin-inline-start` / `marginInlineStart` |
52
- | `margin-right` | `margin-inline-end` / `marginInlineEnd` |
53
- | `padding-left/right` | `padding-inline-start/end` |
54
- | `left: 0` / `right: 0` | `inset-inline-start: 0` / `inset-inline-end`|
55
- | `text-align: left/right` | `text-align: start / end` |
56
- | `border-top-left-radius` | `border-start-start-radius` |
57
- | `float: left/right` | `float: inline-start / inline-end` |
49
+ | Don't (physical) | Do (flow-relative) |
50
+ | ---------------------------- | -------------------------------------------- |
51
+ | `margin-left` / `marginLeft` | `margin-inline-start` / `marginInlineStart` |
52
+ | `margin-right` | `margin-inline-end` / `marginInlineEnd` |
53
+ | `padding-left/right` | `padding-inline-start/end` |
54
+ | `left: 0` / `right: 0` | `inset-inline-start: 0` / `inset-inline-end` |
55
+ | `text-align: left/right` | `text-align: start / end` |
56
+ | `border-top-left-radius` | `border-start-start-radius` |
57
+ | `float: left/right` | `float: inline-start / inline-end` |
58
58
 
59
59
  In **Mantine**, use the logical style props — they emit the logical CSS above:
60
60
 
61
- | Don't | Do |
62
- | ------------ | ------------- |
63
- | `ml` / `mr` | `ms` / `me` |
64
- | `pl` / `pr` | `ps` / `pe` |
61
+ | Don't | Do |
62
+ | ----------- | ----------- |
63
+ | `ml` / `mr` | `ms` / `me` |
64
+ | `pl` / `pr` | `ps` / `pe` |
65
65
 
66
66
  Mantine's own components already use logical properties internally, so once the
67
67
  direction is set they mirror automatically — you only have to be disciplined in
@@ -76,6 +76,7 @@ logical order; let `dir` handle the visual order.
76
76
  ## Applying direction at the root
77
77
 
78
78
  ### Mantine app (e.g. environment-template)
79
+
79
80
  Mantine ships first-class RTL: wrap the tree in `DirectionProvider` and set the
80
81
  matching `dir` on `<html>`.
81
82
 
@@ -83,14 +84,13 @@ matching `dir` on `<html>`.
83
84
  import { DirectionProvider, MantineProvider } from '@mantine/core'
84
85
  import i18n, { detectLocale, localeDir } from './i18n/config'
85
86
 
86
- const locale = typeof window !== 'undefined'
87
- ? detectLocale(window.location.pathname)
88
- : 'en'
87
+ const locale =
88
+ typeof window !== 'undefined' ? detectLocale(window.location.pathname) : 'en'
89
89
  const dir = localeDir(locale)
90
90
 
91
91
  if (typeof document !== 'undefined') {
92
92
  document.documentElement.lang = locale
93
- document.documentElement.dir = dir // Mantine + browser read this
93
+ document.documentElement.dir = dir // Mantine + browser read this
94
94
  }
95
95
 
96
96
  root.render(
@@ -98,14 +98,16 @@ root.render(
98
98
  <MantineProvider theme={theme} defaultColorScheme="dark">
99
99
  {/* …app… */}
100
100
  </MantineProvider>
101
- </DirectionProvider>,
101
+ </DirectionProvider>
102
102
  )
103
103
  ```
104
+
104
105
  To flip direction live (a language switcher) call
105
106
  `document.documentElement.setAttribute('dir', localeDir(next))` and Mantine's
106
107
  `useDirection().setDirection(dir)`; both read the same value.
107
108
 
108
109
  ### Plain Vite SPA (kanban, test-harness vite-spa)
110
+
109
111
  No Mantine — just put `dir`/`lang` on `<html>` at bootstrap, after the locale is
110
112
  detected (the same `detectLocale` the i18n config uses):
111
113
 
@@ -116,9 +118,11 @@ const locale = detectLocale(window.location.pathname)
116
118
  document.documentElement.lang = locale
117
119
  document.documentElement.dir = localeDir(locale)
118
120
  ```
121
+
119
122
  Everything below inherits `dir` from `<html>`; logical CSS does the mirroring.
120
123
 
121
124
  ### Vite SSR (test-harness vite-ssr)
125
+
122
126
  The worker renders the full HTML, so set `lang`/`dir` on the server `<html>`
123
127
  from the **URL** locale (the client inherits it on hydration — no flash):
124
128
 
@@ -132,17 +136,23 @@ const html = `<!doctype html>
132
136
 
133
137
  </html>`
134
138
  ```
139
+
135
140
  i18next's active language must match: call `i18n.changeLanguage(locale)` before
136
141
  `renderToString` so the SSR'd text and `dir` agree.
137
142
 
138
143
  ### Next.js app-router (test-harness next-ssr / next-static)
144
+
139
145
  Set it on the `<html>` in `app/layout.tsx`. With locale-prefixed routes the
140
146
  segment gives the locale; for a single-locale build it's a constant:
141
147
 
142
148
  ```tsx
143
149
  import { localeDir, defaultLocale } from './i18n/config'
144
150
 
145
- export default function RootLayout({ children }: { children: React.ReactNode }) {
151
+ export default function RootLayout({
152
+ children,
153
+ }: {
154
+ children: React.ReactNode
155
+ }) {
146
156
  const locale = defaultLocale // or the [lang] route segment / params
147
157
  return (
148
158
  <html lang={locale} dir={localeDir(locale)}>
@@ -151,6 +161,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
151
161
  )
152
162
  }
153
163
  ```
164
+
154
165
  For `output: 'export'` with `/ar` prefixes, derive `locale` from the route
155
166
  segment so each statically-exported tree carries the right `dir`.
156
167
 
@@ -162,8 +173,11 @@ non-directional icon (search, settings, avatar) must **not**. Flip with the
162
173
  `:dir()` selector — no JS, no per-locale branching:
163
174
 
164
175
  ```css
165
- :dir(rtl) .icon-directional { transform: scaleX(-1); }
176
+ :dir(rtl) .icon-directional {
177
+ transform: scaleX(-1);
178
+ }
166
179
  ```
180
+
167
181
  Or in CSS-in-JS / inline, gate on the resolved direction:
168
182
  `transform: localeDir(locale) === 'rtl' ? 'scaleX(-1)' : undefined`.
169
183
  Prefer logical icon components if your icon set ships them.
@@ -171,8 +185,8 @@ Prefer logical icon components if your icon set ships them.
171
185
  ## Arabic typography niceties
172
186
 
173
187
  - **Font:** the default Latin stack renders Arabic with the system fallback,
174
- which is inconsistent. Add an Arabic-capable family (e.g. *Noto Sans Arabic*,
175
- *IBM Plex Sans Arabic*) to `font-family` so both scripts look intentional.
188
+ which is inconsistent. Add an Arabic-capable family (e.g. _Noto Sans Arabic_,
189
+ _IBM Plex Sans Arabic_) to `font-family` so both scripts look intentional.
176
190
  - **Numerals:** don't hardcode digits. Format numbers/dates with
177
191
  `Intl.NumberFormat`/`Intl.DateTimeFormat` (or i18next formatters) given the
178
192
  active locale, so Western vs Arabic-Indic digits follow the locale choice.
@@ -187,7 +201,7 @@ Prefer logical icon components if your icon set ships them.
187
201
  2. Confirm the `localeDir` helper includes `ar` (it does by default).
188
202
  3. Confirm the root sets `dir` from the locale (recipe above).
189
203
  4. Sweep the app's styles: replace every `left/right`, `ml/mr`, `text-align:
190
- left` with the flow-relative equivalent; revert any manual `row-reverse`.
204
+ left` with the flow-relative equivalent; revert any manual `row-reverse`.
191
205
  5. Flip directional icons.
192
206
  6. `tsc`, then load the Arabic route and verify the whole layout mirrors —
193
207
  sidebar on the right, text right-aligned, arrows pointing the other way.
@@ -1 +0,0 @@
1
- { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "workflowName": { "type": "string" }, "nodeId": { "type": "string" }, "data": {} }, "required": ["workflowName", "nodeId"], "additionalProperties": false, "definitions": {} }
@@ -1 +0,0 @@
1
- { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "runId": { "type": "string" } }, "required": ["runId"], "additionalProperties": false, "definitions": {} }
@@ -1 +0,0 @@
1
- { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "id": { "type": "string" }, "status": { "$ref": "#/definitions/WorkflowStatus" }, "startedAt": { "type": "string", "format": "date-time" }, "completedAt": { "type": "string", "format": "date-time" }, "deterministic": { "type": "boolean" }, "plannedSteps": { "type": "array", "items": { "$ref": "#/definitions/WorkflowPlannedStep" } }, "steps": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string" }, "status": { "$ref": "#/definitions/StepStatus" }, "duration": { "type": "number" } }, "required": ["name", "status"], "additionalProperties": false } }, "output": {}, "error": { "type": "object", "properties": { "message": { "type": "string" } }, "required": ["message"], "additionalProperties": false } }, "required": ["id", "status", "startedAt", "steps"], "additionalProperties": false, "definitions": { "WorkflowStatus": { "type": "string", "enum": ["running", "suspended", "completed", "failed", "cancelled"], "description": "Workflow run status" }, "WorkflowPlannedStep": { "type": "object", "properties": { "stepName": { "type": "string", "description": "Human-readable step label for UI timeline" } }, "required": ["stepName"], "additionalProperties": false }, "StepStatus": { "type": "string", "enum": ["pending", "running", "scheduled", "succeeded", "failed", "suspended"], "description": "Workflow step status" } } }
@@ -1 +0,0 @@
1
- { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "workflowName": { "type": "string" }, "data": {} }, "required": ["workflowName"], "additionalProperties": false, "definitions": {} }
@@ -1 +0,0 @@
1
- { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "workflowName": { "type": "string" }, "data": {} }, "required": ["workflowName"], "additionalProperties": false, "definitions": {} }
@@ -1 +0,0 @@
1
- { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "runId": { "type": "string" } }, "required": ["runId"], "additionalProperties": false, "definitions": {} }
@@ -1 +0,0 @@
1
- { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "workflowName": { "type": "string" }, "runId": { "type": "string" } }, "required": ["workflowName", "runId"], "additionalProperties": false, "definitions": {} }
@@ -1 +0,0 @@
1
- { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "workflowName": { "type": "string" }, "runId": { "type": "string" } }, "required": ["workflowName", "runId"], "additionalProperties": false, "definitions": {} }
@@ -1 +0,0 @@
1
- { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "workflowName": { "type": "string" }, "runId": { "type": "string" } }, "required": ["workflowName", "runId"], "additionalProperties": false, "definitions": {} }
@@ -1,84 +0,0 @@
1
- import type { WorkflowRunStatus } from '@pikku/core/workflow';
2
- export declare const workflowStarter: import("../../.pikku/pikku-types.gen.js").PikkuFunctionConfig<{
3
- workflowName: string;
4
- data?: unknown;
5
- }, {
6
- runId: string;
7
- }, "rpc" | "session", import("../../.pikku/pikku-types.gen.js").PikkuFunctionSessionless<{
8
- workflowName: string;
9
- data?: unknown;
10
- }, {
11
- runId: string;
12
- }, "rpc" | "session", import("../../types/application-types.js").Services> | import("../../.pikku/pikku-types.gen.js").PikkuFunction<{
13
- workflowName: string;
14
- data?: unknown;
15
- }, {
16
- runId: string;
17
- }, "rpc" | "session", import("../../types/application-types.js").Services>, undefined, undefined>;
18
- export declare const workflowRunner: import("../../.pikku/pikku-types.gen.js").PikkuFunctionConfig<{
19
- workflowName: string;
20
- data?: unknown;
21
- }, unknown, "rpc" | "session", import("../../.pikku/pikku-types.gen.js").PikkuFunctionSessionless<{
22
- workflowName: string;
23
- data?: unknown;
24
- }, unknown, "rpc" | "session", import("../../types/application-types.js").Services> | import("../../.pikku/pikku-types.gen.js").PikkuFunction<{
25
- workflowName: string;
26
- data?: unknown;
27
- }, unknown, "rpc" | "session", import("../../types/application-types.js").Services>, undefined, undefined>;
28
- export declare const workflowStatusChecker: import("../../.pikku/pikku-types.gen.js").PikkuFunctionConfig<{
29
- workflowName: string;
30
- runId: string;
31
- }, WorkflowRunStatus, "rpc" | "session", import("../../.pikku/pikku-types.gen.js").PikkuFunctionSessionless<{
32
- workflowName: string;
33
- runId: string;
34
- }, WorkflowRunStatus, "rpc" | "session", import("../../types/application-types.js").Services> | import("../../.pikku/pikku-types.gen.js").PikkuFunction<{
35
- workflowName: string;
36
- runId: string;
37
- }, WorkflowRunStatus, "rpc" | "session", import("../../types/application-types.js").Services>, undefined, undefined>;
38
- /**
39
- * Minimal workflow status stream — sends step names and statuses only.
40
- * Use this for user-facing frontends where internal details should not be exposed.
41
- */
42
- export declare const workflowStatusStream: import("../../.pikku/pikku-types.gen.js").PikkuFunctionConfig<{
43
- workflowName: string;
44
- runId: string;
45
- }, unknown, "rpc" | "session", import("../../.pikku/pikku-types.gen.js").PikkuFunctionSessionless<{
46
- workflowName: string;
47
- runId: string;
48
- }, unknown, "rpc" | "session", import("../../types/application-types.js").Services> | import("../../.pikku/pikku-types.gen.js").PikkuFunction<{
49
- workflowName: string;
50
- runId: string;
51
- }, unknown, "rpc" | "session", import("../../types/application-types.js").Services>, undefined, undefined>;
52
- /**
53
- * Full workflow status stream — includes output, error, and child run IDs.
54
- * Use this for admin consoles and internal tooling.
55
- */
56
- export declare const workflowStatusStreamFull: import("../../.pikku/pikku-types.gen.js").PikkuFunctionConfig<{
57
- workflowName: string;
58
- runId: string;
59
- }, unknown, "rpc" | "session", import("../../.pikku/pikku-types.gen.js").PikkuFunctionSessionless<{
60
- workflowName: string;
61
- runId: string;
62
- }, unknown, "rpc" | "session", import("../../types/application-types.js").Services> | import("../../.pikku/pikku-types.gen.js").PikkuFunction<{
63
- workflowName: string;
64
- runId: string;
65
- }, unknown, "rpc" | "session", import("../../types/application-types.js").Services>, undefined, undefined>;
66
- export declare const graphStarter: import("../../.pikku/pikku-types.gen.js").PikkuFunctionConfig<{
67
- workflowName: string;
68
- nodeId: string;
69
- data?: unknown;
70
- }, {
71
- runId: string;
72
- }, "rpc" | "session", import("../../.pikku/pikku-types.gen.js").PikkuFunctionSessionless<{
73
- workflowName: string;
74
- nodeId: string;
75
- data?: unknown;
76
- }, {
77
- runId: string;
78
- }, "rpc" | "session", import("../../types/application-types.js").Services> | import("../../.pikku/pikku-types.gen.js").PikkuFunction<{
79
- workflowName: string;
80
- nodeId: string;
81
- data?: unknown;
82
- }, {
83
- runId: string;
84
- }, "rpc" | "session", import("../../types/application-types.js").Services>, undefined, undefined>;
@@ -1,229 +0,0 @@
1
- /**
2
- * This file was generated by @pikku/cli@0.12.21
3
- */
4
- /**
5
- * Workflow HTTP catch-all routes
6
- * Do not edit manually - regenerate with 'npx pikku'
7
- */
8
- import { pikkuSessionlessFunc, wireHTTPRoutes } from '../../.pikku/pikku-types.gen.js';
9
- import { MissingServiceError } from '@pikku/core/errors';
10
- function assertWorkflowService(workflowService) {
11
- if (!workflowService)
12
- throw new MissingServiceError('workflowService is required');
13
- }
14
- function assertWorkflowRunService(workflowRunService) {
15
- if (!workflowRunService)
16
- throw new MissingServiceError('workflowRunService is required');
17
- }
18
- export const workflowStarter = pikkuSessionlessFunc({
19
- auth: false,
20
- func: async (_services, { workflowName, data }, { rpc }) => {
21
- return await rpc.startWorkflow(workflowName, (data ?? {}));
22
- },
23
- });
24
- export const workflowRunner = pikkuSessionlessFunc({
25
- auth: false,
26
- func: async ({ workflowService }, { workflowName, data }, { rpc }) => {
27
- assertWorkflowService(workflowService);
28
- return await workflowService.runToCompletion(workflowName, (data ?? {}), rpc);
29
- },
30
- });
31
- export const workflowStatusChecker = pikkuSessionlessFunc({
32
- auth: false,
33
- func: async ({ workflowService }, { runId }) => {
34
- assertWorkflowService(workflowService);
35
- const status = await workflowService.getRunStatus(runId);
36
- if (!status)
37
- throw new Error(`Run not found: ${runId}`);
38
- return status;
39
- },
40
- });
41
- /**
42
- * Minimal workflow status stream — sends step names and statuses only.
43
- * Use this for user-facing frontends where internal details should not be exposed.
44
- */
45
- export const workflowStatusStream = pikkuSessionlessFunc({
46
- auth: false,
47
- func: async ({ workflowRunService }, { runId }, { channel }) => {
48
- assertWorkflowRunService(workflowRunService);
49
- if (!channel)
50
- return;
51
- const terminalStatuses = new Set(['completed', 'failed', 'cancelled']);
52
- let lastHash = '';
53
- let initSent = false;
54
- const poll = async () => {
55
- const run = await workflowRunService.getRun(runId);
56
- if (!run) {
57
- channel.close();
58
- return false;
59
- }
60
- const steps = await workflowRunService.getRunSteps(runId);
61
- if (!initSent && run.deterministic) {
62
- const statusByStep = new Map(steps.map((s) => [
63
- s.stepName,
64
- s.status,
65
- ]));
66
- channel.send({
67
- type: 'init',
68
- deterministic: true,
69
- steps: (run.plannedSteps ?? []).map((s) => ({
70
- stepName: s.stepName,
71
- status: statusByStep.get(s.stepName) ?? 'pending',
72
- })),
73
- });
74
- initSent = true;
75
- }
76
- const hash = JSON.stringify({
77
- s: run.status,
78
- steps: steps.map((s) => [s.stepName, s.status]),
79
- });
80
- if (hash !== lastHash) {
81
- lastHash = hash;
82
- channel.send({
83
- type: 'update',
84
- status: run.status,
85
- steps: steps.map((s) => ({
86
- stepName: s.stepName,
87
- status: s.status,
88
- })),
89
- });
90
- }
91
- if (terminalStatuses.has(run.status)) {
92
- channel.send({ type: 'done' });
93
- channel.close();
94
- return false;
95
- }
96
- return true;
97
- };
98
- const shouldContinue = await poll();
99
- if (!shouldContinue)
100
- return;
101
- await new Promise((resolve) => {
102
- const interval = setInterval(async () => {
103
- const cont = await poll();
104
- if (!cont) {
105
- clearInterval(interval);
106
- resolve();
107
- }
108
- }, 500);
109
- });
110
- },
111
- });
112
- /**
113
- * Full workflow status stream — includes output, error, and child run IDs.
114
- * Use this for admin consoles and internal tooling.
115
- */
116
- export const workflowStatusStreamFull = pikkuSessionlessFunc({
117
- auth: false,
118
- func: async ({ workflowRunService }, { runId }, { channel }) => {
119
- assertWorkflowRunService(workflowRunService);
120
- if (!channel)
121
- return;
122
- const terminalStatuses = new Set(['completed', 'failed', 'cancelled']);
123
- let lastHash = '';
124
- let initSent = false;
125
- const poll = async () => {
126
- const run = await workflowRunService.getRun(runId);
127
- if (!run) {
128
- channel.close();
129
- return false;
130
- }
131
- const steps = await workflowRunService.getRunSteps(runId);
132
- if (!initSent && run.deterministic) {
133
- const statusByStep = new Map(steps.map((s) => [
134
- s.stepName,
135
- s.status,
136
- ]));
137
- channel.send({
138
- type: 'init',
139
- deterministic: true,
140
- steps: (run.plannedSteps ?? []).map((s) => ({
141
- stepName: s.stepName,
142
- status: statusByStep.get(s.stepName) ?? 'pending',
143
- })),
144
- });
145
- initSent = true;
146
- }
147
- const hash = JSON.stringify({
148
- s: run.status,
149
- o: run.output,
150
- steps: steps.map((s) => [s.stepName, s.status]),
151
- });
152
- if (hash !== lastHash) {
153
- lastHash = hash;
154
- channel.send({
155
- type: 'update',
156
- status: run.status,
157
- output: run.output,
158
- error: run.error,
159
- steps: steps.map((s) => ({
160
- stepName: s.stepName,
161
- status: s.status,
162
- ...(s.childRunId ? { childRunId: s.childRunId } : {}),
163
- })),
164
- });
165
- }
166
- if (terminalStatuses.has(run.status)) {
167
- channel.send({ type: 'done' });
168
- channel.close();
169
- return false;
170
- }
171
- return true;
172
- };
173
- const shouldContinue = await poll();
174
- if (!shouldContinue)
175
- return;
176
- await new Promise((resolve) => {
177
- const interval = setInterval(async () => {
178
- const cont = await poll();
179
- if (!cont) {
180
- clearInterval(interval);
181
- resolve();
182
- }
183
- }, 500);
184
- });
185
- },
186
- });
187
- export const graphStarter = pikkuSessionlessFunc({
188
- auth: false,
189
- func: async (_services, { workflowName, nodeId, data }, { rpc }) => {
190
- return await rpc.startWorkflow(workflowName, (data ?? {}), { startNode: nodeId });
191
- },
192
- });
193
- wireHTTPRoutes({
194
- auth: false,
195
- routes: {
196
- workflowStart: {
197
- route: '/workflow/:workflowName/start',
198
- method: 'post',
199
- func: workflowStarter,
200
- },
201
- workflowRun: {
202
- route: '/workflow/:workflowName/run',
203
- method: 'post',
204
- func: workflowRunner,
205
- },
206
- workflowStatus: {
207
- route: '/workflow/:workflowName/status/:runId',
208
- method: 'get',
209
- func: workflowStatusChecker,
210
- },
211
- workflowStatusStream: {
212
- route: '/workflow/:workflowName/status/:runId/stream',
213
- method: 'get',
214
- sse: true,
215
- func: workflowStatusStream,
216
- },
217
- workflowStatusStreamFull: {
218
- route: '/workflow/:workflowName/status/:runId/stream/full',
219
- method: 'get',
220
- sse: true,
221
- func: workflowStatusStreamFull,
222
- },
223
- graphStart: {
224
- route: '/workflow/:workflowName/graph/:nodeId',
225
- method: 'post',
226
- func: graphStarter,
227
- },
228
- },
229
- });