@osfactory/har 0.1.1
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/LICENSE +674 -0
- package/README.md +208 -0
- package/control/docker-compose.build.yml +7 -0
- package/control/docker-compose.yml +31 -0
- package/dist/index.js +41753 -0
- package/dist/prompts/system-authoring.md +104 -0
- package/dist/templates/AGENT.md.template +40 -0
- package/dist/templates/CLAUDE.md.template +5 -0
- package/dist/templates/adaptation-prompt-init.md +124 -0
- package/dist/templates/adaptation-prompt-maintain.md +73 -0
- package/dist/templates/cursor-rule.mdc.template +101 -0
- package/dist/templates/har-boilerplate/CLAUDE.agent.md +54 -0
- package/dist/templates/har-boilerplate/README.md +134 -0
- package/dist/templates/har-boilerplate/agent-cli.sh +172 -0
- package/dist/templates/har-boilerplate/agent-slot.sh +270 -0
- package/dist/templates/har-boilerplate/attach.sh +16 -0
- package/dist/templates/har-boilerplate/docker-compose.agent.yml +66 -0
- package/dist/templates/har-boilerplate/ecosystem.agent.template.cjs +44 -0
- package/dist/templates/har-boilerplate/env.template +37 -0
- package/dist/templates/har-boilerplate/harness.env +50 -0
- package/dist/templates/har-boilerplate/justfile +39 -0
- package/dist/templates/har-boilerplate/launch.sh +229 -0
- package/dist/templates/har-boilerplate/setup-infra.sh +102 -0
- package/dist/templates/har-boilerplate/stages/README.sh +9 -0
- package/dist/templates/har-boilerplate/stages.json +67 -0
- package/dist/templates/har-boilerplate/teardown.sh +86 -0
- package/dist/templates/har-boilerplate/verify.sh +163 -0
- package/dist/templates/har-boilerplate-cli/CLAUDE.agent.md +41 -0
- package/dist/templates/har-boilerplate-cli/README.md +112 -0
- package/dist/templates/har-boilerplate-cli/agent-cli.sh +162 -0
- package/dist/templates/har-boilerplate-cli/agent-slot.sh +270 -0
- package/dist/templates/har-boilerplate-cli/docker-compose.agent.yml +64 -0
- package/dist/templates/har-boilerplate-cli/harness.env +40 -0
- package/dist/templates/har-boilerplate-cli/justfile +27 -0
- package/dist/templates/har-boilerplate-cli/launch.sh +132 -0
- package/dist/templates/har-boilerplate-cli/setup-infra.sh +102 -0
- package/dist/templates/har-boilerplate-cli/stages/README.sh +9 -0
- package/dist/templates/har-boilerplate-cli/stages.json +61 -0
- package/dist/templates/har-boilerplate-cli/teardown.sh +66 -0
- package/dist/templates/har-boilerplate-cli/verify.sh +113 -0
- package/dist/templates/har-boilerplate-ios/ADAPT-PROMPT.md +57 -0
- package/dist/templates/har-boilerplate-ios/CLAUDE.agent.md +65 -0
- package/dist/templates/har-boilerplate-ios/README.md +88 -0
- package/dist/templates/har-boilerplate-ios/agent-cli.sh +125 -0
- package/dist/templates/har-boilerplate-ios/agent-slot.sh +270 -0
- package/dist/templates/har-boilerplate-ios/docker-compose.agent.yml +17 -0
- package/dist/templates/har-boilerplate-ios/harness.env +42 -0
- package/dist/templates/har-boilerplate-ios/justfile +33 -0
- package/dist/templates/har-boilerplate-ios/launch.sh +132 -0
- package/dist/templates/har-boilerplate-ios/setup-infra.sh +84 -0
- package/dist/templates/har-boilerplate-ios/stages/README.sh +12 -0
- package/dist/templates/har-boilerplate-ios/stages.json +61 -0
- package/dist/templates/har-boilerplate-ios/teardown.sh +64 -0
- package/dist/templates/har-boilerplate-ios/verify.sh +165 -0
- package/dist/templates/stage-templates/playwright/.github/workflows/playwright.yml +30 -0
- package/dist/templates/stage-templates/playwright/.har/stages/PLAYWRIGHT.md +21 -0
- package/dist/templates/stage-templates/playwright/.har/stages/browser-e2e.sh +83 -0
- package/dist/templates/stage-templates/playwright/README.md +14 -0
- package/dist/templates/stage-templates/playwright/package.fragment.json +14 -0
- package/dist/templates/stage-templates/playwright/playwright.config.js +30 -0
- package/dist/templates/stage-templates/playwright/template.manifest.json +44 -0
- package/dist/templates/stage-templates/playwright/tests/a11y/smoke.spec.js +14 -0
- package/dist/templates/stage-templates/playwright/tests/api/health.spec.js +10 -0
- package/dist/templates/stage-templates/playwright/tests/frontend/smoke.spec.js +9 -0
- package/dist/templates/stage-templates/rocketsim/.har/stages/ROCKETSIM.md +126 -0
- package/dist/templates/stage-templates/rocketsim/.har/stages/rocketsim-flows.sh +188 -0
- package/dist/templates/stage-templates/rocketsim/README.md +66 -0
- package/dist/templates/stage-templates/rocketsim/flows/README.md +36 -0
- package/dist/templates/stage-templates/rocketsim/flows/example-smoke.sh +60 -0
- package/dist/templates/stage-templates/rocketsim/template.manifest.json +38 -0
- package/package.json +78 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# RocketSim user-flow runner for an agent slot.
|
|
3
|
+
# Discovers and runs all flow scripts under flows/ (or a specific one from args).
|
|
4
|
+
# Outputs JSON to stdout, human-readable progress to stderr.
|
|
5
|
+
#
|
|
6
|
+
# Usage: ./.har/stages/rocketsim-flows.sh <agent-id> [flow-name]
|
|
7
|
+
# flow-name (optional): run only flows/<flow-name>.sh instead of all flows
|
|
8
|
+
#
|
|
9
|
+
# Prerequisite: ./.har/launch.sh <agent-id> AND app installed+running on simulator
|
|
10
|
+
# See: ./.har/stages/ROCKETSIM.md for the full authoring guide.
|
|
11
|
+
set -euo pipefail
|
|
12
|
+
|
|
13
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
14
|
+
HARNESS_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
15
|
+
REPO_ROOT="$(cd "$HARNESS_DIR/.." && pwd)"
|
|
16
|
+
|
|
17
|
+
# shellcheck source=/dev/null
|
|
18
|
+
source "$HARNESS_DIR/harness.env"
|
|
19
|
+
# shellcheck source=/dev/null
|
|
20
|
+
source "$HARNESS_DIR/agent-slot.sh"
|
|
21
|
+
|
|
22
|
+
AGENT_ID="${1:?Usage: rocketsim-flows.sh <agent-id> [flow-name]}"
|
|
23
|
+
FLOW_FILTER="${2:-}"
|
|
24
|
+
|
|
25
|
+
validate_agent_id "$AGENT_ID"
|
|
26
|
+
|
|
27
|
+
log() { echo "==> [rocketsim agent-$AGENT_ID] $*" >&2; }
|
|
28
|
+
|
|
29
|
+
# ── Preflight ─────────────────────────────────────────────────────────────────
|
|
30
|
+
if ! command -v rocketsim >/dev/null 2>&1; then
|
|
31
|
+
echo "Error: rocketsim CLI not found." >&2
|
|
32
|
+
echo " Install from RocketSim → Settings → CLI & Agent → Install Command Line Tool" >&2
|
|
33
|
+
echo " Then re-run: ./.har/stages/rocketsim-flows.sh ${AGENT_ID}" >&2
|
|
34
|
+
exit 1
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
# Check RocketSim + Simulator health
|
|
38
|
+
if ! rocketsim doctor --quiet 2>/dev/null; then
|
|
39
|
+
log "Warning: rocketsim doctor reported issues — flows may be unreliable."
|
|
40
|
+
log " Run 'rocketsim doctor' for details."
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# ── Resolve agent env ─────────────────────────────────────────────────────────
|
|
44
|
+
ENV_FILE="$(resolve_agent_env_file "$AGENT_ID" "$REPO_ROOT")" || {
|
|
45
|
+
echo "No .env.agent.${AGENT_ID} found. Run: ./.har/launch.sh ${AGENT_ID}" >&2
|
|
46
|
+
exit 1
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
set -a
|
|
50
|
+
# shellcheck source=/dev/null
|
|
51
|
+
source "$ENV_FILE"
|
|
52
|
+
set +a
|
|
53
|
+
|
|
54
|
+
WORK_DIR="$(resolve_agent_work_dir "$ENV_FILE")"
|
|
55
|
+
|
|
56
|
+
# ── Discover flows ─────────────────────────────────────────────────────────────
|
|
57
|
+
FLOWS_DIR="$WORK_DIR/flows"
|
|
58
|
+
|
|
59
|
+
if [ ! -d "$FLOWS_DIR" ]; then
|
|
60
|
+
echo "Warning: flows/ directory not found in $WORK_DIR" >&2
|
|
61
|
+
echo " Create flow scripts under flows/ — see .har/stages/ROCKETSIM.md" >&2
|
|
62
|
+
node -e "process.stdout.write(JSON.stringify({
|
|
63
|
+
status: 'pass',
|
|
64
|
+
stageId: 'rocketsim-flows',
|
|
65
|
+
kind: 'test',
|
|
66
|
+
agent_id: ${AGENT_ID},
|
|
67
|
+
total_ms: 0,
|
|
68
|
+
flows: [],
|
|
69
|
+
message: 'No flows directory — nothing to run'
|
|
70
|
+
}, null, 2) + '\n');"
|
|
71
|
+
exit 0
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
if [ -n "$FLOW_FILTER" ]; then
|
|
75
|
+
FLOW_SCRIPTS=("$FLOWS_DIR/${FLOW_FILTER%.sh}.sh")
|
|
76
|
+
if [ ! -f "${FLOW_SCRIPTS[0]}" ]; then
|
|
77
|
+
echo "Error: flow not found: flows/${FLOW_FILTER%.sh}.sh" >&2
|
|
78
|
+
exit 1
|
|
79
|
+
fi
|
|
80
|
+
else
|
|
81
|
+
mapfile -t FLOW_SCRIPTS < <(find "$FLOWS_DIR" -maxdepth 1 -name "*.sh" ! -name ".*" | sort)
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
if [ ${#FLOW_SCRIPTS[@]} -eq 0 ]; then
|
|
85
|
+
log "No flow scripts found in flows/ — add .sh files to define user flows."
|
|
86
|
+
node -e "process.stdout.write(JSON.stringify({
|
|
87
|
+
status: 'pass',
|
|
88
|
+
stageId: 'rocketsim-flows',
|
|
89
|
+
kind: 'test',
|
|
90
|
+
agent_id: ${AGENT_ID},
|
|
91
|
+
total_ms: 0,
|
|
92
|
+
flows: [],
|
|
93
|
+
message: 'No flows defined yet'
|
|
94
|
+
}, null, 2) + '\n');"
|
|
95
|
+
exit 0
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
# ── Artifact directory ────────────────────────────────────────────────────────
|
|
99
|
+
ARTIFACT_DIR="$REPO_ROOT/.har/artifacts/rocketsim-flows"
|
|
100
|
+
mkdir -p "$ARTIFACT_DIR"
|
|
101
|
+
|
|
102
|
+
# ── Run flows ─────────────────────────────────────────────────────────────────
|
|
103
|
+
log "Running ${#FLOW_SCRIPTS[@]} flow(s) against simulator..."
|
|
104
|
+
|
|
105
|
+
OVERALL_PASS=true
|
|
106
|
+
START_TOTAL=$(date +%s%3N 2>/dev/null || echo "0")
|
|
107
|
+
FLOW_RESULTS="[]"
|
|
108
|
+
|
|
109
|
+
for FLOW_SCRIPT in "${FLOW_SCRIPTS[@]}"; do
|
|
110
|
+
FLOW_NAME="$(basename "$FLOW_SCRIPT" .sh)"
|
|
111
|
+
printf " → %-40s" "$FLOW_NAME..." >&2
|
|
112
|
+
|
|
113
|
+
FLOW_ARTIFACT_DIR="$ARTIFACT_DIR/$FLOW_NAME"
|
|
114
|
+
mkdir -p "$FLOW_ARTIFACT_DIR"
|
|
115
|
+
|
|
116
|
+
FLOW_START=$(date +%s%3N 2>/dev/null || echo "0")
|
|
117
|
+
|
|
118
|
+
set +e
|
|
119
|
+
FLOW_OUTPUT=$(
|
|
120
|
+
HARNESS_DIR="$HARNESS_DIR" \
|
|
121
|
+
WORK_DIR="$WORK_DIR" \
|
|
122
|
+
AGENT_ID="$AGENT_ID" \
|
|
123
|
+
FLOW_ARTIFACT_DIR="$FLOW_ARTIFACT_DIR" \
|
|
124
|
+
HARNESS_BUNDLE_ID="${HARNESS_BUNDLE_ID:-}" \
|
|
125
|
+
HARNESS_SIMULATOR_NAME="${HARNESS_SIMULATOR_NAME:-}" \
|
|
126
|
+
bash "$FLOW_SCRIPT" 2>&1
|
|
127
|
+
)
|
|
128
|
+
FLOW_EXIT=$?
|
|
129
|
+
set -e
|
|
130
|
+
|
|
131
|
+
FLOW_END=$(date +%s%3N 2>/dev/null || echo "0")
|
|
132
|
+
FLOW_MS=$(( FLOW_END - FLOW_START ))
|
|
133
|
+
|
|
134
|
+
if [ "$FLOW_EXIT" = "0" ]; then
|
|
135
|
+
echo "✓ (${FLOW_MS}ms)" >&2
|
|
136
|
+
FLOW_PASS="true"
|
|
137
|
+
else
|
|
138
|
+
echo "✗ (${FLOW_MS}ms)" >&2
|
|
139
|
+
echo "$FLOW_OUTPUT" | head -20 | sed 's/^/ /' >&2
|
|
140
|
+
FLOW_PASS="false"
|
|
141
|
+
OVERALL_PASS=false
|
|
142
|
+
fi
|
|
143
|
+
|
|
144
|
+
# Save flow output to artifact
|
|
145
|
+
echo "$FLOW_OUTPUT" > "$FLOW_ARTIFACT_DIR/output.log"
|
|
146
|
+
|
|
147
|
+
FLOW_OUTPUT_ESC=$(echo "$FLOW_OUTPUT" | head -30 | \
|
|
148
|
+
node -e "let d=''; process.stdin.on('data',c=>d+=c); process.stdin.on('end',()=>process.stdout.write(JSON.stringify(d.trim())))" \
|
|
149
|
+
2>/dev/null || echo '""')
|
|
150
|
+
|
|
151
|
+
FLOW_RESULTS=$(echo "$FLOW_RESULTS" | node -e "
|
|
152
|
+
const fs = require('fs');
|
|
153
|
+
let arr = JSON.parse(fs.readFileSync('/dev/stdin','utf8'));
|
|
154
|
+
arr.push({
|
|
155
|
+
name: '$FLOW_NAME',
|
|
156
|
+
pass: $FLOW_PASS,
|
|
157
|
+
ms: $FLOW_MS,
|
|
158
|
+
output: $FLOW_OUTPUT_ESC,
|
|
159
|
+
artifacts: '$FLOW_ARTIFACT_DIR'
|
|
160
|
+
});
|
|
161
|
+
process.stdout.write(JSON.stringify(arr));
|
|
162
|
+
" 2>/dev/null || echo "$FLOW_RESULTS")
|
|
163
|
+
done
|
|
164
|
+
|
|
165
|
+
END_TOTAL=$(date +%s%3N 2>/dev/null || echo "0")
|
|
166
|
+
TOTAL_MS=$(( END_TOTAL - START_TOTAL ))
|
|
167
|
+
|
|
168
|
+
# ── Output ────────────────────────────────────────────────────────────────────
|
|
169
|
+
node -e "
|
|
170
|
+
const flows = $FLOW_RESULTS;
|
|
171
|
+
const overall = flows.length > 0 && flows.every(f => f.pass);
|
|
172
|
+
const out = {
|
|
173
|
+
status: overall ? 'pass' : 'fail',
|
|
174
|
+
stageId: 'rocketsim-flows',
|
|
175
|
+
kind: 'test',
|
|
176
|
+
agent_id: $AGENT_ID,
|
|
177
|
+
total_ms: $TOTAL_MS,
|
|
178
|
+
flows,
|
|
179
|
+
artifacts: [
|
|
180
|
+
{ path: '.har/artifacts/rocketsim-flows', kind: 'directory' }
|
|
181
|
+
]
|
|
182
|
+
};
|
|
183
|
+
process.stdout.write(JSON.stringify(out, null, 2) + '\n');
|
|
184
|
+
"
|
|
185
|
+
|
|
186
|
+
if [ "$OVERALL_PASS" = "false" ]; then
|
|
187
|
+
exit 1
|
|
188
|
+
fi
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# RocketSim Stage Template
|
|
2
|
+
|
|
3
|
+
Add reusable RocketSim user-flow validation to your iOS harness.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
har env add-stage rocketsim
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## What this installs
|
|
10
|
+
|
|
11
|
+
| File | Purpose |
|
|
12
|
+
|------|---------|
|
|
13
|
+
| `.har/stages/rocketsim-flows.sh` | Stage runner — discovers and runs all `flows/*.sh` scripts |
|
|
14
|
+
| `.har/stages/ROCKETSIM.md` | Authoring guide: CLI reference, flow contract, naming conventions |
|
|
15
|
+
| `flows/README.md` | Quick start for adding new flows |
|
|
16
|
+
| `flows/example-smoke.sh` | Example smoke test — adapt for your app's main screen |
|
|
17
|
+
|
|
18
|
+
## Workflow
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
Code change
|
|
22
|
+
│
|
|
23
|
+
launch slot
|
|
24
|
+
│
|
|
25
|
+
build + unit-tests
|
|
26
|
+
│
|
|
27
|
+
rocketsim-flows ◄── your flows/*.sh scripts
|
|
28
|
+
│
|
|
29
|
+
pass/fail
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Full verify (`verify --full`) runs all flows after lint.
|
|
33
|
+
|
|
34
|
+
## Requirements
|
|
35
|
+
|
|
36
|
+
- [RocketSim](https://www.rocketsim.app/) installed and running
|
|
37
|
+
- `rocketsim` CLI installed from RocketSim → Settings → CLI & Agent
|
|
38
|
+
- At least one iOS Simulator booted (`xcrun simctl list devices | grep Booted`)
|
|
39
|
+
- Your app installed on the simulator before running flows
|
|
40
|
+
|
|
41
|
+
## Quick start after install
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# 1. Install the stage
|
|
45
|
+
har env add-stage rocketsim
|
|
46
|
+
|
|
47
|
+
# 2. Check RocketSim health
|
|
48
|
+
rocketsim doctor
|
|
49
|
+
|
|
50
|
+
# 3. Build and install your app
|
|
51
|
+
xcodebuild build ... && xcrun simctl install booted path/to/MyApp.app
|
|
52
|
+
xcrun simctl launch booted com.example.myapp
|
|
53
|
+
|
|
54
|
+
# 4. Run the example flow (adapt it for your main screen)
|
|
55
|
+
./.har/stages/rocketsim-flows.sh 1 example-smoke
|
|
56
|
+
|
|
57
|
+
# 5. Add real flows
|
|
58
|
+
cp flows/example-smoke.sh flows/my-feature.sh
|
|
59
|
+
# Edit flows/my-feature.sh to navigate your app and assert state
|
|
60
|
+
|
|
61
|
+
# 6. Run all flows
|
|
62
|
+
./.har/stages/rocketsim-flows.sh 1
|
|
63
|
+
|
|
64
|
+
# 7. Validate everything
|
|
65
|
+
./.har/verify.sh 1 --full
|
|
66
|
+
```
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# User Flow Scripts (RocketSim)
|
|
2
|
+
|
|
3
|
+
This directory contains reusable bash scripts that validate your iOS app's user flows against a running simulator using the [RocketSim CLI](https://www.rocketsim.app/docs/features/agentic-development/rocketsim-cli/).
|
|
4
|
+
|
|
5
|
+
The `rocketsim-flows` harness stage runs all scripts in this directory during full verification (`verify --full`). A single flow can also be run directly:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
./.har/stages/rocketsim-flows.sh 1 <flow-name>
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Adding a new flow
|
|
12
|
+
|
|
13
|
+
1. Create `flows/<name>.sh` (copy from `example-smoke.sh`)
|
|
14
|
+
2. Make it executable: `chmod +x flows/<name>.sh`
|
|
15
|
+
3. Run it: `./.har/stages/rocketsim-flows.sh 1 <name>`
|
|
16
|
+
|
|
17
|
+
Read `.har/stages/ROCKETSIM.md` for the full authoring guide and CLI reference.
|
|
18
|
+
|
|
19
|
+
## Naming convention
|
|
20
|
+
|
|
21
|
+
| Pattern | Example |
|
|
22
|
+
|---------|---------|
|
|
23
|
+
| One flow per user journey | `onboarding.sh`, `login.sh`, `checkout.sh` |
|
|
24
|
+
| Feature-specific flows | `settings-notifications.sh`, `profile-edit.sh` |
|
|
25
|
+
| Smoke test (run first) | `example-smoke.sh` or `smoke.sh` |
|
|
26
|
+
|
|
27
|
+
## How flows are run
|
|
28
|
+
|
|
29
|
+
The runner (`rocketsim-flows.sh`) sets these variables for every flow:
|
|
30
|
+
|
|
31
|
+
| Variable | Description |
|
|
32
|
+
|----------|-------------|
|
|
33
|
+
| `FLOW_ARTIFACT_DIR` | Save screenshots here (`$FLOW_ARTIFACT_DIR/success.png`, `failure.png`) |
|
|
34
|
+
| `HARNESS_BUNDLE_ID` | App bundle ID from `harness.env` |
|
|
35
|
+
| `HARNESS_SIMULATOR_NAME` | Simulator name from `harness.env` |
|
|
36
|
+
| `WORK_DIR` | Path to the session worktree |
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# flows/example-smoke.sh — Smoke test: verify the app launches and shows its main screen.
|
|
3
|
+
#
|
|
4
|
+
# Adapt this flow for your application:
|
|
5
|
+
# 1. Replace "Main Screen Title" with a visible element on your app's main screen.
|
|
6
|
+
# 2. Optionally add a tap and validate the next screen.
|
|
7
|
+
# 3. Rename and copy to create more specific user journey flows.
|
|
8
|
+
#
|
|
9
|
+
# See .har/stages/ROCKETSIM.md for the full authoring guide.
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
log() { echo "==> [example-smoke] $*" >&2; }
|
|
13
|
+
|
|
14
|
+
# ── Preflight ─────────────────────────────────────────────────────────────────
|
|
15
|
+
# The app must be running on the simulator. Install and launch it before running
|
|
16
|
+
# this flow. Example:
|
|
17
|
+
# xcrun simctl install booted path/to/MyApp.app
|
|
18
|
+
# xcrun simctl launch booted "${HARNESS_BUNDLE_ID}"
|
|
19
|
+
|
|
20
|
+
log "Checking simulator focus..."
|
|
21
|
+
FOCUSED=$(rocketsim simulator focused 2>/dev/null || echo "")
|
|
22
|
+
if [ -z "$FOCUSED" ]; then
|
|
23
|
+
log "✗ No focused simulator — run ./.har/setup-infra.sh first"
|
|
24
|
+
exit 1
|
|
25
|
+
fi
|
|
26
|
+
log "Simulator: $(echo "$FOCUSED" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{try{const j=JSON.parse(d);process.stdout.write(j.name||d.trim());}catch{process.stdout.write(d.trim());}})" 2>/dev/null || echo "$FOCUSED")"
|
|
27
|
+
|
|
28
|
+
# ── Wait for app to be ready ──────────────────────────────────────────────────
|
|
29
|
+
log "Waiting for app to settle..."
|
|
30
|
+
rocketsim wait screen-changed --timeout 5 2>/dev/null || true
|
|
31
|
+
sleep 1
|
|
32
|
+
|
|
33
|
+
# ── Read visible elements ─────────────────────────────────────────────────────
|
|
34
|
+
ELEMENTS=$(rocketsim elements --agent 2>/dev/null || echo "")
|
|
35
|
+
|
|
36
|
+
if [ -z "$ELEMENTS" ]; then
|
|
37
|
+
log "✗ No elements found on screen — is the app running?"
|
|
38
|
+
rocketsim screenshot > "$FLOW_ARTIFACT_DIR/failure-no-elements.png" 2>/dev/null || true
|
|
39
|
+
exit 1
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
log "Visible elements:"
|
|
43
|
+
echo "$ELEMENTS" | head -20 | sed 's/^/ /' >&2
|
|
44
|
+
|
|
45
|
+
# ── Assert main screen ────────────────────────────────────────────────────────
|
|
46
|
+
# TODO: replace "Main Screen Title" with a StaticText or other element that
|
|
47
|
+
# uniquely identifies your app's main screen after launch.
|
|
48
|
+
EXPECTED_LABEL="Main Screen Title"
|
|
49
|
+
|
|
50
|
+
if echo "$ELEMENTS" | grep -qi "$EXPECTED_LABEL"; then
|
|
51
|
+
log "✓ Found expected element: '$EXPECTED_LABEL'"
|
|
52
|
+
rocketsim screenshot > "$FLOW_ARTIFACT_DIR/success.png" 2>/dev/null || true
|
|
53
|
+
else
|
|
54
|
+
log "✗ Expected element not found: '$EXPECTED_LABEL'"
|
|
55
|
+
log " Adapt EXPECTED_LABEL in flows/example-smoke.sh to match your app's main screen."
|
|
56
|
+
rocketsim screenshot > "$FLOW_ARTIFACT_DIR/failure-missing-element.png" 2>/dev/null || true
|
|
57
|
+
exit 1
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
log "Smoke test passed."
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "rocketsim",
|
|
3
|
+
"stageId": "rocketsim-flows",
|
|
4
|
+
"verificationStages": [
|
|
5
|
+
"build",
|
|
6
|
+
"unit-tests",
|
|
7
|
+
"lint",
|
|
8
|
+
"rocketsim-flows"
|
|
9
|
+
],
|
|
10
|
+
"stage": {
|
|
11
|
+
"id": "rocketsim-flows",
|
|
12
|
+
"kind": "test",
|
|
13
|
+
"description": "RocketSim user-flow validation (reusable bash flows against the running app)",
|
|
14
|
+
"script": "stages/rocketsim-flows.sh",
|
|
15
|
+
"requiresAgentId": true,
|
|
16
|
+
"artifacts": [
|
|
17
|
+
{
|
|
18
|
+
"path": ".har/artifacts/rocketsim-flows",
|
|
19
|
+
"kind": "directory",
|
|
20
|
+
"description": "Screenshots, per-flow results, and run summary"
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
{ "src": ".har/stages/rocketsim-flows.sh", "dest": ".har/stages/rocketsim-flows.sh", "executable": true },
|
|
26
|
+
{ "src": ".har/stages/ROCKETSIM.md", "dest": ".har/stages/ROCKETSIM.md" },
|
|
27
|
+
{ "src": "flows/README.md", "dest": "flows/README.md" },
|
|
28
|
+
{ "src": "flows/example-smoke.sh", "dest": "flows/example-smoke.sh", "executable": true }
|
|
29
|
+
],
|
|
30
|
+
"nextSteps": [
|
|
31
|
+
"rocketsim doctor",
|
|
32
|
+
"./.har/launch.sh 1",
|
|
33
|
+
"Build and install your app on the simulator",
|
|
34
|
+
"./.har/stages/rocketsim-flows.sh 1",
|
|
35
|
+
"Open .har/artifacts/rocketsim-flows/ for screenshots and per-flow results",
|
|
36
|
+
"Add new flows in flows/ — read .har/stages/ROCKETSIM.md for the authoring guide"
|
|
37
|
+
]
|
|
38
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@osfactory/har",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "LLM-powered CLI for reproducible agent development environments",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"har",
|
|
7
|
+
"cli",
|
|
8
|
+
"mcp",
|
|
9
|
+
"agent",
|
|
10
|
+
"harness",
|
|
11
|
+
"llm",
|
|
12
|
+
"devops"
|
|
13
|
+
],
|
|
14
|
+
"bin": {
|
|
15
|
+
"har": "dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"control/docker-compose.yml",
|
|
20
|
+
"control/docker-compose.build.yml"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "esbuild src/index.ts --bundle --platform=node --target=node20 --outfile=dist/index.js --banner:js='#!/usr/bin/env node' && rm -rf dist/templates dist/prompts && cp -r src/templates dist/templates && mkdir -p dist/prompts && cp src/llm/prompts/*.md dist/prompts/",
|
|
24
|
+
"dev": "tsx src/index.ts",
|
|
25
|
+
"test": "jest",
|
|
26
|
+
"test:watch": "jest --watch",
|
|
27
|
+
"typecheck": "tsc --noEmit",
|
|
28
|
+
"lint": "eslint src --ext .ts",
|
|
29
|
+
"prepublishOnly": "npm run build",
|
|
30
|
+
"release": "semantic-release"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@anthropic-ai/sdk": "^0.27.0",
|
|
34
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
35
|
+
"chalk": "^5.3.0",
|
|
36
|
+
"inquirer": "^10.1.2",
|
|
37
|
+
"js-yaml": "^4.1.0",
|
|
38
|
+
"ora": "^8.0.1",
|
|
39
|
+
"yargs": "^17.7.2",
|
|
40
|
+
"zod": "^3.23.8"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/inquirer": "^9.0.7",
|
|
44
|
+
"@types/jest": "^29.5.12",
|
|
45
|
+
"@types/js-yaml": "^4.0.9",
|
|
46
|
+
"@types/node": "^22.5.0",
|
|
47
|
+
"@types/yargs": "^17.0.33",
|
|
48
|
+
"@typescript-eslint/eslint-plugin": "^8.62.0",
|
|
49
|
+
"@typescript-eslint/parser": "^8.62.0",
|
|
50
|
+
"esbuild": "^0.23.0",
|
|
51
|
+
"eslint": "^8.57.1",
|
|
52
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
53
|
+
"@semantic-release/exec": "^6.0.3",
|
|
54
|
+
"@semantic-release/git": "^10.0.1",
|
|
55
|
+
"@semantic-release/github": "^11.0.1",
|
|
56
|
+
"@semantic-release/npm": "^12.0.1",
|
|
57
|
+
"jest": "^29.7.0",
|
|
58
|
+
"semantic-release": "^24.2.3",
|
|
59
|
+
"ts-jest": "^29.2.4",
|
|
60
|
+
"tsx": "^4.17.0",
|
|
61
|
+
"typescript": "^5.5.4"
|
|
62
|
+
},
|
|
63
|
+
"engines": {
|
|
64
|
+
"node": ">=20"
|
|
65
|
+
},
|
|
66
|
+
"repository": {
|
|
67
|
+
"type": "git",
|
|
68
|
+
"url": "https://github.com/os-factory/har"
|
|
69
|
+
},
|
|
70
|
+
"homepage": "https://github.com/os-factory/har#readme",
|
|
71
|
+
"bugs": "https://github.com/os-factory/har/issues",
|
|
72
|
+
"author": "Antoine Frau",
|
|
73
|
+
"license": "AGPL-3.0-only",
|
|
74
|
+
"publishConfig": {
|
|
75
|
+
"access": "public",
|
|
76
|
+
"registry": "https://registry.npmjs.org"
|
|
77
|
+
}
|
|
78
|
+
}
|