@walwal-harness/cli 7.1.40 → 7.1.42
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/HR-Resource/cdo/SKILL.md +9 -1
- package/HR-Resource/ceo/SKILL.md +33 -2
- package/HR-Resource/coo/SKILL.md +9 -1
- package/HR-Resource/cqo/SKILL.md +9 -1
- package/HR-Resource/cto/SKILL.md +10 -2
- package/HR-Resource/ops/SKILL.md +12 -4
- package/HR-Resource/ops-prompt-inspector/SKILL.md +11 -6
- package/HR-Resource/ops-prompt-inspector/assets/prompt-inspector-static.js +180 -0
- package/HR-Resource/ops-prompt-inspector/scripts/setup.py +66 -3
- package/apps/harness-dashboard/app/api/metrics/route.ts +17 -0
- package/apps/harness-dashboard/app/api/wake/route.ts +41 -0
- package/apps/harness-dashboard/app/brick.css +482 -0
- package/apps/harness-dashboard/app/layout.tsx +13 -0
- package/apps/harness-dashboard/app/page.tsx +2 -2
- package/apps/harness-dashboard/components/Scene.tsx +690 -36
- package/apps/harness-dashboard/components/brick/BrickDashboard.tsx +149 -0
- package/apps/harness-dashboard/components/brick/reports.tsx +291 -0
- package/apps/harness-dashboard/components/brick/tweaks.tsx +738 -0
- package/apps/harness-dashboard/components/brick/ui.tsx +463 -0
- package/apps/harness-dashboard/components/brick/views-graph.tsx +305 -0
- package/apps/harness-dashboard/components/brick/views-grid.tsx +543 -0
- package/apps/harness-dashboard/components/brick/views-timeline.tsx +238 -0
- package/apps/harness-dashboard/lib/__tests__/harness-state.test.ts +1 -202
- package/apps/harness-dashboard/lib/__tests__/sandbox-e2e.test.ts +490 -0
- package/apps/harness-dashboard/lib/brick/adapter.ts +423 -0
- package/apps/harness-dashboard/lib/brick/contract.ts +247 -0
- package/apps/harness-dashboard/lib/harness-state.ts +107 -796
- package/apps/harness-dashboard/lib/metrics/sampler.ts +545 -0
- package/apps/harness-dashboard/lib/types.ts +22 -222
- package/apps/harness-dashboard/playwright.config.ts +26 -7
- package/assets/templates/AGENTS-ko.md.template +1 -1
- package/assets/templates/AGENTS.md.template +12 -2
- package/assets/templates/HARNESS.md +2 -0
- package/assets/templates/company-pipeline-manifest.json +5 -14
- package/assets/templates/worker-report.md.template +38 -0
- package/bin/init.js +2 -1
- package/commands/goal.md +4 -2
- package/commands/hot-fix.md +1 -1
- package/commands/submission.md +1 -1
- package/package.json +1 -1
- package/scripts/conductor-tick.sh +12 -0
- package/scripts/harness-agenda.sh +105 -0
- package/scripts/harness-company-block.sh +89 -0
- package/scripts/harness-company-complete.sh +17 -8
- package/scripts/harness-company-cycle.sh +54 -0
- package/scripts/harness-progress-set.sh +8 -2
- package/scripts/harness-stop.sh +76 -0
- package/scripts/harness-wake-control.sh +151 -0
- package/scripts/harness-wake.sh +5 -0
- package/scripts/harness-worker-dispatch.sh +31 -7
- package/apps/harness-dashboard/components/Drawer.tsx +0 -146
- package/apps/harness-dashboard/components/Header.tsx +0 -40
- package/apps/harness-dashboard/components/MissionTimeline.tsx +0 -214
- package/apps/harness-dashboard/components/OrgTree.tsx +0 -459
- package/apps/harness-dashboard/components/drawer/AgentLogTab.tsx +0 -56
- package/apps/harness-dashboard/components/drawer/DesignPreviewTab.tsx +0 -160
- package/apps/harness-dashboard/components/drawer/GotchasTab.tsx +0 -89
- package/apps/harness-dashboard/components/drawer/HypothesisTab.tsx +0 -42
- package/apps/harness-dashboard/components/drawer/IncidentsTab.tsx +0 -50
- package/apps/harness-dashboard/components/drawer/MissionDocTab.tsx +0 -96
- package/apps/harness-dashboard/components/drawer/MissionFlowTab.tsx +0 -576
- package/apps/harness-dashboard/components/drawer/OwnerHistoryTab.tsx +0 -160
- package/apps/harness-dashboard/components/drawer/RoomMetricsTab.tsx +0 -90
- package/apps/harness-dashboard/components/drawer/TracksTab.tsx +0 -47
- package/apps/harness-dashboard/components/three/ArchiveBoxes.tsx +0 -95
- package/apps/harness-dashboard/components/three/DeptBoard.tsx +0 -175
- package/apps/harness-dashboard/components/three/Floor3D.tsx +0 -404
- package/apps/harness-dashboard/components/three/GoalCard3D.tsx +0 -123
- package/apps/harness-dashboard/components/three/HypothesisCards.tsx +0 -71
- package/apps/harness-dashboard/components/three/MeetingWhiteboard.tsx +0 -98
- package/apps/harness-dashboard/components/three/Minifig3D.tsx +0 -391
- package/apps/harness-dashboard/components/three/OpsSiren.tsx +0 -99
- package/apps/harness-dashboard/components/three/Stage3D.tsx +0 -104
- package/apps/harness-dashboard/components/three/TrackRibbons.tsx +0 -81
- package/apps/harness-dashboard/lib/__tests__/i18n.test.ts +0 -16
- package/apps/harness-dashboard/lib/__tests__/iso.test.ts +0 -42
- package/apps/harness-dashboard/lib/__tests__/state-mapping.test.ts +0 -183
- package/apps/harness-dashboard/lib/agent-roster.ts +0 -42
- package/apps/harness-dashboard/lib/furniture.ts +0 -165
- package/apps/harness-dashboard/lib/i18n.ts +0 -26
- package/apps/harness-dashboard/lib/iso.ts +0 -150
- package/apps/harness-dashboard/lib/markdown.tsx +0 -306
- package/apps/harness-dashboard/lib/path-planning.ts +0 -72
- package/apps/harness-dashboard/lib/state-mapping.ts +0 -146
package/HR-Resource/cdo/SKILL.md
CHANGED
|
@@ -27,7 +27,15 @@ Before design work, read `.harness/conventions/shared.md`, `.harness/conventions
|
|
|
27
27
|
|
|
28
28
|
## Worker Activity Telemetry
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
When CEO routes this mission to you, set yourself as the live agent on entry so the dashboard shows the handoff: `bash scripts/harness-progress-set.sh . '.current_agent="cdo" | .agent_status="running"'`.
|
|
31
|
+
|
|
32
|
+
Before launching any fresh worker session, update `.harness/progress.json` with `scripts/harness-progress-set.sh` so dashboards can show the worker as active. Record the worker name, owning CXX, report path, and `status:"running"` under `company_state.workers`, increment `company_state.active_workers`, and set `conductor.current_action` to `spawn:{worker-name}`. After the worker report is accepted, update that worker to `status:"complete"` and decrement `active_workers`. Do not leave `active_workers:0` while a worker session is running. Require every worker report to open with a `## Status` line whose body is `IN_PROGRESS` while the worker runs and `COMPLETE` once the report is final, so the dashboard shows true worker liveness instead of guessing from file timestamps.
|
|
33
|
+
|
|
34
|
+
On exit, after writing `cdo.md` and handing back to CEO, run `bash scripts/harness-progress-set.sh . '.agent_status="completed"'` so the loop advances and the dashboard reflects the finished step. Do not clear `conductor.state`; only the CEO's Company Loop Termination step ends the loop.
|
|
35
|
+
|
|
36
|
+
## Operating Mode — Status Briefing & Agenda
|
|
37
|
+
|
|
38
|
+
When the active goal is operating (perpetual, `mission-state.json` lifecycle `operating`), CEO periodically orders a 현황 보고. In it, confirm — with worker-backed evidence — whether your hired workers' live deliverables still operate correctly toward the goal (for CDO: is the shipped UI/UX/brand still consistent and usable?). If you discover a regression, drift, incident, opportunity, or risk, do not silently fix it or sit on it: raise it as an agenda item so CEO can adjudicate and route the next cycle: `bash scripts/harness-agenda.sh . <goal-rel> raise cdo <kind> "<title>" "<evidence-path>"` (kinds: loss, drift, incident, opportunity, risk, verification-gap). When CEO routes a decided agenda item to you, execute it through hired workers, get CQO verification where behavior must be proven, and report so CEO can close the item.
|
|
31
39
|
|
|
32
40
|
## Rule
|
|
33
41
|
|
package/HR-Resource/ceo/SKILL.md
CHANGED
|
@@ -32,6 +32,37 @@ CEO is authorized to approve routine company operations without Owner confirmati
|
|
|
32
32
|
|
|
33
33
|
If an operational choice is reversible and uses existing project-local credentials or configuration, CEO decides, records the rationale in `ceo.md`, and continues. Escalate only for new secrets, new spending, legal/business acceptance, unavailable external production access, destructive data action, or a direct conflict with the Owner's stated direction.
|
|
34
34
|
|
|
35
|
+
## Company Loop Termination
|
|
36
|
+
|
|
37
|
+
There are exactly two legitimate ways to end the company loop, and each requires an explicit **runtime transition**. Writing `ceo.md` and `mission-state.json` is not enough: the autonomous Stop loop and the dashboard read `progress.json` runtime state (`conductor.state`, `agent_status`, `current_agent`), not your documents. If you finish a report but never fire the transition, the loop stays `running` and the harness keeps prompting you to continue — this is the "is it done or not?" ambiguity. Avoid it by always ending in one of these two states:
|
|
38
|
+
|
|
39
|
+
1. **COMPLETE** — the mission is genuinely finished: the final Owner report is in `ceo.md`, all required CXX/worker/OPS evidence is collected, and `mission-state.json` is terminal (`complete`/`closed`/`cancelled`/`superseded`) with `active:false`. As the **literal final action of the turn**, run:
|
|
40
|
+
`bash scripts/harness-company-complete.sh . <reason>`
|
|
41
|
+
2. **BLOCKED on external authority** — the next action genuinely needs authority the harness cannot infer or obtain (new credentials/secrets, payment approval, legal/business acceptance, unavailable production access, destructive data action, or a direct conflict with the Owner's stated direction). Record the exact missing authority and the internally recommended default in `ceo.md`, set `mission-state.json` lifecycle `blocked` with `active:false`, then run:
|
|
42
|
+
`bash scripts/harness-company-block.sh . "<exact missing authority>"`
|
|
43
|
+
|
|
44
|
+
`Truly done = final Owner report + terminal mission-state.json + the matching runtime transition fired.` Until one of these two transitions runs, the mission is still in progress: keep routing CXX and worker work autonomously between turns. Never fire the completion transition while real CXX/worker/verification work remains, and never end a turn in the `running` state with no further action queued.
|
|
45
|
+
|
|
46
|
+
**Completion applies only to FINITE goals.** A perpetual/operating goal (next section) NEVER completes — `harness-company-complete.sh` must not be run for it.
|
|
47
|
+
|
|
48
|
+
## Operating (Perpetual) Goals — the never-ending company loop
|
|
49
|
+
|
|
50
|
+
First, **classify the goal**:
|
|
51
|
+
|
|
52
|
+
- **Finite** goal — "build X", "add Y", "fix Z": has a definite done state. Use the Company Loop Termination above.
|
|
53
|
+
- **Operating (perpetual)** goal — "run/operate/monitor/keep growing X", "지속/영구 운영", "make money continuously", anything that should *never* stop (e.g. "build a trading bot and keep it profitable forever"): it must run as a standing company that cycles indefinitely.
|
|
54
|
+
|
|
55
|
+
For an operating goal, set `mission-state.json` to `{"lifecycle":"operating","active":true}` (it stays active forever) and **never** call `harness-company-complete.sh`. The only ways an operating goal ends are: the Owner explicitly orders it stopped (then run `harness-company-complete.sh`), or a true external-authority block (then `harness-company-block.sh`).
|
|
56
|
+
|
|
57
|
+
Run it as an **agenda-driven standing executive loop**. The agenda is the shared meetup file every CXX co-writes at `.harness/documents/{goal}/agenda.json`, managed with `scripts/harness-agenda.sh`. Each operating tick:
|
|
58
|
+
|
|
59
|
+
1. **Read the agenda** (`scripts/harness-agenda.sh . {goal} list`).
|
|
60
|
+
2. **If there are active items**, you are *forced* to drive them: for every `open` item, decide and record the decision (`scripts/harness-agenda.sh . {goal} decide <id> "<decision>" <owning-cxx>`), then route that CXX to execute through hired workers and CQO-verify; when an item's work is done and verified, close it (`... close <id>`). Do not end the turn with active agenda.
|
|
61
|
+
3. **If the agenda is empty ("보고사항 없음")**, run a **status-briefing round**: require each relevant CXX (COO/CDO/CTO/CQO/OPS) to file a 현황 보고 — confirm that its workers' *live deliverables* still operate correctly toward the goal — and to raise any newly discovered loss/drift/opportunity/risk as a new agenda item (`scripts/harness-agenda.sh . {goal} raise <cxx> <kind> "<title>" "<evidence>"`). This is how the company discovers its own next work.
|
|
62
|
+
4. **If a full briefing round genuinely surfaces nothing**, record the operating heartbeat (`scripts/harness-company-cycle.sh . {goal}`) and end the turn — the hourly wake loop resumes the next cycle. This keeps the company always-on without burning a turn spinning.
|
|
63
|
+
|
|
64
|
+
The canonical operating cycle for a self-improving system: `OPS monitor → on loss/drift/opportunity, COO researches + backtests a new strategy → CTO applies it safely → CQO verifies → OPS operates and watches → (repeat)`. Losses are not failures to report to the Owner; they are agenda items that trigger the next research→apply→operate cycle autonomously.
|
|
65
|
+
|
|
35
66
|
## Lazy Rule Loading
|
|
36
67
|
|
|
37
68
|
Before routing or accepting CXX work, enforce lazy loading:
|
|
@@ -51,8 +82,8 @@ Before routing or accepting CXX work, enforce lazy loading:
|
|
|
51
82
|
- CTO: architecture, platform, API, account, web/app/backend/frontend wiring.
|
|
52
83
|
- CQO: quality gates, e2e/backtest strategy, regression and archive criteria.
|
|
53
84
|
- OPS: build/service environment monitoring, CQO verification watch, port map checks, launch observation, production watch, and exception monitoring.
|
|
54
|
-
4. Route completed outputs to the next responsible CXX.
|
|
55
|
-
5. Report outcomes, final acceptance requests, and true external-authority blocks to the Owner.
|
|
85
|
+
4. Route completed outputs to the next responsible CXX. Keep `progress.json` `current_agent` honest as you route so the dashboard shows the live handoff: each CXX sets `current_agent` to its own role on entry; set it back with `bash scripts/harness-progress-set.sh . '.current_agent="ceo" | .agent_status="running"'` whenever you resume between CXX steps and before the final report.
|
|
86
|
+
5. Report outcomes, final acceptance requests, and true external-authority blocks to the Owner, then fire the Company Loop Termination transition (complete or blocked).
|
|
56
87
|
|
|
57
88
|
## Hard Rules
|
|
58
89
|
|
package/HR-Resource/coo/SKILL.md
CHANGED
|
@@ -37,7 +37,15 @@ During planning, COO must determine whether the active runtime exposes MCP serve
|
|
|
37
37
|
|
|
38
38
|
## Worker Activity Telemetry
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
When CEO routes this mission to you, set yourself as the live agent on entry so the dashboard shows the handoff: `bash scripts/harness-progress-set.sh . '.current_agent="coo" | .agent_status="running"'`.
|
|
41
|
+
|
|
42
|
+
Before launching any fresh worker session, update `.harness/progress.json` with `scripts/harness-progress-set.sh` so dashboards can show the worker as active. Record the worker name, owning CXX, report path, and `status:"running"` under `company_state.workers`, increment `company_state.active_workers`, and set `conductor.current_action` to `spawn:{worker-name}`. After the worker report is accepted, update that worker to `status:"complete"` and decrement `active_workers`. Do not leave `active_workers:0` while a worker session is running. Require every worker report to open with a `## Status` line whose body is `IN_PROGRESS` while the worker runs and `COMPLETE` once the report is final, so the dashboard shows true worker liveness instead of guessing from file timestamps.
|
|
43
|
+
|
|
44
|
+
On exit, after writing `coo.md` and handing back to CEO, run `bash scripts/harness-progress-set.sh . '.agent_status="completed"'` so the loop advances and the dashboard reflects the finished step. Do not clear `conductor.state`; only the CEO's Company Loop Termination step ends the loop.
|
|
45
|
+
|
|
46
|
+
## Operating Mode — Status Briefing & Agenda
|
|
47
|
+
|
|
48
|
+
When the active goal is operating (perpetual, `mission-state.json` lifecycle `operating`), CEO periodically orders a 현황 보고. In it, confirm — with worker-backed evidence — whether your hired workers' live deliverables still operate correctly toward the goal (for COO: is the current strategy/plan still valid and performing in research/backtest?). If you discover a loss, drift, regression, incident, opportunity, or risk, do not silently fix it or sit on it: raise it as an agenda item so CEO can adjudicate and route the next cycle: `bash scripts/harness-agenda.sh . <goal-rel> raise coo <kind> "<title>" "<evidence-path>"` (kinds: loss, drift, incident, opportunity, risk, verification-gap). When CEO routes a decided agenda item to you, execute it through hired workers, get CQO verification where behavior must be proven, and report so CEO can close the item.
|
|
41
49
|
|
|
42
50
|
## Non-Execution Rule
|
|
43
51
|
|
package/HR-Resource/cqo/SKILL.md
CHANGED
|
@@ -26,7 +26,15 @@ Before quality work, read `.harness/conventions/shared.md`, `.harness/convention
|
|
|
26
26
|
|
|
27
27
|
## Worker Activity Telemetry
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
When CEO routes this mission to you, set yourself as the live agent on entry so the dashboard shows the handoff: `bash scripts/harness-progress-set.sh . '.current_agent="cqo" | .agent_status="running"'`.
|
|
30
|
+
|
|
31
|
+
Before launching any fresh worker session, update `.harness/progress.json` with `scripts/harness-progress-set.sh` so dashboards can show the worker as active. Record the worker name, owning CXX, report path, and `status:"running"` under `company_state.workers`, increment `company_state.active_workers`, and set `conductor.current_action` to `spawn:{worker-name}`. After the worker report is accepted, update that worker to `status:"complete"` and decrement `active_workers`. Do not leave `active_workers:0` while a worker session is running. Require every worker report to open with a `## Status` line whose body is `IN_PROGRESS` while the worker runs and `COMPLETE` once the report is final, so the dashboard shows true worker liveness instead of guessing from file timestamps.
|
|
32
|
+
|
|
33
|
+
On exit, after writing `cqo.md` and handing back to CEO, run `bash scripts/harness-progress-set.sh . '.agent_status="completed"'` so the loop advances and the dashboard reflects the finished step. Do not clear `conductor.state`; only the CEO's Company Loop Termination step ends the loop.
|
|
34
|
+
|
|
35
|
+
## Operating Mode — Status Briefing & Agenda
|
|
36
|
+
|
|
37
|
+
When the active goal is operating (perpetual, `mission-state.json` lifecycle `operating`), CEO periodically orders a 현황 보고. In it, confirm — with evaluator-worker evidence — whether the live system still passes the quality/regression bar toward the goal (for CQO: are regression/e2e/perf/security gates still green on the running system?). If you discover a regression, quality drift, incident, or verification gap, do not silently sit on it: raise it as an agenda item so CEO can adjudicate and route the next cycle: `bash scripts/harness-agenda.sh . <goal-rel> raise cqo <kind> "<title>" "<evidence-path>"` (kinds: loss, drift, incident, opportunity, risk, verification-gap). When CEO routes a decided agenda item to you, run the evaluator/tester workers, issue a verdict, and report so CEO can close the item.
|
|
30
38
|
|
|
31
39
|
## Hard Rules
|
|
32
40
|
|
package/HR-Resource/cto/SKILL.md
CHANGED
|
@@ -28,13 +28,21 @@ Before engineering work, read `.harness/conventions/shared.md`, `.harness/conven
|
|
|
28
28
|
|
|
29
29
|
## Worker Activity Telemetry
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
When CEO routes this mission to you, set yourself as the live agent on entry so the dashboard shows the handoff: `bash scripts/harness-progress-set.sh . '.current_agent="cto" | .agent_status="running"'`.
|
|
32
|
+
|
|
33
|
+
Before launching any fresh worker session, update `.harness/progress.json` with `scripts/harness-progress-set.sh` so dashboards can show the worker as active. Record the worker name, owning CXX, report path, and `status:"running"` under `company_state.workers`, increment `company_state.active_workers`, and set `conductor.current_action` to `spawn:{worker-name}`. After the worker report is accepted, update that worker to `status:"complete"` and decrement `active_workers`. Do not leave `active_workers:0` while a worker session is running. Require every worker report to open with a `## Status` line whose body is `IN_PROGRESS` while the worker runs and `COMPLETE` once the report is final, so the dashboard shows true worker liveness instead of guessing from file timestamps.
|
|
34
|
+
|
|
35
|
+
On exit, after writing `cto.md` and handing back to CEO, run `bash scripts/harness-progress-set.sh . '.agent_status="completed"'` so the loop advances and the dashboard reflects the finished step. Do not clear `conductor.state`; only the CEO's Company Loop Termination step ends the loop.
|
|
36
|
+
|
|
37
|
+
## Operating Mode — Status Briefing & Agenda
|
|
38
|
+
|
|
39
|
+
When the active goal is operating (perpetual, `mission-state.json` lifecycle `operating`), CEO periodically orders a 현황 보고. In it, confirm — with worker-backed evidence — whether your hired workers' live deliverables still operate correctly toward the goal (for CTO: is the running system stable — errors, latency, uptime, resource limits?). If you discover a failure, drift, incident, opportunity, or risk, do not silently fix it or sit on it: raise it as an agenda item so CEO can adjudicate and route the next cycle: `bash scripts/harness-agenda.sh . <goal-rel> raise cto <kind> "<title>" "<evidence-path>"` (kinds: loss, drift, incident, opportunity, risk, verification-gap). When CEO routes a decided agenda item to you, implement it through hired workers, get CQO verification, and report so CEO can close the item.
|
|
32
40
|
|
|
33
41
|
## Browser Automation Briefing
|
|
34
42
|
|
|
35
43
|
Every CTO worker brief that may use Playwright, browser automation, browser-based inspection, E2E, or visual verification must explicitly include this requirement:
|
|
36
44
|
|
|
37
|
-
> Run Playwright/browser automation with a visible browser. Set `headless: false` in launch/config code, use headed test mode (`--headed`, `PWDEBUG=1`, or equivalent), prefer `channel: 'chrome'` when available, and do not use headless mode unless the Owner has explicitly approved an exception in this mission.
|
|
45
|
+
> Run Playwright/browser automation with a visible browser. Set `headless: false` in launch/config code, use headed test mode (`--headed`, `PWDEBUG=1`, or equivalent), prefer `channel: 'chrome'` when available, and do not use headless mode unless the Owner has explicitly approved an exception in this mission. Pace it middle-fast: `slowMo: 120` (ms) — observable but brisk. Do not use `slowMo: 300`+ (too slow); raise it only if the Owner explicitly asks to slow the demo down.
|
|
38
46
|
|
|
39
47
|
CTO must not accept worker plans or reports that omit this requirement when browser automation is in scope.
|
|
40
48
|
|
package/HR-Resource/ops/SKILL.md
CHANGED
|
@@ -52,13 +52,13 @@ OPS must actively watch the runtime while CQO evaluator workers test the product
|
|
|
52
52
|
|
|
53
53
|
## Dev-Mode Prompt Inspector
|
|
54
54
|
|
|
55
|
-
When OPS builds or runs a React/Next.js UI in development mode, and screen-level discussion would benefit from selecting visible UI elements and mapping intended REST/API behavior, OPS must use the installed `harness-ops-prompt-inspector` skill.
|
|
55
|
+
When OPS builds or runs a React/Next.js or static HTML UI in development mode, and screen-level discussion would benefit from selecting visible UI elements and mapping intended REST/API behavior, OPS must use the installed `harness-ops-prompt-inspector` skill.
|
|
56
56
|
|
|
57
57
|
- Use Prompt Inspector only for development builds or verification environments. Do not inject it into production builds or production service environments.
|
|
58
58
|
- Install it from the project-local skill path under `.claude/skills/harness-ops-prompt-inspector/` or `.codex/skills/harness-ops-prompt-inspector/`; do not download it at mission runtime.
|
|
59
59
|
- Run its installer against the target app path before or during the dev-mode run so the toolbar is available in the browser for UI/API binding notes.
|
|
60
60
|
- Record the install result, target app path, dev server URL, and generated API discovery evidence in OPS environment evidence.
|
|
61
|
-
- If the project is not React/Next.js or the build is not development mode, report Prompt Inspector as not applicable instead of forcing installation.
|
|
61
|
+
- If the project is not React/Next.js, static HTML, or the build is not development mode, report Prompt Inspector as not applicable instead of forcing installation.
|
|
62
62
|
|
|
63
63
|
## Post-Launch Watch
|
|
64
64
|
|
|
@@ -92,13 +92,21 @@ After service launch, OPS continues the same monitoring duty against `runtime.pr
|
|
|
92
92
|
|
|
93
93
|
## Worker Activity Telemetry
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
When CEO routes this mission to you, set yourself as the live agent on entry so the dashboard shows the handoff: `bash scripts/harness-progress-set.sh . '.current_agent="ops" | .agent_status="running"'`.
|
|
96
|
+
|
|
97
|
+
Before launching any fresh worker session, update `.harness/progress.json` with `scripts/harness-progress-set.sh` so dashboards can show the worker as active. Record the worker name, owning CXX, report path, and `status:"running"` under `company_state.workers`, increment `company_state.active_workers`, and set `conductor.current_action` to `spawn:{worker-name}`. After the worker report is accepted, update that worker to `status:"complete"` and decrement `active_workers`. Do not leave `active_workers:0` while a worker session is running. Require every worker report to open with a `## Status` line whose body is `IN_PROGRESS` while the worker runs and `COMPLETE` once the report is final, so the dashboard shows true worker liveness instead of guessing from file timestamps.
|
|
98
|
+
|
|
99
|
+
On exit, after writing `ops.md` and handing back to CEO, run `bash scripts/harness-progress-set.sh . '.agent_status="completed"'` so the loop advances and the dashboard reflects the finished step. Do not clear `conductor.state`; only the CEO's Company Loop Termination step ends the loop.
|
|
100
|
+
|
|
101
|
+
## Operating Mode — Status Briefing & Agenda
|
|
102
|
+
|
|
103
|
+
When the active goal is operating (perpetual, `mission-state.json` lifecycle `operating`), OPS is the primary monitor of the never-ending loop. On every CEO 현황 보고, report — with monitoring evidence (logs, health, ports, metrics, P&L or domain KPIs) — whether the live system still operates correctly toward the goal. Any loss, drawdown, drift, incident, degraded health, or opportunity MUST become an agenda item so CEO can adjudicate and route the next research→apply→operate cycle: `bash scripts/harness-agenda.sh . <goal-rel> raise ops <kind> "<title>" "<evidence-path>"` (kinds: loss, drift, incident, opportunity, risk, verification-gap). OPS does not fix systems itself — it surfaces the agenda and supplies evidence; CTO owns the fix, COO owns new-strategy research, CQO owns verification. A clean monitoring round with nothing to raise is itself a valid briefing result.
|
|
96
104
|
|
|
97
105
|
## Browser Automation Briefing
|
|
98
106
|
|
|
99
107
|
Every OPS worker brief that may use Playwright, browser automation, browser-based monitoring, E2E, or visual/runtime verification must explicitly include this requirement:
|
|
100
108
|
|
|
101
|
-
> Run Playwright/browser automation with a visible browser. Set `headless: false` in launch/config code, use headed test mode (`--headed`, `PWDEBUG=1`, or equivalent), prefer `channel: 'chrome'` when available, and do not use headless mode unless the Owner has explicitly approved an exception in this mission.
|
|
109
|
+
> Run Playwright/browser automation with a visible browser. Set `headless: false` in launch/config code, use headed test mode (`--headed`, `PWDEBUG=1`, or equivalent), prefer `channel: 'chrome'` when available, and do not use headless mode unless the Owner has explicitly approved an exception in this mission. Pace it middle-fast: `slowMo: 120` (ms) — observable but brisk. Do not use `slowMo: 300`+ (too slow); raise it only if the Owner explicitly asks to slow the demo down.
|
|
102
110
|
|
|
103
111
|
OPS must not accept worker plans or reports that omit this requirement when browser automation is in scope.
|
|
104
112
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: harness-ops-prompt-inspector
|
|
3
|
-
description: "OPS-only dev-mode visual API binding for React/Next.js UI elements and REST endpoints."
|
|
3
|
+
description: "OPS-only dev-mode visual API binding for React/Next.js or static HTML UI elements and REST endpoints."
|
|
4
4
|
permissionMode: bypassPermissions
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# OPS Prompt Inspector
|
|
8
8
|
|
|
9
|
-
OPS-only visual tool for binding UI elements to REST APIs in React/Next.js projects during development builds.
|
|
9
|
+
OPS-only visual tool for binding UI elements to REST APIs in React/Next.js or static HTML projects during development builds.
|
|
10
10
|
|
|
11
11
|
Use this skill only when OPS is responsible for a dev-mode build/run or verification environment where screen-level communication would be improved by an on-page inspector. Do not install it into production builds.
|
|
12
12
|
|
|
@@ -29,9 +29,9 @@ bash scripts/install.sh <project_path>
|
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
This automatically:
|
|
32
|
-
1. Detects project type (Next.js App/Pages Router)
|
|
33
|
-
2. Copies component to `components/PromptInspector.tsx`
|
|
34
|
-
3. Injects into root layout (dev mode only)
|
|
32
|
+
1. Detects project type (Next.js App/Pages Router or static `index.html`)
|
|
33
|
+
2. Copies component to `components/PromptInspector.tsx`, or static script to `public/prompt-inspector-static.js`
|
|
34
|
+
3. Injects into root layout or `index.html` (dev mode only)
|
|
35
35
|
4. Discovers all APIs in the project
|
|
36
36
|
|
|
37
37
|
## Workflow
|
|
@@ -85,7 +85,7 @@ After setup, toolbar appears at bottom-right in dev mode:
|
|
|
85
85
|
|
|
86
86
|
## Manual Setup (if auto fails)
|
|
87
87
|
|
|
88
|
-
1.
|
|
88
|
+
1. For React/Next.js, copy `assets/PromptInspector.tsx` to project's `components/`
|
|
89
89
|
|
|
90
90
|
2. Add to root layout:
|
|
91
91
|
```tsx
|
|
@@ -95,6 +95,11 @@ import { PromptInspector } from '@/components/PromptInspector';
|
|
|
95
95
|
{process.env.NODE_ENV === 'development' && <PromptInspector />}
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
+
3. For static HTML, copy `assets/prompt-inspector-static.js` to `public/` and add before `</body>`:
|
|
99
|
+
```html
|
|
100
|
+
<script src="/public/prompt-inspector-static.js" data-harness-ops-prompt-inspector></script>
|
|
101
|
+
```
|
|
102
|
+
|
|
98
103
|
## Handler Templates
|
|
99
104
|
|
|
100
105
|
See [references/handler-templates.md](references/handler-templates.md) for code generation patterns.
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
const VERSION = '1.2.0-static';
|
|
3
|
+
const ID = 'harness-ops-prompt-inspector';
|
|
4
|
+
if (window.__HARNESS_OPS_PROMPT_INSPECTOR__) return;
|
|
5
|
+
window.__HARNESS_OPS_PROMPT_INSPECTOR__ = { version: VERSION };
|
|
6
|
+
|
|
7
|
+
const state = {
|
|
8
|
+
selecting: false,
|
|
9
|
+
target: null,
|
|
10
|
+
bindings: [],
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const css = `
|
|
14
|
+
#${ID}-bar{position:fixed;right:18px;bottom:18px;z-index:2147483647;display:flex;gap:8px;align-items:center;background:#111827;color:#fff;border:1px solid #374151;border-radius:8px;padding:10px;box-shadow:0 16px 36px rgba(0,0,0,.32);font:13px/1.4 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}
|
|
15
|
+
#${ID}-bar button,#${ID}-panel button{border:0;border-radius:6px;padding:8px 10px;background:#2563eb;color:#fff;cursor:pointer;font:inherit}
|
|
16
|
+
#${ID}-bar button.secondary,#${ID}-panel button.secondary{background:#374151}
|
|
17
|
+
#${ID}-bar button.danger{background:#dc2626}
|
|
18
|
+
#${ID}-panel{position:fixed;right:18px;bottom:76px;width:min(420px,calc(100vw - 36px));z-index:2147483647;background:#fff;color:#111827;border:1px solid #d1d5db;border-radius:8px;box-shadow:0 18px 48px rgba(0,0,0,.28);padding:14px;font:13px/1.45 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}
|
|
19
|
+
#${ID}-panel h3{margin:0 0 10px;font-size:15px}
|
|
20
|
+
#${ID}-panel label{display:block;margin:10px 0 4px;color:#374151;font-weight:600}
|
|
21
|
+
#${ID}-panel input,#${ID}-panel textarea,#${ID}-panel select{width:100%;box-sizing:border-box;border:1px solid #d1d5db;border-radius:6px;padding:8px;font:inherit}
|
|
22
|
+
#${ID}-panel textarea{min-height:72px;resize:vertical}
|
|
23
|
+
#${ID}-panel .row{display:flex;gap:8px;margin-top:12px}
|
|
24
|
+
#${ID}-highlight{position:fixed;z-index:2147483646;pointer-events:none;border:2px solid #f59e0b;background:rgba(245,158,11,.12);border-radius:4px}
|
|
25
|
+
.${ID}-badge{position:absolute;z-index:2147483645;background:#f59e0b;color:#111827;border-radius:999px;padding:2px 7px;font:11px/1.4 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;box-shadow:0 2px 8px rgba(0,0,0,.24)}
|
|
26
|
+
`;
|
|
27
|
+
|
|
28
|
+
const style = document.createElement('style');
|
|
29
|
+
style.textContent = css;
|
|
30
|
+
document.head.appendChild(style);
|
|
31
|
+
|
|
32
|
+
const bar = document.createElement('div');
|
|
33
|
+
bar.id = `${ID}-bar`;
|
|
34
|
+
bar.innerHTML = `
|
|
35
|
+
<strong>Prompt Inspector</strong>
|
|
36
|
+
<button type="button" data-action="select">Select</button>
|
|
37
|
+
<button type="button" class="secondary" data-action="export">Export</button>
|
|
38
|
+
<button type="button" class="danger" data-action="clear">Clear</button>
|
|
39
|
+
`;
|
|
40
|
+
document.body.appendChild(bar);
|
|
41
|
+
|
|
42
|
+
const highlight = document.createElement('div');
|
|
43
|
+
highlight.id = `${ID}-highlight`;
|
|
44
|
+
highlight.hidden = true;
|
|
45
|
+
document.body.appendChild(highlight);
|
|
46
|
+
|
|
47
|
+
function selectorFor(el) {
|
|
48
|
+
if (!el || el === document.body) return 'body';
|
|
49
|
+
if (el.id) return `#${CSS.escape(el.id)}`;
|
|
50
|
+
const parts = [];
|
|
51
|
+
let node = el;
|
|
52
|
+
while (node && node.nodeType === 1 && node !== document.body && parts.length < 4) {
|
|
53
|
+
let part = node.tagName.toLowerCase();
|
|
54
|
+
if (node.classList && node.classList.length) {
|
|
55
|
+
part += `.${Array.from(node.classList).slice(0, 2).map((c) => CSS.escape(c)).join('.')}`;
|
|
56
|
+
}
|
|
57
|
+
const parent = node.parentElement;
|
|
58
|
+
if (parent) {
|
|
59
|
+
const same = Array.from(parent.children).filter((child) => child.tagName === node.tagName);
|
|
60
|
+
if (same.length > 1) part += `:nth-of-type(${same.indexOf(node) + 1})`;
|
|
61
|
+
}
|
|
62
|
+
parts.unshift(part);
|
|
63
|
+
node = parent;
|
|
64
|
+
}
|
|
65
|
+
return parts.join(' > ');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function positionHighlight(el) {
|
|
69
|
+
if (!el) {
|
|
70
|
+
highlight.hidden = true;
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const rect = el.getBoundingClientRect();
|
|
74
|
+
highlight.hidden = false;
|
|
75
|
+
highlight.style.left = `${rect.left}px`;
|
|
76
|
+
highlight.style.top = `${rect.top}px`;
|
|
77
|
+
highlight.style.width = `${rect.width}px`;
|
|
78
|
+
highlight.style.height = `${rect.height}px`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function panelFor(el) {
|
|
82
|
+
const selector = selectorFor(el);
|
|
83
|
+
const panel = document.createElement('div');
|
|
84
|
+
panel.id = `${ID}-panel`;
|
|
85
|
+
panel.innerHTML = `
|
|
86
|
+
<h3>Bind selected element</h3>
|
|
87
|
+
<label>Selector</label>
|
|
88
|
+
<input value="${selector.replace(/"/g, '"')}" data-field="selector">
|
|
89
|
+
<label>Mode</label>
|
|
90
|
+
<select data-field="mode">
|
|
91
|
+
<option value="comment">Comment Only</option>
|
|
92
|
+
<option value="api">Connect API</option>
|
|
93
|
+
</select>
|
|
94
|
+
<label>API / Note</label>
|
|
95
|
+
<input placeholder="GET /api/example or short title" data-field="api">
|
|
96
|
+
<label>Comment</label>
|
|
97
|
+
<textarea placeholder="Describe intended behavior, visual issue, or API binding." data-field="comment"></textarea>
|
|
98
|
+
<div class="row">
|
|
99
|
+
<button type="button" data-action="save">Save</button>
|
|
100
|
+
<button type="button" class="secondary" data-action="cancel">Cancel</button>
|
|
101
|
+
</div>
|
|
102
|
+
`;
|
|
103
|
+
document.body.appendChild(panel);
|
|
104
|
+
panel.querySelector('[data-action="save"]').addEventListener('click', () => {
|
|
105
|
+
const binding = {
|
|
106
|
+
route: `${location.pathname}${location.hash || ''}`,
|
|
107
|
+
selector: panel.querySelector('[data-field="selector"]').value,
|
|
108
|
+
mode: panel.querySelector('[data-field="mode"]').value,
|
|
109
|
+
api: panel.querySelector('[data-field="api"]').value.trim(),
|
|
110
|
+
comment: panel.querySelector('[data-field="comment"]').value.trim(),
|
|
111
|
+
};
|
|
112
|
+
state.bindings.push(binding);
|
|
113
|
+
addBadge(el, state.bindings.length);
|
|
114
|
+
panel.remove();
|
|
115
|
+
state.target = null;
|
|
116
|
+
positionHighlight(null);
|
|
117
|
+
});
|
|
118
|
+
panel.querySelector('[data-action="cancel"]').addEventListener('click', () => {
|
|
119
|
+
panel.remove();
|
|
120
|
+
state.target = null;
|
|
121
|
+
positionHighlight(null);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function addBadge(el, index) {
|
|
126
|
+
const rect = el.getBoundingClientRect();
|
|
127
|
+
const badge = document.createElement('div');
|
|
128
|
+
badge.className = `${ID}-badge`;
|
|
129
|
+
badge.textContent = `PI-${index}`;
|
|
130
|
+
badge.style.left = `${Math.max(6, rect.left + window.scrollX)}px`;
|
|
131
|
+
badge.style.top = `${Math.max(6, rect.top + window.scrollY - 20)}px`;
|
|
132
|
+
document.body.appendChild(badge);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function exportMarkdown() {
|
|
136
|
+
const lines = [`# Prompt Inspector`, ``, `Route: ${location.pathname}${location.hash || ''}`, ``];
|
|
137
|
+
state.bindings.forEach((binding, i) => {
|
|
138
|
+
lines.push(`## ${i + 1}. ${binding.mode === 'api' ? 'API Binding' : 'Comment'}`);
|
|
139
|
+
lines.push(`- Selector: \`${binding.selector}\``);
|
|
140
|
+
if (binding.api) lines.push(`- API: ${binding.api}`);
|
|
141
|
+
if (binding.comment) lines.push(`- Comment: ${binding.comment}`);
|
|
142
|
+
lines.push('');
|
|
143
|
+
});
|
|
144
|
+
const markdown = lines.join('\n');
|
|
145
|
+
navigator.clipboard?.writeText(markdown).catch(() => {});
|
|
146
|
+
console.log(markdown);
|
|
147
|
+
alert(`Prompt Inspector export copied/logged. Bindings: ${state.bindings.length}`);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
document.addEventListener('mouseover', (event) => {
|
|
151
|
+
if (!state.selecting || bar.contains(event.target)) return;
|
|
152
|
+
positionHighlight(event.target);
|
|
153
|
+
}, true);
|
|
154
|
+
|
|
155
|
+
document.addEventListener('click', (event) => {
|
|
156
|
+
if (!state.selecting || bar.contains(event.target)) return;
|
|
157
|
+
event.preventDefault();
|
|
158
|
+
event.stopPropagation();
|
|
159
|
+
state.selecting = false;
|
|
160
|
+
state.target = event.target;
|
|
161
|
+
bar.querySelector('[data-action="select"]').textContent = 'Select';
|
|
162
|
+
document.getElementById(`${ID}-panel`)?.remove();
|
|
163
|
+
panelFor(event.target);
|
|
164
|
+
}, true);
|
|
165
|
+
|
|
166
|
+
bar.addEventListener('click', (event) => {
|
|
167
|
+
const action = event.target && event.target.getAttribute('data-action');
|
|
168
|
+
if (action === 'select') {
|
|
169
|
+
state.selecting = !state.selecting;
|
|
170
|
+
event.target.textContent = state.selecting ? 'Selecting...' : 'Select';
|
|
171
|
+
if (!state.selecting) positionHighlight(null);
|
|
172
|
+
}
|
|
173
|
+
if (action === 'export') exportMarkdown();
|
|
174
|
+
if (action === 'clear') {
|
|
175
|
+
state.bindings = [];
|
|
176
|
+
document.querySelectorAll(`.${ID}-badge`).forEach((el) => el.remove());
|
|
177
|
+
positionHighlight(null);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
})();
|
|
@@ -34,6 +34,9 @@ def find_project_type(project_path: Path) -> dict:
|
|
|
34
34
|
'components_dir': None,
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
# Check for static/vanilla apps
|
|
38
|
+
static_index = project_path / 'index.html'
|
|
39
|
+
|
|
37
40
|
# Check for Next.js App Router
|
|
38
41
|
app_layout = project_path / 'app' / 'layout.tsx'
|
|
39
42
|
app_layout_js = project_path / 'app' / 'layout.js'
|
|
@@ -81,6 +84,10 @@ def find_project_type(project_path: Path) -> dict:
|
|
|
81
84
|
result['type'] = 'nextjs-pages'
|
|
82
85
|
result['layout_file'] = src_pages_app_js
|
|
83
86
|
result['components_dir'] = project_path / 'src' / 'components'
|
|
87
|
+
elif static_index.exists():
|
|
88
|
+
result['type'] = 'static-html'
|
|
89
|
+
result['layout_file'] = static_index
|
|
90
|
+
result['components_dir'] = project_path / 'public'
|
|
84
91
|
|
|
85
92
|
return result
|
|
86
93
|
|
|
@@ -227,6 +234,46 @@ def inject_into_layout(layout_file: Path, project_type: str) -> bool:
|
|
|
227
234
|
return True
|
|
228
235
|
|
|
229
236
|
|
|
237
|
+
def copy_static_inspector(public_dir: Path, script_dir: Path, force_update: bool = False) -> bool:
|
|
238
|
+
"""Copy the static Prompt Inspector browser script."""
|
|
239
|
+
public_dir.mkdir(parents=True, exist_ok=True)
|
|
240
|
+
source = script_dir.parent / 'assets' / 'prompt-inspector-static.js'
|
|
241
|
+
dest = public_dir / 'prompt-inspector-static.js'
|
|
242
|
+
if not source.exists():
|
|
243
|
+
print(f" Error: Source static inspector not found at {source}")
|
|
244
|
+
return False
|
|
245
|
+
if dest.exists() and not force_update:
|
|
246
|
+
print(f" Static inspector already exists at {dest}")
|
|
247
|
+
return True
|
|
248
|
+
shutil.copy(source, dest)
|
|
249
|
+
print(f" Installed static Prompt Inspector at {dest}")
|
|
250
|
+
return True
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def inject_into_static_html(index_file: Path) -> bool:
|
|
254
|
+
"""Inject the static inspector script into index.html."""
|
|
255
|
+
content = index_file.read_text(encoding='utf-8')
|
|
256
|
+
marker = 'data-harness-ops-prompt-inspector'
|
|
257
|
+
if marker in content:
|
|
258
|
+
print(f" Static inspector already injected in {index_file}")
|
|
259
|
+
return True
|
|
260
|
+
|
|
261
|
+
script_tag = ' <script src="/public/prompt-inspector-static.js" data-harness-ops-prompt-inspector></script>'
|
|
262
|
+
if '</body>' in content:
|
|
263
|
+
new_content = content.replace('</body>', f'{script_tag}\n</body>')
|
|
264
|
+
else:
|
|
265
|
+
new_content = content + f'\n{script_tag}\n'
|
|
266
|
+
|
|
267
|
+
backup_file = index_file.with_suffix(index_file.suffix + '.backup')
|
|
268
|
+
if not backup_file.exists():
|
|
269
|
+
shutil.copy(index_file, backup_file)
|
|
270
|
+
print(f" Backup created at {backup_file}")
|
|
271
|
+
|
|
272
|
+
index_file.write_text(new_content, encoding='utf-8')
|
|
273
|
+
print(f" Injected static inspector into {index_file}")
|
|
274
|
+
return True
|
|
275
|
+
|
|
276
|
+
|
|
230
277
|
def main():
|
|
231
278
|
if len(sys.argv) < 2:
|
|
232
279
|
print("Usage: python setup.py <project_path> [options]")
|
|
@@ -266,9 +313,9 @@ def main():
|
|
|
266
313
|
|
|
267
314
|
if not project_info['type']:
|
|
268
315
|
print("Could not detect project type.")
|
|
269
|
-
print("Supported: Next.js (App Router / Pages Router)")
|
|
270
|
-
print("\nManual setup required. Copy PromptInspector.tsx
|
|
271
|
-
print("and import it in your root layout/app file.")
|
|
316
|
+
print("Supported: Next.js (App Router / Pages Router), static index.html")
|
|
317
|
+
print("\nManual setup required. Copy PromptInspector.tsx or prompt-inspector-static.js")
|
|
318
|
+
print("and import it in your root layout/app file or index.html.")
|
|
272
319
|
return 1
|
|
273
320
|
|
|
274
321
|
print(f"Detected: {project_info['type']}")
|
|
@@ -277,6 +324,12 @@ def main():
|
|
|
277
324
|
|
|
278
325
|
# Check-only mode
|
|
279
326
|
if check_only:
|
|
327
|
+
if project_info['type'] == 'static-html':
|
|
328
|
+
dest = project_info['components_dir'] / 'prompt-inspector-static.js'
|
|
329
|
+
print(f"\n Static install check:")
|
|
330
|
+
print(f" └─ Installed: {'yes' if dest.exists() else 'no'}")
|
|
331
|
+
return 0 if dest.exists() else 1
|
|
332
|
+
|
|
280
333
|
source = script_dir.parent / 'assets' / 'PromptInspector.tsx'
|
|
281
334
|
dest = project_info['components_dir'] / 'PromptInspector.tsx'
|
|
282
335
|
|
|
@@ -299,6 +352,16 @@ def main():
|
|
|
299
352
|
|
|
300
353
|
print("\nSetting up Prompt Inspector...")
|
|
301
354
|
|
|
355
|
+
if project_info['type'] == 'static-html':
|
|
356
|
+
if not copy_static_inspector(project_info['components_dir'], script_dir, force_update):
|
|
357
|
+
return 1
|
|
358
|
+
if not inject_into_static_html(project_info['layout_file']):
|
|
359
|
+
return 1
|
|
360
|
+
print("\nSetup complete!")
|
|
361
|
+
print("Run your dev server and look for the toolbar in the bottom-right corner.")
|
|
362
|
+
print("The static inspector is intended for development servers only.")
|
|
363
|
+
return 0
|
|
364
|
+
|
|
302
365
|
# Step 1: Copy component
|
|
303
366
|
if not copy_component(project_info['components_dir'], script_dir, force_update):
|
|
304
367
|
return 1
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { sampleMetrics } from "@/lib/metrics/sampler";
|
|
3
|
+
import { resolveHarnessRoot } from "@/lib/harness-root";
|
|
4
|
+
|
|
5
|
+
// node:fs + ps/tmux child_process require the Node.js runtime (not Edge).
|
|
6
|
+
export const runtime = "nodejs";
|
|
7
|
+
export const dynamic = "force-dynamic";
|
|
8
|
+
export const revalidate = 0;
|
|
9
|
+
|
|
10
|
+
export async function GET() {
|
|
11
|
+
const root = resolveHarnessRoot();
|
|
12
|
+
const sample = await sampleMetrics(root);
|
|
13
|
+
return NextResponse.json(sample, {
|
|
14
|
+
status: 200,
|
|
15
|
+
headers: { "Cache-Control": "no-store" },
|
|
16
|
+
});
|
|
17
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { execFileSync } from "node:child_process";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { resolveHarnessRoot } from "@/lib/harness-root";
|
|
6
|
+
|
|
7
|
+
export const dynamic = "force-dynamic";
|
|
8
|
+
|
|
9
|
+
// Per-project hourly-wake launchd control, keyed by HARNESS_BASE_PORT. The
|
|
10
|
+
// dashboard runs locally for the Owner, so it may toggle the Owner's own
|
|
11
|
+
// launchd job. Only the fixed subcommands status|on|off are ever invoked.
|
|
12
|
+
function runControl(action: "status" | "on" | "off") {
|
|
13
|
+
const root = resolveHarnessRoot();
|
|
14
|
+
const script = path.join(root, "scripts", "harness-wake-control.sh");
|
|
15
|
+
if (!existsSync(script)) {
|
|
16
|
+
return { supported: false, enabled: false, error: "harness-wake-control.sh not installed in this project" };
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
const out = execFileSync("bash", [script, action, root], {
|
|
20
|
+
encoding: "utf8",
|
|
21
|
+
timeout: 20000,
|
|
22
|
+
});
|
|
23
|
+
const lastLine = out.trim().split("\n").filter(Boolean).pop() ?? "{}";
|
|
24
|
+
return JSON.parse(lastLine);
|
|
25
|
+
} catch (e) {
|
|
26
|
+
return { supported: false, enabled: false, error: (e as Error).message };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function GET() {
|
|
31
|
+
return NextResponse.json(runControl("status"));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function POST(req: Request) {
|
|
35
|
+
const body = (await req.json().catch(() => ({}))) as { action?: string };
|
|
36
|
+
const action = body.action === "on" ? "on" : body.action === "off" ? "off" : null;
|
|
37
|
+
if (!action) {
|
|
38
|
+
return NextResponse.json({ error: "action must be 'on' or 'off'" }, { status: 400 });
|
|
39
|
+
}
|
|
40
|
+
return NextResponse.json(runControl(action));
|
|
41
|
+
}
|