keystone-cli 0.8.0 → 1.0.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.
- package/README.md +486 -54
- package/package.json +8 -2
- package/src/__fixtures__/index.ts +100 -0
- package/src/cli.ts +809 -90
- package/src/db/memory-db.ts +35 -1
- package/src/db/workflow-db.test.ts +24 -0
- package/src/db/workflow-db.ts +469 -14
- package/src/expression/evaluator.ts +68 -4
- package/src/parser/agent-parser.ts +6 -3
- package/src/parser/config-schema.ts +38 -2
- package/src/parser/schema.ts +192 -7
- package/src/parser/test-schema.ts +29 -0
- package/src/parser/workflow-parser.test.ts +54 -0
- package/src/parser/workflow-parser.ts +153 -7
- package/src/runner/aggregate-error.test.ts +57 -0
- package/src/runner/aggregate-error.ts +46 -0
- package/src/runner/audit-verification.test.ts +2 -2
- package/src/runner/auto-heal.test.ts +1 -1
- package/src/runner/blueprint-executor.test.ts +63 -0
- package/src/runner/blueprint-executor.ts +157 -0
- package/src/runner/concurrency-limit.test.ts +82 -0
- package/src/runner/debug-repl.ts +18 -3
- package/src/runner/durable-timers.test.ts +200 -0
- package/src/runner/engine-executor.test.ts +464 -0
- package/src/runner/engine-executor.ts +491 -0
- package/src/runner/foreach-executor.ts +30 -12
- package/src/runner/llm-adapter.test.ts +282 -5
- package/src/runner/llm-adapter.ts +581 -8
- package/src/runner/llm-clarification.test.ts +79 -21
- package/src/runner/llm-errors.ts +83 -0
- package/src/runner/llm-executor.test.ts +258 -219
- package/src/runner/llm-executor.ts +226 -29
- package/src/runner/mcp-client.ts +70 -3
- package/src/runner/mcp-manager.test.ts +52 -52
- package/src/runner/mcp-manager.ts +12 -5
- package/src/runner/mcp-server.test.ts +117 -78
- package/src/runner/mcp-server.ts +13 -4
- package/src/runner/optimization-runner.ts +48 -31
- package/src/runner/reflexion.test.ts +1 -1
- package/src/runner/resource-pool.test.ts +113 -0
- package/src/runner/resource-pool.ts +164 -0
- package/src/runner/shell-executor.ts +130 -32
- package/src/runner/standard-tools-integration.test.ts +36 -36
- package/src/runner/standard-tools.test.ts +18 -0
- package/src/runner/standard-tools.ts +110 -37
- package/src/runner/step-executor.test.ts +176 -16
- package/src/runner/step-executor.ts +530 -86
- package/src/runner/stream-utils.test.ts +14 -0
- package/src/runner/subflow-outputs.test.ts +103 -0
- package/src/runner/test-harness.ts +161 -0
- package/src/runner/tool-integration.test.ts +73 -79
- package/src/runner/workflow-runner.test.ts +492 -15
- package/src/runner/workflow-runner.ts +1438 -79
- package/src/runner/workflow-subflows.test.ts +255 -0
- package/src/templates/agents/keystone-architect.md +17 -12
- package/src/templates/agents/tester.md +21 -0
- package/src/templates/child-rollback.yaml +11 -0
- package/src/templates/decompose-implement.yaml +53 -0
- package/src/templates/decompose-problem.yaml +159 -0
- package/src/templates/decompose-research.yaml +52 -0
- package/src/templates/decompose-review.yaml +51 -0
- package/src/templates/dev.yaml +134 -0
- package/src/templates/engine-example.yaml +33 -0
- package/src/templates/fan-out-fan-in.yaml +61 -0
- package/src/templates/memory-service.yaml +1 -1
- package/src/templates/parent-rollback.yaml +16 -0
- package/src/templates/robust-automation.yaml +1 -1
- package/src/templates/scaffold-feature.yaml +29 -27
- package/src/templates/scaffold-generate.yaml +41 -0
- package/src/templates/scaffold-plan.yaml +53 -0
- package/src/types/status.ts +3 -0
- package/src/ui/dashboard.tsx +4 -3
- package/src/utils/assets.macro.ts +36 -0
- package/src/utils/auth-manager.ts +585 -8
- package/src/utils/blueprint-utils.test.ts +49 -0
- package/src/utils/blueprint-utils.ts +80 -0
- package/src/utils/circuit-breaker.test.ts +177 -0
- package/src/utils/circuit-breaker.ts +160 -0
- package/src/utils/config-loader.test.ts +100 -13
- package/src/utils/config-loader.ts +44 -17
- package/src/utils/constants.ts +62 -0
- package/src/utils/error-renderer.test.ts +267 -0
- package/src/utils/error-renderer.ts +320 -0
- package/src/utils/json-parser.test.ts +4 -0
- package/src/utils/json-parser.ts +18 -1
- package/src/utils/mermaid.ts +4 -0
- package/src/utils/paths.test.ts +46 -0
- package/src/utils/paths.ts +70 -0
- package/src/utils/process-sandbox.test.ts +128 -0
- package/src/utils/process-sandbox.ts +293 -0
- package/src/utils/rate-limiter.test.ts +143 -0
- package/src/utils/rate-limiter.ts +221 -0
- package/src/utils/redactor.test.ts +23 -15
- package/src/utils/redactor.ts +65 -25
- package/src/utils/resource-loader.test.ts +54 -0
- package/src/utils/resource-loader.ts +158 -0
- package/src/utils/sandbox.test.ts +69 -4
- package/src/utils/sandbox.ts +69 -6
- package/src/utils/schema-validator.ts +65 -0
- package/src/utils/workflow-registry.test.ts +57 -0
- package/src/utils/workflow-registry.ts +45 -25
- /package/src/expression/{evaluator.audit.test.ts → evaluator-audit.test.ts} +0 -0
- /package/src/runner/{mcp-client.audit.test.ts → mcp-client-audit.test.ts} +0 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
name: dev
|
|
2
|
+
description: "Self-bootstrapping DevMode workflow for Keystone CLI"
|
|
3
|
+
|
|
4
|
+
inputs:
|
|
5
|
+
task:
|
|
6
|
+
type: string
|
|
7
|
+
description: "The development task to perform"
|
|
8
|
+
auto_approve:
|
|
9
|
+
type: boolean
|
|
10
|
+
default: false
|
|
11
|
+
description: "If true, skip the plan approval step"
|
|
12
|
+
|
|
13
|
+
outputs:
|
|
14
|
+
summary: ${{ steps.implement.output.summary }}
|
|
15
|
+
files_changed: ${{ steps.implement.output.files_changed }}
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- id: analyze
|
|
19
|
+
type: llm
|
|
20
|
+
agent: keystone-architect
|
|
21
|
+
useStandardTools: true
|
|
22
|
+
prompt: |
|
|
23
|
+
Analyze the following task and the current project structure.
|
|
24
|
+
Task: ${{ inputs.task }}
|
|
25
|
+
|
|
26
|
+
Identify the relevant files and components that need to be modified or created.
|
|
27
|
+
Provide a high-level summary of your analysis.
|
|
28
|
+
outputSchema:
|
|
29
|
+
type: object
|
|
30
|
+
properties:
|
|
31
|
+
analysis: { type: string }
|
|
32
|
+
affected_files: { type: array, items: { type: string } }
|
|
33
|
+
required: [analysis, affected_files]
|
|
34
|
+
|
|
35
|
+
- id: plan
|
|
36
|
+
type: llm
|
|
37
|
+
agent: keystone-architect
|
|
38
|
+
needs: [analyze]
|
|
39
|
+
prompt: |
|
|
40
|
+
Based on the analysis, create a detailed implementation plan for:
|
|
41
|
+
${{ inputs.task }}
|
|
42
|
+
|
|
43
|
+
Analysis:
|
|
44
|
+
${{ steps.analyze.output.analysis }}
|
|
45
|
+
|
|
46
|
+
Proposed Implementation Strategy:
|
|
47
|
+
1. What changes are needed in which files?
|
|
48
|
+
2. Are there any new files to be created?
|
|
49
|
+
3. What are the potential risks or breaking changes?
|
|
50
|
+
4. How will the changes be verified?
|
|
51
|
+
outputSchema:
|
|
52
|
+
type: object
|
|
53
|
+
properties:
|
|
54
|
+
plan_summary: { type: string }
|
|
55
|
+
detailed_steps: { type: array, items: { type: string } }
|
|
56
|
+
required: [plan_summary, detailed_steps]
|
|
57
|
+
|
|
58
|
+
- id: approve_plan
|
|
59
|
+
type: human
|
|
60
|
+
if: "!${{ inputs.auto_approve }}"
|
|
61
|
+
needs: [plan]
|
|
62
|
+
message: |
|
|
63
|
+
Proposed Plan for: ${{ inputs.task }}
|
|
64
|
+
|
|
65
|
+
Summary:
|
|
66
|
+
${{ steps.plan.output.plan_summary }}
|
|
67
|
+
|
|
68
|
+
Detailed Steps:
|
|
69
|
+
${{ steps.plan.output.detailed_steps.join('\n') }}
|
|
70
|
+
|
|
71
|
+
Do you want to proceed with this plan? (yes/no)
|
|
72
|
+
inputType: confirm
|
|
73
|
+
|
|
74
|
+
- id: implement
|
|
75
|
+
type: llm
|
|
76
|
+
agent: software-engineer
|
|
77
|
+
needs: [approve_plan]
|
|
78
|
+
useStandardTools: true
|
|
79
|
+
allowInsecure: true
|
|
80
|
+
prompt: |
|
|
81
|
+
Implement the following plan for the task:
|
|
82
|
+
${{ inputs.task }}
|
|
83
|
+
|
|
84
|
+
Plan:
|
|
85
|
+
${{ steps.plan.output.plan_summary }}
|
|
86
|
+
|
|
87
|
+
Detailed Steps:
|
|
88
|
+
${{ steps.plan.output.detailed_steps.join('\n') }}
|
|
89
|
+
|
|
90
|
+
Use your tools to modify or create the necessary files.
|
|
91
|
+
outputSchema:
|
|
92
|
+
type: object
|
|
93
|
+
properties:
|
|
94
|
+
summary: { type: string }
|
|
95
|
+
files_changed: { type: array, items: { type: string } }
|
|
96
|
+
required: [summary]
|
|
97
|
+
|
|
98
|
+
- id: verify
|
|
99
|
+
type: llm
|
|
100
|
+
agent: tester
|
|
101
|
+
needs: [implement]
|
|
102
|
+
useStandardTools: true
|
|
103
|
+
allowInsecure: true
|
|
104
|
+
prompt: |
|
|
105
|
+
Verify the changes made for the task:
|
|
106
|
+
${{ inputs.task }}
|
|
107
|
+
|
|
108
|
+
Files changed:
|
|
109
|
+
${{ steps.implement.output.files_changed.join(', ') }}
|
|
110
|
+
|
|
111
|
+
Run relevant tests and perform any necessary checks to ensure the implementation is correct and hasn't introduced regressions.
|
|
112
|
+
outputSchema:
|
|
113
|
+
type: object
|
|
114
|
+
properties:
|
|
115
|
+
test_results: { type: string }
|
|
116
|
+
success: { type: boolean }
|
|
117
|
+
required: [test_results, success]
|
|
118
|
+
|
|
119
|
+
- id: final_review
|
|
120
|
+
type: human
|
|
121
|
+
needs: [verify]
|
|
122
|
+
message: |
|
|
123
|
+
Implementation Complete for: ${{ inputs.task }}
|
|
124
|
+
|
|
125
|
+
Summary of changes:
|
|
126
|
+
${{ steps.implement.output.summary }}
|
|
127
|
+
|
|
128
|
+
Test Results:
|
|
129
|
+
${{ steps.verify.output.test_results }}
|
|
130
|
+
|
|
131
|
+
Status: ${{ steps.verify.output.success ? 'PASSED' : 'FAILED' }}
|
|
132
|
+
|
|
133
|
+
Should we finalize these changes? (yes/no)
|
|
134
|
+
inputType: confirm
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: engine-example
|
|
2
|
+
description: "Run an allowlisted external CLI and capture a structured summary"
|
|
3
|
+
|
|
4
|
+
# Requires an allowlisted engine in .keystone/config.yaml:
|
|
5
|
+
# engines:
|
|
6
|
+
# allowlist:
|
|
7
|
+
# bun:
|
|
8
|
+
# command: bun
|
|
9
|
+
# version: "1.3."
|
|
10
|
+
# versionArgs: ["--version"]
|
|
11
|
+
|
|
12
|
+
inputs:
|
|
13
|
+
message:
|
|
14
|
+
type: string
|
|
15
|
+
default: "Hello engine"
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- id: run_engine
|
|
19
|
+
type: engine
|
|
20
|
+
command: bun
|
|
21
|
+
args:
|
|
22
|
+
- -e
|
|
23
|
+
- 'const fs = require("node:fs"); const input = fs.readFileSync(0, "utf8"); const data = input ? JSON.parse(input) : {}; const summary = { received: data.message || null }; fs.writeFileSync(process.env.KEYSTONE_ENGINE_SUMMARY_PATH, JSON.stringify(summary));'
|
|
24
|
+
input:
|
|
25
|
+
message: ${{ inputs.message }}
|
|
26
|
+
env:
|
|
27
|
+
PATH: ${{ env.PATH }}
|
|
28
|
+
cwd: .
|
|
29
|
+
outputSchema:
|
|
30
|
+
type: object
|
|
31
|
+
properties:
|
|
32
|
+
received: { type: string }
|
|
33
|
+
required: [received]
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
name: fan-out-fan-in-example
|
|
2
|
+
description: Demonstrates dynamic join conditions and nested compensations
|
|
3
|
+
|
|
4
|
+
inputs:
|
|
5
|
+
should_fail:
|
|
6
|
+
type: boolean
|
|
7
|
+
default: false
|
|
8
|
+
|
|
9
|
+
steps:
|
|
10
|
+
- id: prepare
|
|
11
|
+
type: shell
|
|
12
|
+
run: echo "Preparing..."
|
|
13
|
+
compensate:
|
|
14
|
+
id: cleanup_prepare
|
|
15
|
+
type: shell
|
|
16
|
+
run: echo "Cleaning up preparation..."
|
|
17
|
+
|
|
18
|
+
- id: parallel_1
|
|
19
|
+
type: shell
|
|
20
|
+
run: sleep 2 && echo "Parallel 1 done"
|
|
21
|
+
needs: [prepare]
|
|
22
|
+
compensate:
|
|
23
|
+
id: undo_1
|
|
24
|
+
type: shell
|
|
25
|
+
run: echo "Undoing Parallel 1..."
|
|
26
|
+
|
|
27
|
+
- id: parallel_2
|
|
28
|
+
type: shell
|
|
29
|
+
run: |
|
|
30
|
+
echo "Parallel 2 failing intentionally..."
|
|
31
|
+
exit 1
|
|
32
|
+
needs: [prepare]
|
|
33
|
+
compensate:
|
|
34
|
+
id: undo_2
|
|
35
|
+
type: shell
|
|
36
|
+
run: echo "Undoing Parallel 2..."
|
|
37
|
+
|
|
38
|
+
- id: early_join
|
|
39
|
+
type: join
|
|
40
|
+
condition: any
|
|
41
|
+
needs: [parallel_1, parallel_2]
|
|
42
|
+
|
|
43
|
+
- id: after_early_join
|
|
44
|
+
type: shell
|
|
45
|
+
run: echo "One of the parallel steps finished! Proceeding early..."
|
|
46
|
+
needs: [early_join]
|
|
47
|
+
|
|
48
|
+
- id: final_join
|
|
49
|
+
type: join
|
|
50
|
+
condition: all
|
|
51
|
+
needs: [parallel_1, parallel_2]
|
|
52
|
+
|
|
53
|
+
- id: conclude
|
|
54
|
+
type: shell
|
|
55
|
+
run: echo "Both parallel steps finished! Workflow complete."
|
|
56
|
+
needs: [final_join]
|
|
57
|
+
|
|
58
|
+
compensate:
|
|
59
|
+
id: workflow_cleanup
|
|
60
|
+
type: shell
|
|
61
|
+
run: echo "Performing final top-level workflow cleanup..."
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
name: nested-rollback-parent
|
|
2
|
+
description: Parent workflow that triggers a child and then fails
|
|
3
|
+
|
|
4
|
+
steps:
|
|
5
|
+
- id: run_child
|
|
6
|
+
type: workflow
|
|
7
|
+
path: ./child-rollback.yaml
|
|
8
|
+
compensate:
|
|
9
|
+
id: parent_post_child_undo
|
|
10
|
+
type: shell
|
|
11
|
+
run: echo "Parent cleanup after child rollback..."
|
|
12
|
+
|
|
13
|
+
- id: fail_step
|
|
14
|
+
type: shell
|
|
15
|
+
run: exit 1
|
|
16
|
+
needs: [run_child]
|
|
@@ -7,37 +7,39 @@ steps:
|
|
|
7
7
|
message: "Describe the workflow you want to build:"
|
|
8
8
|
inputType: text
|
|
9
9
|
|
|
10
|
-
- id:
|
|
11
|
-
type:
|
|
12
|
-
agent: keystone-architect
|
|
10
|
+
- id: blueprint
|
|
11
|
+
type: blueprint
|
|
13
12
|
needs: [get_requirements]
|
|
14
|
-
|
|
15
|
-
useStandardTools: true
|
|
13
|
+
agent: keystone-architect
|
|
16
14
|
prompt: |
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
15
|
+
Generate a system blueprint for the following requirements:
|
|
16
|
+
${{ steps.get_requirements.output }}
|
|
17
|
+
|
|
18
|
+
- id: plan
|
|
19
|
+
type: workflow
|
|
20
|
+
needs: [blueprint]
|
|
21
|
+
path: scaffold-plan
|
|
22
|
+
inputs:
|
|
23
|
+
requirements: ${{ steps.get_requirements.output }}
|
|
24
|
+
blueprint: ${{ steps.blueprint.output }}
|
|
25
|
+
outputMapping:
|
|
26
|
+
files: files
|
|
27
|
+
|
|
28
|
+
- id: generate
|
|
29
|
+
type: workflow
|
|
30
|
+
needs: [blueprint, plan]
|
|
31
|
+
path: scaffold-generate
|
|
32
|
+
inputs:
|
|
33
|
+
requirements: ${{ steps.get_requirements.output }}
|
|
34
|
+
blueprint: ${{ steps.blueprint.output }}
|
|
35
|
+
files: ${{ steps.plan.outputs.files }}
|
|
36
|
+
outputMapping:
|
|
37
|
+
files: files
|
|
36
38
|
|
|
37
39
|
- id: write_files
|
|
38
40
|
type: file
|
|
39
|
-
needs: [
|
|
40
|
-
foreach: ${{ steps.
|
|
41
|
+
needs: [generate]
|
|
42
|
+
foreach: ${{ steps.generate.outputs.files }}
|
|
41
43
|
op: write
|
|
42
44
|
path: ${{ item.path }}
|
|
43
45
|
content: ${{ item.content }}
|
|
@@ -47,4 +49,4 @@ steps:
|
|
|
47
49
|
needs: [write_files]
|
|
48
50
|
run: |
|
|
49
51
|
echo "Scaffolding complete. Files created:"
|
|
50
|
-
echo "${{ steps.
|
|
52
|
+
echo "${{ steps.generate.outputs && steps.generate.outputs.files ? steps.generate.outputs.files.map(f => f.path).join('\n') : '(dry run: no files generated)' }}"
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: scaffold-generate
|
|
2
|
+
description: "Generate file contents from a file plan"
|
|
3
|
+
|
|
4
|
+
inputs:
|
|
5
|
+
requirements: { type: string }
|
|
6
|
+
blueprint: { type: object }
|
|
7
|
+
files: { type: array }
|
|
8
|
+
|
|
9
|
+
outputs:
|
|
10
|
+
files: ${{ steps.generate.output }}
|
|
11
|
+
|
|
12
|
+
steps:
|
|
13
|
+
- id: generate
|
|
14
|
+
type: llm
|
|
15
|
+
agent: software-engineer
|
|
16
|
+
allowClarification: true
|
|
17
|
+
useStandardTools: true
|
|
18
|
+
foreach: ${{ inputs.files }}
|
|
19
|
+
prompt: |
|
|
20
|
+
You are generating the content for a single file based on a system blueprint and a specific file spec.
|
|
21
|
+
|
|
22
|
+
<system_blueprint>
|
|
23
|
+
${{ inputs.blueprint }}
|
|
24
|
+
</system_blueprint>
|
|
25
|
+
|
|
26
|
+
Overall user requirements:
|
|
27
|
+
${{ inputs.requirements }}
|
|
28
|
+
|
|
29
|
+
File spec for this file:
|
|
30
|
+
${{ item }}
|
|
31
|
+
|
|
32
|
+
Produce the full file content for the given path. Ensure it aligns with the architectural constraints and dependencies defined in the blueprint.
|
|
33
|
+
Return only JSON with fields: path, content.
|
|
34
|
+
outputSchema:
|
|
35
|
+
type: object
|
|
36
|
+
properties:
|
|
37
|
+
path:
|
|
38
|
+
type: string
|
|
39
|
+
content:
|
|
40
|
+
type: string
|
|
41
|
+
required: [path, content]
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
name: scaffold-plan
|
|
2
|
+
description: "Plan the files for a new workflow and optional agents"
|
|
3
|
+
|
|
4
|
+
inputs:
|
|
5
|
+
requirements: { type: string }
|
|
6
|
+
blueprint: { type: object }
|
|
7
|
+
|
|
8
|
+
outputs:
|
|
9
|
+
files: ${{ steps.plan.output.files }}
|
|
10
|
+
|
|
11
|
+
steps:
|
|
12
|
+
- id: plan
|
|
13
|
+
type: llm
|
|
14
|
+
agent: keystone-architect
|
|
15
|
+
allowClarification: true
|
|
16
|
+
useStandardTools: true
|
|
17
|
+
prompt: |
|
|
18
|
+
You are planning a file structure based on a system blueprint.
|
|
19
|
+
|
|
20
|
+
<system_blueprint>
|
|
21
|
+
${{ inputs.blueprint }}
|
|
22
|
+
</system_blueprint>
|
|
23
|
+
|
|
24
|
+
User requirements:
|
|
25
|
+
${{ inputs.requirements }}
|
|
26
|
+
|
|
27
|
+
Create a file plan. Follow the architecture and file list provided in the blueprint.
|
|
28
|
+
Use paths under:
|
|
29
|
+
- .keystone/workflows/ for workflows
|
|
30
|
+
- .keystone/workflows/agents/ for agents
|
|
31
|
+
|
|
32
|
+
Each file should include:
|
|
33
|
+
- path: relative path
|
|
34
|
+
- purpose: short purpose statement
|
|
35
|
+
- notes: optional implementation guidance
|
|
36
|
+
|
|
37
|
+
Return only the structured JSON required by the schema.
|
|
38
|
+
outputSchema:
|
|
39
|
+
type: object
|
|
40
|
+
properties:
|
|
41
|
+
files:
|
|
42
|
+
type: array
|
|
43
|
+
items:
|
|
44
|
+
type: object
|
|
45
|
+
properties:
|
|
46
|
+
path:
|
|
47
|
+
type: string
|
|
48
|
+
purpose:
|
|
49
|
+
type: string
|
|
50
|
+
notes:
|
|
51
|
+
type: string
|
|
52
|
+
required: [path, purpose]
|
|
53
|
+
required: [files]
|
package/src/types/status.ts
CHANGED
|
@@ -10,6 +10,8 @@ export const StepStatus = {
|
|
|
10
10
|
SUSPENDED: 'suspended',
|
|
11
11
|
SKIPPED: 'skipped',
|
|
12
12
|
RUNNING: 'running',
|
|
13
|
+
CANCELED: 'canceled',
|
|
14
|
+
WAITING: 'waiting', // Waiting on durable timer
|
|
13
15
|
} as const;
|
|
14
16
|
|
|
15
17
|
export type StepStatusType = (typeof StepStatus)[keyof typeof StepStatus];
|
|
@@ -20,6 +22,7 @@ export const WorkflowStatus = {
|
|
|
20
22
|
PAUSED: 'paused',
|
|
21
23
|
SUSPENDED: 'suspended',
|
|
22
24
|
RUNNING: 'running',
|
|
25
|
+
CANCELED: 'canceled',
|
|
23
26
|
} as const;
|
|
24
27
|
|
|
25
28
|
export type WorkflowStatusType = (typeof WorkflowStatus)[keyof typeof WorkflowStatus];
|
package/src/ui/dashboard.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Box, Newline, Text, render, useInput } from 'ink';
|
|
2
2
|
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
|
3
3
|
import { WorkflowDb } from '../db/workflow-db.ts';
|
|
4
|
+
import { ConsoleLogger } from '../utils/logger.ts';
|
|
4
5
|
|
|
5
6
|
interface Run {
|
|
6
7
|
id: string;
|
|
@@ -10,6 +11,8 @@ interface Run {
|
|
|
10
11
|
total_tokens?: number;
|
|
11
12
|
}
|
|
12
13
|
|
|
14
|
+
const logger = new ConsoleLogger();
|
|
15
|
+
|
|
13
16
|
const Dashboard = () => {
|
|
14
17
|
const [runs, setRuns] = useState<Run[]>([]);
|
|
15
18
|
const [loading, setLoading] = useState(true);
|
|
@@ -50,9 +53,7 @@ const Dashboard = () => {
|
|
|
50
53
|
);
|
|
51
54
|
setRuns(runsWithUsage);
|
|
52
55
|
} catch (error) {
|
|
53
|
-
|
|
54
|
-
// For now we keep it as is or could use a toast.
|
|
55
|
-
console.error('Failed to fetch runs:', error);
|
|
56
|
+
logger.error(`Failed to fetch runs: ${String(error)}`);
|
|
56
57
|
} finally {
|
|
57
58
|
setLoading(false);
|
|
58
59
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
2
|
+
import { join, relative } from 'node:path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Bun macro to read all files in a directory and return them as a map.
|
|
6
|
+
* This runs at build-time.
|
|
7
|
+
*/
|
|
8
|
+
export function bundleAssets(): Record<string, string> {
|
|
9
|
+
const dirPath = process.env.ASSETS_DIR || '.keystone';
|
|
10
|
+
const assets: Record<string, string> = {};
|
|
11
|
+
|
|
12
|
+
if (!existsSync(dirPath)) {
|
|
13
|
+
// If directory doesn't exist, return empty (e.g. during dev of CLI itself)
|
|
14
|
+
return {};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function walk(currentDir: string) {
|
|
18
|
+
const files = readdirSync(currentDir);
|
|
19
|
+
for (const file of files) {
|
|
20
|
+
const fullPath = join(currentDir, file);
|
|
21
|
+
const stats = statSync(fullPath);
|
|
22
|
+
|
|
23
|
+
if (stats.isDirectory()) {
|
|
24
|
+
walk(fullPath);
|
|
25
|
+
} else if (stats.isFile()) {
|
|
26
|
+
// Use relative path as key
|
|
27
|
+
const relPath = relative(dirPath, fullPath);
|
|
28
|
+
assets[relPath] = readFileSync(fullPath, 'utf8');
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
walk(dirPath);
|
|
34
|
+
|
|
35
|
+
return assets;
|
|
36
|
+
}
|