@loopstack/cli 0.17.0 → 0.18.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 (125) 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/run.d.ts.map +1 -1
  11. package/dist/commands/run.js +38 -13
  12. package/dist/commands/run.js.map +1 -1
  13. package/dist/commands/runs.d.ts.map +1 -1
  14. package/dist/commands/runs.js +67 -69
  15. package/dist/commands/runs.js.map +1 -1
  16. package/dist/commands/watch.d.ts.map +1 -1
  17. package/dist/commands/watch.js +3 -1
  18. package/dist/commands/watch.js.map +1 -1
  19. package/dist/hitl/discovery.d.ts +15 -7
  20. package/dist/hitl/discovery.d.ts.map +1 -1
  21. package/dist/hitl/discovery.js +85 -28
  22. package/dist/hitl/discovery.js.map +1 -1
  23. package/dist/hitl/idle-handler.d.ts +0 -1
  24. package/dist/hitl/idle-handler.d.ts.map +1 -1
  25. package/dist/hitl/idle-handler.js +49 -17
  26. package/dist/hitl/idle-handler.js.map +1 -1
  27. package/dist/hitl/pending.d.ts +16 -0
  28. package/dist/hitl/pending.d.ts.map +1 -0
  29. package/dist/hitl/pending.js +45 -0
  30. package/dist/hitl/pending.js.map +1 -0
  31. package/dist/hitl/prompt.d.ts +5 -8
  32. package/dist/hitl/prompt.d.ts.map +1 -1
  33. package/dist/hitl/prompt.js +47 -105
  34. package/dist/hitl/prompt.js.map +1 -1
  35. package/dist/main.js +4 -0
  36. package/dist/main.js.map +1 -1
  37. package/dist/output/format.d.ts +1 -1
  38. package/dist/output/format.d.ts.map +1 -1
  39. package/dist/output/format.js +16 -5
  40. package/dist/output/format.js.map +1 -1
  41. package/dist/run/documents.d.ts +13 -1
  42. package/dist/run/documents.d.ts.map +1 -1
  43. package/dist/run/documents.js +82 -22
  44. package/dist/run/documents.js.map +1 -1
  45. package/dist/run/follow.d.ts +6 -2
  46. package/dist/run/follow.d.ts.map +1 -1
  47. package/dist/run/follow.js +61 -9
  48. package/dist/run/follow.js.map +1 -1
  49. package/dist/run/retry.d.ts +5 -0
  50. package/dist/run/retry.d.ts.map +1 -0
  51. package/dist/run/retry.js +59 -0
  52. package/dist/run/retry.js.map +1 -0
  53. package/dist/run/trail.d.ts +17 -0
  54. package/dist/run/trail.d.ts.map +1 -0
  55. package/dist/run/trail.js +42 -0
  56. package/dist/run/trail.js.map +1 -0
  57. package/dist/widgets/collect/form.collect.d.ts +7 -0
  58. package/dist/widgets/collect/form.collect.d.ts.map +1 -0
  59. package/dist/widgets/collect/form.collect.js +71 -0
  60. package/dist/widgets/collect/form.collect.js.map +1 -0
  61. package/dist/widgets/collect/prompts.collect.d.ts +5 -0
  62. package/dist/widgets/collect/prompts.collect.d.ts.map +1 -0
  63. package/dist/widgets/collect/prompts.collect.js +38 -0
  64. package/dist/widgets/collect/prompts.collect.js.map +1 -0
  65. package/dist/widgets/collect/secret-input.collect.d.ts +3 -0
  66. package/dist/widgets/collect/secret-input.collect.d.ts.map +1 -0
  67. package/dist/widgets/collect/secret-input.collect.js +47 -0
  68. package/dist/widgets/collect/secret-input.collect.js.map +1 -0
  69. package/dist/widgets/collect/transition.d.ts +3 -0
  70. package/dist/widgets/collect/transition.d.ts.map +1 -0
  71. package/dist/widgets/collect/transition.js +5 -0
  72. package/dist/widgets/collect/transition.js.map +1 -0
  73. package/dist/widgets/collect/workflow.collect.d.ts +4 -0
  74. package/dist/widgets/collect/workflow.collect.d.ts.map +1 -0
  75. package/dist/widgets/collect/workflow.collect.js +24 -0
  76. package/dist/widgets/collect/workflow.collect.js.map +1 -0
  77. package/dist/widgets/document/form.widget.d.ts +3 -0
  78. package/dist/widgets/document/form.widget.d.ts.map +1 -0
  79. package/dist/widgets/document/form.widget.js +34 -0
  80. package/dist/widgets/document/form.widget.js.map +1 -0
  81. package/dist/widgets/document/json.widget.d.ts +3 -0
  82. package/dist/widgets/document/json.widget.d.ts.map +1 -0
  83. package/dist/widgets/document/json.widget.js +17 -0
  84. package/dist/widgets/document/json.widget.js.map +1 -0
  85. package/dist/widgets/document/link.widget.d.ts +3 -0
  86. package/dist/widgets/document/link.widget.d.ts.map +1 -0
  87. package/dist/widgets/document/link.widget.js +9 -0
  88. package/dist/widgets/document/link.widget.js.map +1 -0
  89. package/dist/widgets/document/llm-message.widget.d.ts +3 -0
  90. package/dist/widgets/document/llm-message.widget.d.ts.map +1 -0
  91. package/dist/widgets/document/llm-message.widget.js +67 -0
  92. package/dist/widgets/document/llm-message.widget.js.map +1 -0
  93. package/dist/widgets/document/message.widget.d.ts +5 -0
  94. package/dist/widgets/document/message.widget.d.ts.map +1 -0
  95. package/dist/widgets/document/message.widget.js +35 -0
  96. package/dist/widgets/document/message.widget.js.map +1 -0
  97. package/dist/widgets/document/oauth-prompt.widget.d.ts +4 -0
  98. package/dist/widgets/document/oauth-prompt.widget.d.ts.map +1 -0
  99. package/dist/widgets/document/oauth-prompt.widget.js +22 -0
  100. package/dist/widgets/document/oauth-prompt.widget.js.map +1 -0
  101. package/dist/widgets/document/secret-request.widget.d.ts +3 -0
  102. package/dist/widgets/document/secret-request.widget.d.ts.map +1 -0
  103. package/dist/widgets/document/secret-request.widget.js +9 -0
  104. package/dist/widgets/document/secret-request.widget.js.map +1 -0
  105. package/dist/{hitl → widgets}/editor.d.ts +0 -1
  106. package/dist/widgets/editor.d.ts.map +1 -0
  107. package/dist/{hitl → widgets}/editor.js +0 -13
  108. package/dist/widgets/editor.js.map +1 -0
  109. package/dist/widgets/field/field-widgets.d.ts +6 -0
  110. package/dist/widgets/field/field-widgets.d.ts.map +1 -0
  111. package/dist/widgets/field/field-widgets.js +64 -0
  112. package/dist/widgets/field/field-widgets.js.map +1 -0
  113. package/dist/widgets/registry.d.ts +6 -0
  114. package/dist/widgets/registry.d.ts.map +1 -0
  115. package/dist/widgets/registry.js +45 -0
  116. package/dist/widgets/registry.js.map +1 -0
  117. package/dist/widgets/types.d.ts +44 -0
  118. package/dist/widgets/types.d.ts.map +1 -0
  119. package/dist/widgets/types.js +2 -0
  120. package/dist/widgets/types.js.map +1 -0
  121. package/fixtures/app/CLAUDE.md +15 -1
  122. package/fixtures/app/src/main.ts +3 -0
  123. package/package.json +3 -3
  124. package/dist/hitl/editor.d.ts.map +0 -1
  125. package/dist/hitl/editor.js.map +0 -1
package/README.md CHANGED
@@ -24,7 +24,7 @@ result: {"greeting":"Hello, Maya! 👋"}
24
24
  ■ run completed in 96ms
25
25
  ```
26
26
 
27
- When a run asks a question, answer it right there — or in Studio; whichever answers first wins, and the CLI keeps following either way. Field forms hand off to Studio via deep link (or open in `$EDITOR` with `--editor`):
27
+ When a run asks a question, answer it right there — or in Studio; whichever answers first wins, and the CLI keeps following either way. Choices, confirms, chat inputs, and secret entry render as native prompts; forms submit as-is or open the complete content in `$EDITOR` first:
28
28
 
29
29
  ```
30
30
  ⏸ waiting_for_answer
@@ -36,17 +36,21 @@ answer: 2
36
36
  ■ run completed
37
37
  ```
38
38
 
39
+ Input only Studio can collect is named explicitly (with a deep link) instead of hanging, and browser-based waits — like an OAuth sign-in link — resume the attached session automatically once completed.
40
+
39
41
  ## Commands
40
42
 
41
- | Command | What it does |
42
- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
43
- | `loopstack create <dir>` | Scaffold a new Loopstack app (NestJS + LoopstackModule + hello workflow + docker-compose) |
44
- | `loopstack list [workflow]` | Workflows you can run; with a name: its arguments and a ready-to-run example |
45
- | `loopstack run <workflow>` | Start a run and follow it live; `--arg k=v`, `--arg k=@file.json`, `--arg k=@-` (stdin), `--detach`, `--quiet`, `--open`, `--json` |
46
- | `loopstack runs [run-id]` | Recent runs (waiting-for-input first) or one run’s audit trail; `--follow` reattaches and answers prompts |
47
- | `loopstack watch` | Live event stream; `--workflow`, `--type`, NDJSON with `--json` |
48
- | `loopstack login` | Save a backend environment to `~/.loopstack/config.json` |
49
- | `loopstack env list\|use` | Manage saved environments |
43
+ | Command | What it does |
44
+ | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
45
+ | `loopstack create <dir>` | Scaffold a new Loopstack app (NestJS + LoopstackModule + hello workflow + docker-compose) |
46
+ | `loopstack list [workflow]` | Workflows you can run; with a name: its arguments and a ready-to-run example |
47
+ | `loopstack run <workflow>` | Start a run and follow it live; `--arg k=v`, `--arg k=@file.json`, `--arg k=@-` (stdin), `--detach`, `--quiet`, `--open`, `--json` |
48
+ | `loopstack runs [run-id]` | Recent runs (waiting-for-input first) or one run’s full transcript; `--json` includes the pending prompt (`pendingPrompt`), `--record <file>` derives a replay fixture from the run’s recorded tool calls |
49
+ | `loopstack answer <run-id>` | Answer a waiting run’s prompt non-interactively — `--arg k=v` fields or `--payload <json>`; for agents and scripts |
50
+ | `loopstack attach <run-id>` | Rejoin a run live transcript so far, then streaming output and interactive prompts (docker/tmux semantics) |
51
+ | `loopstack watch` | Live event stream; `--workflow`, `--type`, NDJSON with `--json` |
52
+ | `loopstack login` | Save a backend environment to `~/.loopstack/config.json` |
53
+ | `loopstack env list\|use` | Manage saved environments |
50
54
 
51
55
  ## CI
52
56
 
@@ -0,0 +1,3 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerAnswerCommand(program: Command): void;
3
+ //# sourceMappingURL=answer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"answer.d.ts","sourceRoot":"","sources":["../../src/commands/answer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAwBzC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAyD5D"}
@@ -0,0 +1,82 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import pc from 'picocolors';
3
+ import { createClientFor, resolveConnection } from '../config/resolve.js';
4
+ import { CliError, ExitCode } from '../errors.js';
5
+ import { inspectPendingPrompt } from '../hitl/pending.js';
6
+ import { printData, printStatus } from '../output/format.js';
7
+ import { parseRunArgs } from '../run/args.js';
8
+ const collect = (value, previous) => [...previous, value];
9
+ export function registerAnswerCommand(program) {
10
+ program
11
+ .command('answer <runId>')
12
+ .description('Answer the prompt a waiting run is parked on — non-interactive (for agents and scripts)')
13
+ .option('--arg <key=value>', 'answer field (repeatable); key=@file reads a file, key=@- reads stdin', collect, [])
14
+ .option('--payload <json>', 'full answer payload as JSON; @file reads a file, @- reads stdin')
15
+ .option('--transition <id>', 'wait transition to answer (required when several are available)')
16
+ .action(async (runId, options, cmd) => {
17
+ const globals = cmd.optsWithGlobals();
18
+ const connection = resolveConnection(globals);
19
+ const client = createClientFor(connection);
20
+ const json = !!globals.json;
21
+ const pending = await inspectPendingPrompt(client, runId);
22
+ if (!pending) {
23
+ throw new CliError(`Run ${runId} has no pending prompt — nothing to answer.`);
24
+ }
25
+ if (pending.studioOnly) {
26
+ throw new CliError(`The pending input (${pending.widget}) can only be collected in Studio — the CLI cannot answer it.`);
27
+ }
28
+ const transition = options.transition ?? pending.transition;
29
+ if (!transition) {
30
+ throw new CliError(`Several transitions are available (${pending.transitions.join(', ')}) — pick one with --transition <id>.`);
31
+ }
32
+ if (!pending.transitions.includes(transition)) {
33
+ throw new CliError(`Transition '${transition}' is not available — currently available: ${pending.transitions.join(', ')}.`);
34
+ }
35
+ const payload = buildPayload(options);
36
+ await client.processor.run(pending.workflowId, {
37
+ transition: { id: transition, workflowId: pending.workflowId, payload },
38
+ });
39
+ if (json) {
40
+ printData(JSON.stringify({ runId, promptWorkflowId: pending.workflowId, transition, payload, submitted: true }, null, 2));
41
+ }
42
+ else {
43
+ printStatus(`${pc.green('✓')} answered '${transition}' on ${pending.workflowName}`);
44
+ printStatus(pc.dim(` follow with: loopstack attach ${runId}`));
45
+ }
46
+ process.exit(ExitCode.Success);
47
+ });
48
+ }
49
+ function buildPayload(options) {
50
+ if (options.payload !== undefined && options.arg.length > 0) {
51
+ throw new CliError('Use either --payload or --arg, not both.');
52
+ }
53
+ if (options.payload !== undefined) {
54
+ const raw = options.payload === '@-'
55
+ ? readFileSync(0, 'utf8')
56
+ : options.payload.startsWith('@')
57
+ ? readPayloadFile(options.payload.slice(1))
58
+ : options.payload;
59
+ try {
60
+ const parsed = JSON.parse(raw);
61
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
62
+ throw new CliError('--payload must be a JSON object.');
63
+ }
64
+ return parsed;
65
+ }
66
+ catch (error) {
67
+ if (error instanceof CliError)
68
+ throw error;
69
+ throw new CliError(`--payload is not valid JSON: ${raw.slice(0, 120)}`);
70
+ }
71
+ }
72
+ return parseRunArgs(options.arg);
73
+ }
74
+ function readPayloadFile(path) {
75
+ try {
76
+ return readFileSync(path, 'utf8');
77
+ }
78
+ catch {
79
+ throw new CliError(`Cannot read --payload file "${path}".`);
80
+ }
81
+ }
82
+ //# sourceMappingURL=answer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"answer.js","sourceRoot":"","sources":["../../src/commands/answer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerAttachCommand(program: Command): void;
3
+ //# sourceMappingURL=attach.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attach.d.ts","sourceRoot":"","sources":["../../src/commands/attach.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+BzC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAyG5D"}
@@ -0,0 +1,103 @@
1
+ import pc from 'picocolors';
2
+ import { WorkflowState } from '@loopstack/contracts/enums';
3
+ import { createClientFor, resolveConnection } from '../config/resolve.js';
4
+ import { ExitCode } from '../errors.js';
5
+ import { createIdleHandler } from '../hitl/idle-handler.js';
6
+ import { printData, printStatus, renderResult } from '../output/format.js';
7
+ import { openInBrowser, studioRunUrl } from '../output/studio-link.js';
8
+ import { followRun } from '../run/follow.js';
9
+ import { offerRetry } from '../run/retry.js';
10
+ import { renderRunTrail } from '../run/trail.js';
11
+ const TERMINAL_STATES = [
12
+ WorkflowState.Completed,
13
+ WorkflowState.Failed,
14
+ WorkflowState.Canceled,
15
+ ];
16
+ const IDLE_STATES = [WorkflowState.Waiting, WorkflowState.Paused];
17
+ function exitCodeForStatus(status) {
18
+ if (status === WorkflowState.Completed)
19
+ return ExitCode.Success;
20
+ if (IDLE_STATES.includes(status))
21
+ return ExitCode.NeedsInput;
22
+ return ExitCode.RunFailed;
23
+ }
24
+ export function registerAttachCommand(program) {
25
+ program
26
+ .command('attach <runId>')
27
+ .description('Attach to a run: transcript so far, then live progress and prompts (exit 0 completed / 1 failed / 3 waiting)')
28
+ .option('--open', 'open the run in Studio')
29
+ .action(async (runId, options, cmd) => {
30
+ const globals = cmd.optsWithGlobals();
31
+ const connection = resolveConnection(globals);
32
+ const client = createClientFor(connection);
33
+ const json = !!globals.json;
34
+ const out = json ? process.stderr : process.stdout;
35
+ const link = studioRunUrl(connection, runId);
36
+ if (options.open) {
37
+ if (link)
38
+ openInBrowser(link);
39
+ else
40
+ printStatus('No Studio URL configured for this environment — set one with `loopstack login`.');
41
+ }
42
+ const events = client.stream.events();
43
+ await client.stream.waitForOpen();
44
+ const workflow = await client.workflows.get(runId);
45
+ const checkpoints = await client.workflows.checkpoints(runId);
46
+ const { renderer, streamedMessageIds, streamedToolCallIds, visibleWorkflowIds } = await renderRunTrail(client, connection, out, workflow, checkpoints);
47
+ if (TERMINAL_STATES.includes(workflow.status)) {
48
+ if (json) {
49
+ printData(JSON.stringify({
50
+ workflowId: runId,
51
+ status: workflow.status,
52
+ result: workflow.result,
53
+ errorMessage: workflow.errorMessage,
54
+ }, null, 2));
55
+ }
56
+ else {
57
+ out.write('\n');
58
+ if (renderResult(out, workflow.result))
59
+ out.write('\n');
60
+ }
61
+ out.write(pc.dim(`run already ${workflow.status} — nothing to attach to\n`));
62
+ if (workflow.status !== WorkflowState.Completed && workflow.errorMessage) {
63
+ out.write(`${pc.red('■')} ${workflow.errorMessage}\n`);
64
+ }
65
+ client.stream.close();
66
+ process.exit(exitCodeForStatus(workflow.status));
67
+ }
68
+ out.write(pc.dim('\n— attached, following live —\n'));
69
+ const followOptions = {
70
+ onIdle: createIdleHandler(client, runId, out, { studioUrl: link }),
71
+ initiallyIdle: IDLE_STATES.includes(workflow.status),
72
+ onDocument: renderer.onDocument,
73
+ streamedMessageIds,
74
+ streamedToolCallIds,
75
+ visibleWorkflowIds,
76
+ onStreamReset: async () => (await client.workflows.get(runId)).status,
77
+ };
78
+ let outcome = await followRun(events, runId, out, followOptions);
79
+ if (!json)
80
+ outcome = await offerRetry(client, runId, events, out, out, followOptions, outcome);
81
+ client.stream.close();
82
+ const full = await client.workflows.get(runId);
83
+ if (json) {
84
+ printData(JSON.stringify({ workflowId: runId, status: outcome.status, result: full.result, errorMessage: full.errorMessage }, null, 2));
85
+ }
86
+ if (outcome.status === WorkflowState.Completed) {
87
+ if (!json) {
88
+ out.write('\n');
89
+ if (renderResult(out, full.result))
90
+ out.write('\n');
91
+ }
92
+ out.write(`${pc.green('■')} run completed\n`);
93
+ }
94
+ else if (IDLE_STATES.includes(outcome.status)) {
95
+ out.write(`${pc.yellow('⏸')} run is still waiting for input\n`);
96
+ }
97
+ else {
98
+ out.write(`${pc.red('■')} run ${outcome.status}${full.errorMessage ? `: ${full.errorMessage}` : ''}\n`);
99
+ }
100
+ process.exit(exitCodeForStatus(outcome.status));
101
+ });
102
+ }
103
+ //# sourceMappingURL=attach.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attach.js","sourceRoot":"","sources":["../../src/commands/attach.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA2BzC,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgGzD"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA4BzC,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA4HzD"}
@@ -9,6 +9,7 @@ import { openInBrowser, studioRunUrl } from '../output/studio-link.js';
9
9
  import { parseRunArgs } from '../run/args.js';
10
10
  import { createDocumentRenderer } from '../run/documents.js';
11
11
  import { followRun } from '../run/follow.js';
12
+ import { offerRetry } from '../run/retry.js';
12
13
  import { resolveWorkspaceId } from '../run/workspace.js';
13
14
  const collect = (value, previous) => [...previous, value];
14
15
  const discard = () => new Writable({ write: (_chunk, _encoding, callback) => callback() });
@@ -19,22 +20,29 @@ export function registerRunCommand(program) {
19
20
  .option('--arg <key=value>', 'workflow argument, repeatable; key=@file.json reads a file, key=@- reads stdin', collect, [])
20
21
  .option('--workspace <id>', 'workspace to run in (default: newest workspace of the workflow’s app)')
21
22
  .option('--detach', 'start the run, print its id, exit immediately')
23
+ .option('--trace', 'persist the run trace (tool calls, transitions) — enables `runs <id> --record`')
22
24
  .option('--quiet', 'no progress — print only the final result')
23
25
  .option('--open', 'open the run in Studio')
24
- .option('--editor', 'answer field forms in $EDITOR instead of handing off to Studio')
25
26
  .action(async (workflowName, options, cmd) => {
26
27
  const globals = cmd.optsWithGlobals();
27
28
  const connection = resolveConnection(globals);
28
29
  const client = createClientFor(connection);
29
30
  const json = !!globals.json;
30
31
  const quiet = !!options.quiet;
31
- const out = json ? process.stderr : quiet ? discard() : process.stdout;
32
+ const out = json ? process.stderr : quiet ? discard() : options.detach ? process.stderr : process.stdout;
32
33
  const statusOut = json || quiet ? process.stderr : process.stdout;
33
34
  const args = parseRunArgs(options.arg);
34
35
  const workspaceId = await resolveWorkspaceId(client, workflowName, options.workspace, connection.workspaceId);
35
36
  const events = options.detach ? undefined : client.stream.events();
37
+ if (events)
38
+ await client.stream.waitForOpen();
36
39
  const startedAt = Date.now();
37
- const run = await client.processor.start({ workflowName, workspaceId, args });
40
+ const run = await client.processor.start({
41
+ workflowName,
42
+ workspaceId,
43
+ args,
44
+ ...(options.trace ? { trace: true } : {}),
45
+ });
38
46
  out.write(pc.dim(`▸ run ${run.workflowId} started\n`));
39
47
  const link = studioRunUrl(connection, run.workflowId);
40
48
  if (link && events)
@@ -48,14 +56,30 @@ export function registerRunCommand(program) {
48
56
  if (!events) {
49
57
  if (link)
50
58
  printStatus(pc.dim(`⧉ ${link}`));
51
- printStatus(pc.dim(`resume with: loopstack runs ${run.workflowId} --follow`));
59
+ printStatus(pc.dim(`attach with: loopstack attach ${run.workflowId}`));
52
60
  printData(json ? JSON.stringify({ workflowId: run.workflowId, ...(link && { studioUrl: link }) }) : run.workflowId);
53
61
  process.exit(ExitCode.Success);
54
62
  }
55
- const outcome = await followRun(events, run.workflowId, out, {
56
- onIdle: createIdleHandler(client, run.workflowId, statusOut, { studioUrl: link, editor: options.editor }),
57
- onDocument: createDocumentRenderer(client, out),
63
+ const streamedMessageIds = new Set();
64
+ const streamedToolCallIds = new Set();
65
+ const visibleWorkflowIds = new Set();
66
+ const renderer = createDocumentRenderer(client, out, {
67
+ studioUrl: (id) => studioRunUrl(connection, id),
68
+ streamedMessageIds,
69
+ streamedToolCallIds,
70
+ visibleWorkflowIds,
58
71
  });
72
+ const followOptions = {
73
+ onIdle: createIdleHandler(client, run.workflowId, statusOut, { studioUrl: link }),
74
+ onDocument: renderer.onDocument,
75
+ streamedMessageIds,
76
+ streamedToolCallIds,
77
+ visibleWorkflowIds,
78
+ onStreamReset: async () => (await client.workflows.get(run.workflowId)).status,
79
+ };
80
+ let outcome = await followRun(events, run.workflowId, out, followOptions);
81
+ if (!json)
82
+ outcome = await offerRetry(client, run.workflowId, events, out, statusOut, followOptions, outcome);
59
83
  client.stream.close();
60
84
  const durationMs = Date.now() - startedAt;
61
85
  const full = await client.workflows.get(run.workflowId);
@@ -70,17 +94,18 @@ export function registerRunCommand(program) {
70
94
  }, null, 2));
71
95
  }
72
96
  if (outcome.status === WorkflowState.Completed) {
73
- if (!json && quiet) {
74
- const result = full.result;
75
- printData(typeof result === 'string' ? result : JSON.stringify(result ?? null, null, 2));
97
+ if (!json) {
98
+ if (!quiet)
99
+ out.write('\n');
100
+ const wrote = renderResult(quiet ? process.stdout : out, full.result);
101
+ if (!quiet && wrote)
102
+ out.write('\n');
76
103
  }
77
- if (!json && !quiet)
78
- renderResult(out, full.result);
79
104
  statusOut.write(`${pc.green('■')} run completed in ${formatDuration(durationMs)}\n`);
80
105
  process.exit(ExitCode.Success);
81
106
  }
82
107
  if (outcome.status === WorkflowState.Waiting || outcome.status === WorkflowState.Paused) {
83
- statusOut.write(`${pc.yellow('⏸')} run is waiting for input — resume with \`loopstack runs ${run.workflowId} --follow\`\n`);
108
+ statusOut.write(`${pc.yellow('⏸')} run is waiting for input — resume with \`loopstack attach ${run.workflowId}\`\n`);
84
109
  if (link)
85
110
  statusOut.write(pc.dim(`⧉ answer in Studio: ${link}\n`));
86
111
  process.exit(ExitCode.NeedsInput);
@@ -1 +1 @@
1
- {"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"runs.d.ts","sourceRoot":"","sources":["../../src/commands/runs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsCzC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAwB1D"}
1
+ {"version":3,"file":"runs.d.ts","sourceRoot":"","sources":["../../src/commands/runs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiCzC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA4B1D"}
@@ -1,44 +1,78 @@
1
+ import { writeFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
1
3
  import pc from 'picocolors';
2
4
  import { SortOrder, WorkflowState } from '@loopstack/contracts/enums';
3
5
  import { createClientFor, resolveConnection } from '../config/resolve.js';
4
6
  import { CliError, ExitCode } from '../errors.js';
5
- import { createIdleHandler } from '../hitl/idle-handler.js';
6
- import { colorStatus, formatDuration, printData, printStatus, renderResult, renderTable } from '../output/format.js';
7
+ import { inspectPendingPrompt } from '../hitl/pending.js';
8
+ import { colorStatus, printData, printStatus, renderResult, renderTable } from '../output/format.js';
7
9
  import { openInBrowser, studioRunUrl } from '../output/studio-link.js';
8
- import { createDocumentRenderer } from '../run/documents.js';
9
- import { followRun } from '../run/follow.js';
10
- const TERMINAL_STATES = [
11
- WorkflowState.Completed,
12
- WorkflowState.Failed,
13
- WorkflowState.Canceled,
14
- ];
10
+ import { renderRunTrail } from '../run/trail.js';
15
11
  const IDLE_STATES = [WorkflowState.Waiting, WorkflowState.Paused];
16
12
  export function registerRunsCommand(program) {
17
13
  program
18
14
  .command('runs [runId]')
19
- .description('Recent runs (runs waiting for input first), or one run’s audit trail; --follow attaches live')
20
- .option('--follow', 'attach to the live stream after printing the trail (requires a run id)')
15
+ .description('Recent runs (runs waiting for input first), or one run’s full transcript; `attach` joins it live')
21
16
  .option('--limit <n>', 'maximum number of runs to list', '20')
22
17
  .option('--workspace <id>', 'filter by workspace id')
23
18
  .option('--search <text>', 'search runs')
24
19
  .option('--status <status>', 'filter by status (e.g. waiting, completed, failed)')
25
20
  .option('--open', 'open the run in Studio (requires a run id)')
26
- .option('--editor', 'answer field forms in $EDITOR instead of handing off to Studio')
21
+ .option('--record <file>', 'write the run’s recorded tool calls as a replay fixture (JSON)')
22
+ .option('--tools <names>', 'comma-separated tool names to include in the fixture (default: all)')
27
23
  .action(async (runId, options, cmd) => {
28
24
  const globals = cmd.optsWithGlobals();
29
25
  const connection = resolveConnection(globals);
30
26
  const client = createClientFor(connection);
31
27
  if (!runId) {
32
- if (options.follow)
33
- throw new CliError('--follow requires a run id: loopstack runs <run-id> --follow');
34
28
  if (options.open)
35
29
  throw new CliError('--open requires a run id: loopstack runs <run-id> --open');
30
+ if (options.record)
31
+ throw new CliError('--record requires a run id: loopstack runs <run-id> --record <file>');
36
32
  await listRuns(client, connection, options, !!globals.json);
37
33
  return;
38
34
  }
35
+ if (options.record) {
36
+ await recordFixture(client, runId, options.record, options.tools, !!globals.json);
37
+ return;
38
+ }
39
39
  await showRun(client, connection, runId, options, !!globals.json);
40
40
  });
41
41
  }
42
+ async function recordFixture(client, runId, file, tools, json) {
43
+ const allRecords = await client.workflows.toolCalls(runId);
44
+ if (allRecords.length === 0) {
45
+ throw new CliError('No recorded tool calls for this run. Start the run with `loopstack run <workflow> --trace` ' +
46
+ '(or the backend with LOOPSTACK_TRACE=true) and run the workflow again.');
47
+ }
48
+ const boundary = tools ? new Set(tools.split(',').map((name) => name.trim())) : undefined;
49
+ const inBoundary = boundary ? allRecords.filter((record) => boundary.has(record.toolName)) : allRecords;
50
+ if (boundary && inBoundary.length === 0) {
51
+ throw new CliError(`No recorded calls match --tools ${tools}. Recorded tools: ` +
52
+ `${[...new Set(allRecords.map((record) => record.toolName))].join(', ')}`);
53
+ }
54
+ const records = inBoundary.filter((record) => !record.envelope.pending);
55
+ const recordings = records.map((record) => ({
56
+ tool: record.toolName,
57
+ workflow: record.workflowName,
58
+ transition: record.transitionId ?? '',
59
+ ...(record.args !== null ? { args: record.args } : {}),
60
+ ...(record.config !== null ? { config: record.config } : {}),
61
+ envelope: record.envelope,
62
+ }));
63
+ const target = resolve(file);
64
+ writeFileSync(target, `${JSON.stringify({ version: 3, recordings }, null, 2)}\n`);
65
+ const skipped = inBoundary.length - records.length;
66
+ if (json) {
67
+ printData(JSON.stringify({ file: target, recordings: recordings.length, skippedPending: skipped }, null, 2));
68
+ }
69
+ else {
70
+ printStatus(`${pc.green('✓')} wrote ${recordings.length} recording(s) to ${target}`);
71
+ if (skipped > 0) {
72
+ printStatus(` ${skipped} pending envelope(s) skipped — async tools always run live in replayed tests`);
73
+ }
74
+ }
75
+ }
42
76
  async function listRuns(client, connection, options, json) {
43
77
  const filter = {
44
78
  ...(options.workspace && { workspaceId: options.workspace }),
@@ -77,12 +111,11 @@ async function listRuns(client, connection, options, json) {
77
111
  printData(renderTable(['ID', 'WORKFLOW', 'RUN', 'STATUS', 'TITLE', 'CREATED'], rows));
78
112
  printStatus('');
79
113
  if (needsInput.length > 0) {
80
- printStatus(`${pc.yellow('⏸')} ${needsInput.length} waiting for input — answer with \`loopstack runs <run-id> --follow\``);
114
+ printStatus(`${pc.yellow('⏸')} ${needsInput.length} waiting for input — answer with \`loopstack attach <run-id>\``);
81
115
  }
82
116
  printStatus(`${ordered.length} of ${page.total} runs (${connection.name})`);
83
117
  }
84
118
  async function showRun(client, connection, runId, options, json) {
85
- const out = json ? process.stderr : process.stdout;
86
119
  const link = studioRunUrl(connection, runId);
87
120
  if (options.open) {
88
121
  if (link)
@@ -90,64 +123,29 @@ async function showRun(client, connection, runId, options, json) {
90
123
  else
91
124
  printStatus('No Studio URL configured for this environment — set one with `loopstack login`.');
92
125
  }
93
- const events = options.follow ? client.stream.events() : undefined;
94
126
  const workflow = await client.workflows.get(runId);
95
127
  const checkpoints = await client.workflows.checkpoints(runId);
96
- if (json && !options.follow) {
97
- printData(JSON.stringify({ workflow, checkpoints, ...(link && { studioUrl: link }) }, null, 2));
98
- process.exit(ExitCode.Success);
99
- }
100
- const title = workflow.title ? ` (${workflow.title})` : '';
101
- out.write(`${pc.bold(workflow.workflowName)} #${workflow.run}${title} — ${colorStatus(workflow.status)}\n`);
102
- out.write(pc.dim(`started ${new Date(workflow.createdAt).toLocaleString()} ${workflow.id}\n`));
103
- if (link)
104
- out.write(pc.dim(`⧉ ${link}\n`));
105
- const steps = [];
106
- for (const checkpoint of checkpoints) {
107
- const at = new Date(checkpoint.createdAt).getTime();
108
- const last = steps[steps.length - 1];
109
- if (last && last.place === checkpoint.place)
110
- continue;
111
- if (last)
112
- last.leftAt = at;
113
- steps.push({ place: checkpoint.place, enteredAt: at });
114
- }
115
- for (const step of steps) {
116
- if (step.place === 'end')
117
- continue;
118
- const duration = step.leftAt !== undefined ? pc.dim(` (${formatDuration(step.leftAt - step.enteredAt)})`) : '';
119
- out.write(`${pc.green('✓')} ${step.place}${duration}\n`);
120
- }
121
- if (workflow.errorMessage)
122
- out.write(`${pc.red('✖')} ${workflow.errorMessage}\n`);
123
- if (!options.follow || TERMINAL_STATES.includes(workflow.status)) {
124
- renderResult(out, workflow.result);
125
- client.stream.close();
126
- process.exit(ExitCode.Success);
127
- }
128
- out.write(pc.dim('— attached, following live —\n'));
129
- const onIdle = createIdleHandler(client, runId, out, { studioUrl: link, editor: options.editor });
130
- const outcome = await followRun(events, runId, out, {
131
- onIdle,
132
- initiallyIdle: IDLE_STATES.includes(workflow.status),
133
- onDocument: createDocumentRenderer(client, out),
134
- });
135
- client.stream.close();
136
- const full = await client.workflows.get(runId);
128
+ const pendingPrompt = IDLE_STATES.includes(workflow.status)
129
+ ? await inspectPendingPrompt(client, runId).catch(() => undefined)
130
+ : undefined;
137
131
  if (json) {
138
- printData(JSON.stringify({ workflowId: runId, status: outcome.status, result: full.result, errorMessage: full.errorMessage }, null, 2));
139
- }
140
- if (outcome.status === WorkflowState.Completed) {
141
- if (!json)
142
- renderResult(out, full.result);
143
- out.write(`${pc.green('■')} run completed\n`);
132
+ printData(JSON.stringify({ workflow, checkpoints, ...(pendingPrompt && { pendingPrompt }), ...(link && { studioUrl: link }) }, null, 2));
144
133
  process.exit(ExitCode.Success);
145
134
  }
146
- if (IDLE_STATES.includes(outcome.status)) {
147
- out.write(`${pc.yellow('⏸')} run is still waiting for input\n`);
148
- process.exit(ExitCode.NeedsInput);
135
+ const out = process.stdout;
136
+ await renderRunTrail(client, connection, out, workflow, checkpoints);
137
+ out.write('\n');
138
+ if (renderResult(out, workflow.result))
139
+ out.write('\n');
140
+ if (IDLE_STATES.includes(workflow.status)) {
141
+ if (pendingPrompt && !pendingPrompt.studioOnly) {
142
+ out.write(`${pc.yellow('⏸')} waiting for input: ${pendingPrompt.description}\n`);
143
+ out.write(pc.dim(` answer with \`loopstack answer ${runId} --arg k=v\` or interactively: \`loopstack attach ${runId}\`\n`));
144
+ }
145
+ else {
146
+ out.write(`${pc.yellow('⏸')} run is waiting for input — answer with \`loopstack attach ${runId}\`\n`);
147
+ }
149
148
  }
150
- out.write(`${pc.red('■')} run ${outcome.status}${full.errorMessage ? `: ${full.errorMessage}` : ''}\n`);
151
- process.exit(ExitCode.RunFailed);
149
+ process.exit(ExitCode.Success);
152
150
  }
153
151
  //# sourceMappingURL=runs.js.map