@loopstack/cli 0.17.0 → 0.19.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 (131) hide show
  1. package/README.md +14 -10
  2. package/dist/commands/answer.d.ts +3 -0
  3. package/dist/commands/answer.d.ts.map +1 -0
  4. package/dist/commands/answer.js +82 -0
  5. package/dist/commands/answer.js.map +1 -0
  6. package/dist/commands/attach.d.ts +3 -0
  7. package/dist/commands/attach.d.ts.map +1 -0
  8. package/dist/commands/attach.js +103 -0
  9. package/dist/commands/attach.js.map +1 -0
  10. package/dist/commands/create.js +3 -3
  11. package/dist/commands/create.js.map +1 -1
  12. package/dist/commands/run.d.ts.map +1 -1
  13. package/dist/commands/run.js +38 -13
  14. package/dist/commands/run.js.map +1 -1
  15. package/dist/commands/runs.d.ts.map +1 -1
  16. package/dist/commands/runs.js +67 -69
  17. package/dist/commands/runs.js.map +1 -1
  18. package/dist/commands/watch.d.ts.map +1 -1
  19. package/dist/commands/watch.js +3 -1
  20. package/dist/commands/watch.js.map +1 -1
  21. package/dist/hitl/discovery.d.ts +15 -7
  22. package/dist/hitl/discovery.d.ts.map +1 -1
  23. package/dist/hitl/discovery.js +85 -28
  24. package/dist/hitl/discovery.js.map +1 -1
  25. package/dist/hitl/idle-handler.d.ts +0 -1
  26. package/dist/hitl/idle-handler.d.ts.map +1 -1
  27. package/dist/hitl/idle-handler.js +49 -17
  28. package/dist/hitl/idle-handler.js.map +1 -1
  29. package/dist/hitl/pending.d.ts +16 -0
  30. package/dist/hitl/pending.d.ts.map +1 -0
  31. package/dist/hitl/pending.js +45 -0
  32. package/dist/hitl/pending.js.map +1 -0
  33. package/dist/hitl/prompt.d.ts +5 -8
  34. package/dist/hitl/prompt.d.ts.map +1 -1
  35. package/dist/hitl/prompt.js +47 -105
  36. package/dist/hitl/prompt.js.map +1 -1
  37. package/dist/main.js +4 -0
  38. package/dist/main.js.map +1 -1
  39. package/dist/output/format.d.ts +1 -1
  40. package/dist/output/format.d.ts.map +1 -1
  41. package/dist/output/format.js +16 -5
  42. package/dist/output/format.js.map +1 -1
  43. package/dist/run/documents.d.ts +13 -1
  44. package/dist/run/documents.d.ts.map +1 -1
  45. package/dist/run/documents.js +82 -22
  46. package/dist/run/documents.js.map +1 -1
  47. package/dist/run/follow.d.ts +6 -2
  48. package/dist/run/follow.d.ts.map +1 -1
  49. package/dist/run/follow.js +61 -9
  50. package/dist/run/follow.js.map +1 -1
  51. package/dist/run/retry.d.ts +5 -0
  52. package/dist/run/retry.d.ts.map +1 -0
  53. package/dist/run/retry.js +59 -0
  54. package/dist/run/retry.js.map +1 -0
  55. package/dist/run/trail.d.ts +17 -0
  56. package/dist/run/trail.d.ts.map +1 -0
  57. package/dist/run/trail.js +42 -0
  58. package/dist/run/trail.js.map +1 -0
  59. package/dist/widgets/collect/form.collect.d.ts +7 -0
  60. package/dist/widgets/collect/form.collect.d.ts.map +1 -0
  61. package/dist/widgets/collect/form.collect.js +71 -0
  62. package/dist/widgets/collect/form.collect.js.map +1 -0
  63. package/dist/widgets/collect/prompts.collect.d.ts +5 -0
  64. package/dist/widgets/collect/prompts.collect.d.ts.map +1 -0
  65. package/dist/widgets/collect/prompts.collect.js +38 -0
  66. package/dist/widgets/collect/prompts.collect.js.map +1 -0
  67. package/dist/widgets/collect/secret-input.collect.d.ts +3 -0
  68. package/dist/widgets/collect/secret-input.collect.d.ts.map +1 -0
  69. package/dist/widgets/collect/secret-input.collect.js +47 -0
  70. package/dist/widgets/collect/secret-input.collect.js.map +1 -0
  71. package/dist/widgets/collect/transition.d.ts +3 -0
  72. package/dist/widgets/collect/transition.d.ts.map +1 -0
  73. package/dist/widgets/collect/transition.js +5 -0
  74. package/dist/widgets/collect/transition.js.map +1 -0
  75. package/dist/widgets/collect/workflow.collect.d.ts +4 -0
  76. package/dist/widgets/collect/workflow.collect.d.ts.map +1 -0
  77. package/dist/widgets/collect/workflow.collect.js +24 -0
  78. package/dist/widgets/collect/workflow.collect.js.map +1 -0
  79. package/dist/widgets/document/form.widget.d.ts +3 -0
  80. package/dist/widgets/document/form.widget.d.ts.map +1 -0
  81. package/dist/widgets/document/form.widget.js +34 -0
  82. package/dist/widgets/document/form.widget.js.map +1 -0
  83. package/dist/widgets/document/json.widget.d.ts +3 -0
  84. package/dist/widgets/document/json.widget.d.ts.map +1 -0
  85. package/dist/widgets/document/json.widget.js +17 -0
  86. package/dist/widgets/document/json.widget.js.map +1 -0
  87. package/dist/widgets/document/link.widget.d.ts +3 -0
  88. package/dist/widgets/document/link.widget.d.ts.map +1 -0
  89. package/dist/widgets/document/link.widget.js +9 -0
  90. package/dist/widgets/document/link.widget.js.map +1 -0
  91. package/dist/widgets/document/llm-message.widget.d.ts +3 -0
  92. package/dist/widgets/document/llm-message.widget.d.ts.map +1 -0
  93. package/dist/widgets/document/llm-message.widget.js +97 -0
  94. package/dist/widgets/document/llm-message.widget.js.map +1 -0
  95. package/dist/widgets/document/message.widget.d.ts +5 -0
  96. package/dist/widgets/document/message.widget.d.ts.map +1 -0
  97. package/dist/widgets/document/message.widget.js +35 -0
  98. package/dist/widgets/document/message.widget.js.map +1 -0
  99. package/dist/widgets/document/oauth-prompt.widget.d.ts +4 -0
  100. package/dist/widgets/document/oauth-prompt.widget.d.ts.map +1 -0
  101. package/dist/widgets/document/oauth-prompt.widget.js +22 -0
  102. package/dist/widgets/document/oauth-prompt.widget.js.map +1 -0
  103. package/dist/widgets/document/secret-request.widget.d.ts +3 -0
  104. package/dist/widgets/document/secret-request.widget.d.ts.map +1 -0
  105. package/dist/widgets/document/secret-request.widget.js +9 -0
  106. package/dist/widgets/document/secret-request.widget.js.map +1 -0
  107. package/dist/{hitl → widgets}/editor.d.ts +0 -1
  108. package/dist/widgets/editor.d.ts.map +1 -0
  109. package/dist/{hitl → widgets}/editor.js +0 -13
  110. package/dist/widgets/editor.js.map +1 -0
  111. package/dist/widgets/field/field-widgets.d.ts +6 -0
  112. package/dist/widgets/field/field-widgets.d.ts.map +1 -0
  113. package/dist/widgets/field/field-widgets.js +64 -0
  114. package/dist/widgets/field/field-widgets.js.map +1 -0
  115. package/dist/widgets/registry.d.ts +6 -0
  116. package/dist/widgets/registry.d.ts.map +1 -0
  117. package/dist/widgets/registry.js +45 -0
  118. package/dist/widgets/registry.js.map +1 -0
  119. package/dist/widgets/types.d.ts +44 -0
  120. package/dist/widgets/types.d.ts.map +1 -0
  121. package/dist/widgets/types.js +2 -0
  122. package/dist/widgets/types.js.map +1 -0
  123. package/fixtures/app/CLAUDE.md +16 -2
  124. package/fixtures/app/README.md +8 -2
  125. package/fixtures/app/docker-compose.studio.yml +10 -0
  126. package/fixtures/app/docker-compose.yml +0 -8
  127. package/fixtures/app/env.example +5 -0
  128. package/fixtures/app/src/main.ts +3 -0
  129. package/package.json +3 -3
  130. package/dist/hitl/editor.d.ts.map +0 -1
  131. package/dist/hitl/editor.js.map +0 -1
@@ -6,7 +6,7 @@ This is a [Loopstack](https://loopstack.ai) app — a NestJS backend where AI wo
6
6
 
7
7
  - `src/app.module.ts` — mounts `LoopstackModule.forRoot()` plus your app modules
8
8
  - `src/hello/` — a complete example: `hello.module.ts` (`@StudioApp` groups workflows into an app) and `hello.workflow.ts` (a minimal workflow)
9
- - `docker-compose.yml` — Postgres, Redis, and Studio (http://localhost:5173)
9
+ - `docker-compose.yml` — Postgres + Redis (Studio is a separate, optional `docker-compose.studio.yml` on http://localhost:5173; you don't need it — use the CLI below)
10
10
  - `.env` — configuration; add API keys here when a module needs them
11
11
 
12
12
  ## The feedback loop — use the CLI, not guesswork
@@ -17,12 +17,16 @@ The `loopstack` CLI talks to the running backend (`npm run start:dev`, http://lo
17
17
  loopstack list # what can be run right now
18
18
  loopstack run <workflow> --arg k=v # run it, streamed live; --json for machine-readable output
19
19
  loopstack runs # recent runs, waiting-for-input first
20
- loopstack runs <run-id> --follow # audit trail; reattach to a waiting run and answer prompts
20
+ loopstack runs <run-id> # one run's audit trail (+ pendingPrompt under --json)
21
+ loopstack answer <run-id> --arg k=v # answer a waiting run's prompt non-interactively
22
+ loopstack attach <run-id> # rejoin a run live and answer its prompts interactively
21
23
  loopstack watch --json # NDJSON event firehose while you develop
22
24
  ```
23
25
 
24
26
  Exit codes: `0` completed, `1` failed, `2` connection/config error, `3` waiting for input in a non-interactive shell. After changing code, the dev server reloads automatically — rerun the workflow to verify.
25
27
 
28
+ When a run exits `3`, it parked on a human question. Close the loop instead of stopping: read the question with `loopstack runs <run-id> --json` (the `pendingPrompt` field carries the description, the expected answer schema, and the transition), ask the human for their answer, submit it with `loopstack answer <run-id> --arg key=value`, then re-check `loopstack runs <run-id>` for the outcome. Never invent an answer to a human question yourself.
29
+
26
30
  ## Writing workflows
27
31
 
28
32
  Follow `src/hello/hello.workflow.ts` as the canonical example:
@@ -33,6 +37,16 @@ Follow `src/hello/hello.workflow.ts` as the canonical example:
33
37
  - Documents are classes with `@Document({ schema })` (zod) saved via `this.documentStore.save(SomeDocument, data)` — they are what Studio renders.
34
38
  - Tools are NestJS providers with `@Tool({ name })`, implementing `protected async handle(args, ctx, options?): Promise<ToolEnvelope>`.
35
39
 
40
+ ## Testing
41
+
42
+ Tests are ordinary vitest tests that run the real engine in-process — no backend, no database:
43
+
44
+ - `runWorkflow(MyWorkflow, args, { providers, answers })` from `@loopstack/testing` executes the workflow and returns `{ status, path, result, documents, document(key) }` for assertions. Script HITL input with `answers: { waitTransitionName: payload }`; sub-workflows run inline.
45
+ - `testTool().forTool(MyTool)` unit-tests a tool's `handle()` in isolation.
46
+ - For deterministic LLM/tool regression tests, record a live run (`loopstack run <workflow> --trace`, then `loopstack runs <run-id> --record fixture.json`) and pass `replay: replay('fixture.json')` to `runWorkflow`. Omit `replay` to test against real providers — keep those assertions structural and run them on demand, not in CI.
47
+
48
+ Write workflow tests for every non-trivial workflow you build. Guide: https://loopstack.ai/docs/build/testing
49
+
36
50
  ## Learning more
37
51
 
38
52
  - Docs: https://loopstack.ai/docs — for agents, the full corpus is at https://loopstack.ai/llms.txt (route via titles, fetch what you need)
@@ -5,16 +5,22 @@ Scaffolded with `loopstack create`.
5
5
  ## Quickstart
6
6
 
7
7
  ```bash
8
- docker compose up -d # Postgres, Redis, and Studio
8
+ docker compose up -d # Postgres + Redis
9
9
  npm run start:dev # the backend on http://localhost:3000
10
10
  ```
11
11
 
12
- Open Studio at [http://localhost:5173](http://localhost:5173), or run the hello workflow from the terminal:
12
+ Run the hello workflow from the terminal:
13
13
 
14
14
  ```bash
15
15
  npx @loopstack/cli run hello --arg name=You
16
16
  ```
17
17
 
18
+ Prefer a visual UI? Start Studio (optional) and open [http://localhost:5173](http://localhost:5173):
19
+
20
+ ```bash
21
+ docker compose -f docker-compose.studio.yml up -d
22
+ ```
23
+
18
24
  ## Next steps
19
25
 
20
26
  - Build your first workflow: https://loopstack.ai/docs
@@ -0,0 +1,10 @@
1
+ name: loopstack
2
+
3
+ services:
4
+ studio:
5
+ image: ghcr.io/loopstack-ai/loopstack-studio:latest
6
+ pull_policy: always
7
+ ports:
8
+ - '5173:3000'
9
+ environment:
10
+ VITE_API_URL: ${VITE_API_URL:-http://localhost:3000}
@@ -20,14 +20,6 @@ services:
20
20
  volumes:
21
21
  - redis_data:/data
22
22
 
23
- studio:
24
- image: ghcr.io/loopstack-ai/loopstack-studio:latest
25
- pull_policy: always
26
- ports:
27
- - '5173:3000'
28
- environment:
29
- VITE_API_URL: ${VITE_API_URL:-http://localhost:3000}
30
-
31
23
  volumes:
32
24
  postgres_data:
33
25
  redis_data:
@@ -2,6 +2,11 @@
2
2
 
3
3
  # PORT=3000
4
4
 
5
+ # Point at any Postgres / Redis with a single URL (managed, hosted, or your own),
6
+ # or use the discrete vars below. A URL, when set, takes precedence.
7
+ # DATABASE_URL=postgres://postgres:admin@localhost:5432/postgres
8
+ # REDIS_URL=redis://localhost:6379
9
+
5
10
  # DATABASE_HOST=localhost
6
11
  # DATABASE_PORT=5432
7
12
  # DATABASE_USERNAME=postgres
@@ -3,6 +3,9 @@ import { AppModule } from './app.module';
3
3
 
4
4
  async function bootstrap() {
5
5
  const app = await NestFactory.create(AppModule);
6
+ // Graceful shutdown: on SIGTERM/SIGINT the BullMQ worker finishes in-flight
7
+ // transitions before the process exits, instead of stalling them.
8
+ app.enableShutdownHooks();
6
9
  await app.listen(process.env.PORT ?? 3000);
7
10
  }
8
11
  void bootstrap();
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@loopstack/cli",
3
3
  "displayName": "Loopstack CLI",
4
4
  "description": "Run, trace, and watch Loopstack workflows from the terminal",
5
- "version": "0.17.0",
5
+ "version": "0.19.0",
6
6
  "license": "MIT",
7
7
  "author": {
8
8
  "name": "Jakob Klippel",
@@ -26,8 +26,8 @@
26
26
  "watch": "tsc -p tsconfig.build.json --watch"
27
27
  },
28
28
  "dependencies": {
29
- "@loopstack/client": "^0.37.1",
30
- "@loopstack/contracts": "^0.37.0",
29
+ "@loopstack/client": "^0.39.0",
30
+ "@loopstack/contracts": "^0.39.0",
31
31
  "commander": "^15.0.0",
32
32
  "picocolors": "^1.1.1"
33
33
  },
@@ -1 +0,0 @@
1
- {"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../../src/hitl/editor.ts"],"names":[],"mappings":"AAiCA,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAS7G;AAGD,wBAAgB,kBAAkB,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,CAU7E;AAQD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,cAAc,GAAG,OAAO,CAqBzG"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"editor.js","sourceRoot":"","sources":["../../src/hitl/editor.ts"],"names":[],"mappings":"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"}