@cluesmith/codev 2.0.0-rc.9 → 2.0.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/bin/af.js +2 -2
- package/bin/consult.js +1 -1
- package/dashboard/dist/assets/index-4n9zpWLY.css +32 -0
- package/dashboard/dist/assets/index-b38SaXk5.js +136 -0
- package/dashboard/dist/assets/index-b38SaXk5.js.map +1 -0
- package/dashboard/dist/index.html +14 -0
- package/dist/agent-farm/cli.d.ts.map +1 -1
- package/dist/agent-farm/cli.js +179 -104
- package/dist/agent-farm/cli.js.map +1 -1
- package/dist/agent-farm/commands/architect.d.ts +3 -3
- package/dist/agent-farm/commands/architect.d.ts.map +1 -1
- package/dist/agent-farm/commands/architect.js +20 -147
- package/dist/agent-farm/commands/architect.js.map +1 -1
- package/dist/agent-farm/commands/attach.d.ts +13 -0
- package/dist/agent-farm/commands/attach.d.ts.map +1 -0
- package/dist/agent-farm/commands/attach.js +144 -0
- package/dist/agent-farm/commands/attach.js.map +1 -0
- package/dist/agent-farm/commands/cleanup.d.ts.map +1 -1
- package/dist/agent-farm/commands/cleanup.js +35 -19
- package/dist/agent-farm/commands/cleanup.js.map +1 -1
- package/dist/agent-farm/commands/consult.d.ts +3 -4
- package/dist/agent-farm/commands/consult.d.ts.map +1 -1
- package/dist/agent-farm/commands/consult.js +27 -37
- package/dist/agent-farm/commands/consult.js.map +1 -1
- package/dist/agent-farm/commands/index.d.ts +2 -2
- package/dist/agent-farm/commands/index.d.ts.map +1 -1
- package/dist/agent-farm/commands/index.js +2 -2
- package/dist/agent-farm/commands/index.js.map +1 -1
- package/dist/agent-farm/commands/open.d.ts +4 -2
- package/dist/agent-farm/commands/open.d.ts.map +1 -1
- package/dist/agent-farm/commands/open.js +33 -83
- package/dist/agent-farm/commands/open.js.map +1 -1
- package/dist/agent-farm/commands/send.d.ts +1 -1
- package/dist/agent-farm/commands/send.d.ts.map +1 -1
- package/dist/agent-farm/commands/send.js +70 -79
- package/dist/agent-farm/commands/send.js.map +1 -1
- package/dist/agent-farm/commands/shell.d.ts +15 -0
- package/dist/agent-farm/commands/shell.d.ts.map +1 -0
- package/dist/agent-farm/commands/shell.js +50 -0
- package/dist/agent-farm/commands/shell.js.map +1 -0
- package/dist/agent-farm/commands/spawn-roles.d.ts +80 -0
- package/dist/agent-farm/commands/spawn-roles.d.ts.map +1 -0
- package/dist/agent-farm/commands/spawn-roles.js +278 -0
- package/dist/agent-farm/commands/spawn-roles.js.map +1 -0
- package/dist/agent-farm/commands/spawn-worktree.d.ts +96 -0
- package/dist/agent-farm/commands/spawn-worktree.d.ts.map +1 -0
- package/dist/agent-farm/commands/spawn-worktree.js +305 -0
- package/dist/agent-farm/commands/spawn-worktree.js.map +1 -0
- package/dist/agent-farm/commands/spawn.d.ts +5 -1
- package/dist/agent-farm/commands/spawn.d.ts.map +1 -1
- package/dist/agent-farm/commands/spawn.js +241 -561
- package/dist/agent-farm/commands/spawn.js.map +1 -1
- package/dist/agent-farm/commands/start.d.ts +10 -20
- package/dist/agent-farm/commands/start.d.ts.map +1 -1
- package/dist/agent-farm/commands/start.js +45 -449
- package/dist/agent-farm/commands/start.js.map +1 -1
- package/dist/agent-farm/commands/status.d.ts +2 -0
- package/dist/agent-farm/commands/status.d.ts.map +1 -1
- package/dist/agent-farm/commands/status.js +75 -24
- package/dist/agent-farm/commands/status.js.map +1 -1
- package/dist/agent-farm/commands/stop.d.ts +6 -0
- package/dist/agent-farm/commands/stop.d.ts.map +1 -1
- package/dist/agent-farm/commands/stop.js +49 -109
- package/dist/agent-farm/commands/stop.js.map +1 -1
- package/dist/agent-farm/commands/tower-cloud.d.ts +48 -0
- package/dist/agent-farm/commands/tower-cloud.d.ts.map +1 -0
- package/dist/agent-farm/commands/tower-cloud.js +293 -0
- package/dist/agent-farm/commands/tower-cloud.js.map +1 -0
- package/dist/agent-farm/commands/tower.d.ts +9 -0
- package/dist/agent-farm/commands/tower.d.ts.map +1 -1
- package/dist/agent-farm/commands/tower.js +59 -19
- package/dist/agent-farm/commands/tower.js.map +1 -1
- package/dist/agent-farm/db/index.d.ts +6 -2
- package/dist/agent-farm/db/index.d.ts.map +1 -1
- package/dist/agent-farm/db/index.js +301 -19
- package/dist/agent-farm/db/index.js.map +1 -1
- package/dist/agent-farm/db/migrate.d.ts +0 -4
- package/dist/agent-farm/db/migrate.d.ts.map +1 -1
- package/dist/agent-farm/db/migrate.js +6 -55
- package/dist/agent-farm/db/migrate.js.map +1 -1
- package/dist/agent-farm/db/schema.d.ts +3 -3
- package/dist/agent-farm/db/schema.d.ts.map +1 -1
- package/dist/agent-farm/db/schema.js +25 -19
- package/dist/agent-farm/db/schema.js.map +1 -1
- package/dist/agent-farm/db/types.d.ts +3 -13
- package/dist/agent-farm/db/types.d.ts.map +1 -1
- package/dist/agent-farm/db/types.js +3 -11
- package/dist/agent-farm/db/types.js.map +1 -1
- package/dist/agent-farm/hq-connector.d.ts +2 -6
- package/dist/agent-farm/hq-connector.d.ts.map +1 -1
- package/dist/agent-farm/hq-connector.js +2 -17
- package/dist/agent-farm/hq-connector.js.map +1 -1
- package/dist/agent-farm/lib/cloud-config.d.ts +59 -0
- package/dist/agent-farm/lib/cloud-config.d.ts.map +1 -0
- package/dist/agent-farm/lib/cloud-config.js +143 -0
- package/dist/agent-farm/lib/cloud-config.js.map +1 -0
- package/dist/agent-farm/lib/device-name.d.ts +25 -0
- package/dist/agent-farm/lib/device-name.d.ts.map +1 -0
- package/dist/agent-farm/lib/device-name.js +46 -0
- package/dist/agent-farm/lib/device-name.js.map +1 -0
- package/dist/agent-farm/lib/nonce-store.d.ts +28 -0
- package/dist/agent-farm/lib/nonce-store.d.ts.map +1 -0
- package/dist/agent-farm/lib/nonce-store.js +60 -0
- package/dist/agent-farm/lib/nonce-store.js.map +1 -0
- package/dist/agent-farm/lib/token-exchange.d.ts +18 -0
- package/dist/agent-farm/lib/token-exchange.d.ts.map +1 -0
- package/dist/agent-farm/lib/token-exchange.js +48 -0
- package/dist/agent-farm/lib/token-exchange.js.map +1 -0
- package/dist/agent-farm/lib/tower-client.d.ts +163 -0
- package/dist/agent-farm/lib/tower-client.d.ts.map +1 -0
- package/dist/agent-farm/lib/tower-client.js +233 -0
- package/dist/agent-farm/lib/tower-client.js.map +1 -0
- package/dist/agent-farm/lib/tunnel-client.d.ts +117 -0
- package/dist/agent-farm/lib/tunnel-client.d.ts.map +1 -0
- package/dist/agent-farm/lib/tunnel-client.js +504 -0
- package/dist/agent-farm/lib/tunnel-client.js.map +1 -0
- package/dist/agent-farm/servers/tower-instances.d.ts +82 -0
- package/dist/agent-farm/servers/tower-instances.d.ts.map +1 -0
- package/dist/agent-farm/servers/tower-instances.js +454 -0
- package/dist/agent-farm/servers/tower-instances.js.map +1 -0
- package/dist/agent-farm/servers/tower-routes.d.ts +34 -0
- package/dist/agent-farm/servers/tower-routes.d.ts.map +1 -0
- package/dist/agent-farm/servers/tower-routes.js +1445 -0
- package/dist/agent-farm/servers/tower-routes.js.map +1 -0
- package/dist/agent-farm/servers/tower-server.d.ts +5 -2
- package/dist/agent-farm/servers/tower-server.d.ts.map +1 -1
- package/dist/agent-farm/servers/tower-server.js +157 -475
- package/dist/agent-farm/servers/tower-server.js.map +1 -1
- package/dist/agent-farm/servers/tower-terminals.d.ts +119 -0
- package/dist/agent-farm/servers/tower-terminals.d.ts.map +1 -0
- package/dist/agent-farm/servers/tower-terminals.js +629 -0
- package/dist/agent-farm/servers/tower-terminals.js.map +1 -0
- package/dist/agent-farm/servers/tower-tunnel.d.ts +34 -0
- package/dist/agent-farm/servers/tower-tunnel.d.ts.map +1 -0
- package/dist/agent-farm/servers/tower-tunnel.js +480 -0
- package/dist/agent-farm/servers/tower-tunnel.js.map +1 -0
- package/dist/agent-farm/servers/tower-types.d.ts +86 -0
- package/dist/agent-farm/servers/tower-types.d.ts.map +1 -0
- package/dist/agent-farm/servers/tower-types.js +6 -0
- package/dist/agent-farm/servers/tower-types.js.map +1 -0
- package/dist/agent-farm/servers/tower-utils.d.ts +58 -0
- package/dist/agent-farm/servers/tower-utils.d.ts.map +1 -0
- package/dist/agent-farm/servers/tower-utils.js +182 -0
- package/dist/agent-farm/servers/tower-utils.js.map +1 -0
- package/dist/agent-farm/servers/tower-websocket.d.ts +25 -0
- package/dist/agent-farm/servers/tower-websocket.d.ts.map +1 -0
- package/dist/agent-farm/servers/tower-websocket.js +171 -0
- package/dist/agent-farm/servers/tower-websocket.js.map +1 -0
- package/dist/agent-farm/state.d.ts +6 -2
- package/dist/agent-farm/state.d.ts.map +1 -1
- package/dist/agent-farm/state.js +34 -25
- package/dist/agent-farm/state.js.map +1 -1
- package/dist/agent-farm/types.d.ts +49 -26
- package/dist/agent-farm/types.d.ts.map +1 -1
- package/dist/agent-farm/utils/config.d.ts +0 -5
- package/dist/agent-farm/utils/config.d.ts.map +1 -1
- package/dist/agent-farm/utils/config.js +12 -44
- package/dist/agent-farm/utils/config.js.map +1 -1
- package/dist/agent-farm/utils/deps.d.ts.map +1 -1
- package/dist/agent-farm/utils/deps.js +0 -32
- package/dist/agent-farm/utils/deps.js.map +1 -1
- package/dist/agent-farm/utils/file-tabs.d.ts +27 -0
- package/dist/agent-farm/utils/file-tabs.d.ts.map +1 -0
- package/dist/agent-farm/utils/file-tabs.js +46 -0
- package/dist/agent-farm/utils/file-tabs.js.map +1 -0
- package/dist/agent-farm/utils/gate-status.d.ts +16 -0
- package/dist/agent-farm/utils/gate-status.d.ts.map +1 -0
- package/dist/agent-farm/utils/gate-status.js +79 -0
- package/dist/agent-farm/utils/gate-status.js.map +1 -0
- package/dist/agent-farm/utils/gate-watcher.d.ts +38 -0
- package/dist/agent-farm/utils/gate-watcher.d.ts.map +1 -0
- package/dist/agent-farm/utils/gate-watcher.js +122 -0
- package/dist/agent-farm/utils/gate-watcher.js.map +1 -0
- package/dist/agent-farm/utils/index.d.ts +0 -1
- package/dist/agent-farm/utils/index.d.ts.map +1 -1
- package/dist/agent-farm/utils/index.js +0 -1
- package/dist/agent-farm/utils/index.js.map +1 -1
- package/dist/agent-farm/utils/notifications.d.ts +30 -0
- package/dist/agent-farm/utils/notifications.d.ts.map +1 -0
- package/dist/agent-farm/utils/notifications.js +121 -0
- package/dist/agent-farm/utils/notifications.js.map +1 -0
- package/dist/agent-farm/utils/server-utils.d.ts +5 -5
- package/dist/agent-farm/utils/server-utils.d.ts.map +1 -1
- package/dist/agent-farm/utils/server-utils.js +5 -16
- package/dist/agent-farm/utils/server-utils.js.map +1 -1
- package/dist/agent-farm/utils/session.d.ts +32 -0
- package/dist/agent-farm/utils/session.d.ts.map +1 -0
- package/dist/agent-farm/utils/session.js +57 -0
- package/dist/agent-farm/utils/session.js.map +1 -0
- package/dist/agent-farm/utils/shell.d.ts +9 -22
- package/dist/agent-farm/utils/shell.d.ts.map +1 -1
- package/dist/agent-farm/utils/shell.js +34 -34
- package/dist/agent-farm/utils/shell.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +6 -37
- package/dist/cli.js.map +1 -1
- package/dist/commands/adopt.d.ts.map +1 -1
- package/dist/commands/adopt.js +33 -4
- package/dist/commands/adopt.js.map +1 -1
- package/dist/commands/consult/index.d.ts +13 -2
- package/dist/commands/consult/index.d.ts.map +1 -1
- package/dist/commands/consult/index.js +244 -29
- package/dist/commands/consult/index.js.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +96 -79
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +36 -3
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/porch/build-counter.d.ts +5 -0
- package/dist/commands/porch/build-counter.d.ts.map +1 -0
- package/dist/commands/porch/build-counter.js +5 -0
- package/dist/commands/porch/build-counter.js.map +1 -0
- package/dist/commands/porch/checks.d.ts +3 -2
- package/dist/commands/porch/checks.d.ts.map +1 -1
- package/dist/commands/porch/checks.js +8 -2
- package/dist/commands/porch/checks.js.map +1 -1
- package/dist/commands/porch/index.d.ts +4 -0
- package/dist/commands/porch/index.d.ts.map +1 -1
- package/dist/commands/porch/index.js +109 -70
- package/dist/commands/porch/index.js.map +1 -1
- package/dist/commands/porch/next.d.ts +22 -0
- package/dist/commands/porch/next.d.ts.map +1 -0
- package/dist/commands/porch/next.js +571 -0
- package/dist/commands/porch/next.js.map +1 -0
- package/dist/commands/porch/plan.d.ts +11 -1
- package/dist/commands/porch/plan.d.ts.map +1 -1
- package/dist/commands/porch/plan.js +33 -5
- package/dist/commands/porch/plan.js.map +1 -1
- package/dist/commands/porch/prompts.d.ts.map +1 -1
- package/dist/commands/porch/prompts.js +44 -26
- package/dist/commands/porch/prompts.js.map +1 -1
- package/dist/commands/porch/protocol.d.ts +6 -4
- package/dist/commands/porch/protocol.d.ts.map +1 -1
- package/dist/commands/porch/protocol.js +59 -15
- package/dist/commands/porch/protocol.js.map +1 -1
- package/dist/commands/porch/state.d.ts +29 -2
- package/dist/commands/porch/state.d.ts.map +1 -1
- package/dist/commands/porch/state.js +71 -3
- package/dist/commands/porch/state.js.map +1 -1
- package/dist/commands/porch/types.d.ts +45 -2
- package/dist/commands/porch/types.d.ts.map +1 -1
- package/dist/commands/porch/verdict.d.ts +31 -0
- package/dist/commands/porch/verdict.d.ts.map +1 -0
- package/dist/commands/porch/verdict.js +59 -0
- package/dist/commands/porch/verdict.js.map +1 -0
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +18 -6
- package/dist/commands/update.js.map +1 -1
- package/dist/lib/scaffold.d.ts +13 -0
- package/dist/lib/scaffold.d.ts.map +1 -1
- package/dist/lib/scaffold.js +36 -0
- package/dist/lib/scaffold.js.map +1 -1
- package/dist/terminal/index.d.ts +8 -0
- package/dist/terminal/index.d.ts.map +1 -0
- package/dist/terminal/index.js +5 -0
- package/dist/terminal/index.js.map +1 -0
- package/dist/terminal/pty-manager.d.ts +69 -0
- package/dist/terminal/pty-manager.d.ts.map +1 -0
- package/dist/terminal/pty-manager.js +377 -0
- package/dist/terminal/pty-manager.js.map +1 -0
- package/dist/terminal/pty-session.d.ts +104 -0
- package/dist/terminal/pty-session.d.ts.map +1 -0
- package/dist/terminal/pty-session.js +327 -0
- package/dist/terminal/pty-session.js.map +1 -0
- package/dist/terminal/ring-buffer.d.ts +34 -0
- package/dist/terminal/ring-buffer.d.ts.map +1 -0
- package/dist/terminal/ring-buffer.js +94 -0
- package/dist/terminal/ring-buffer.js.map +1 -0
- package/dist/terminal/session-manager.d.ts +115 -0
- package/dist/terminal/session-manager.d.ts.map +1 -0
- package/dist/terminal/session-manager.js +582 -0
- package/dist/terminal/session-manager.js.map +1 -0
- package/dist/terminal/shellper-client.d.ts +66 -0
- package/dist/terminal/shellper-client.d.ts.map +1 -0
- package/dist/terminal/shellper-client.js +234 -0
- package/dist/terminal/shellper-client.js.map +1 -0
- package/dist/terminal/shellper-main.d.ts +19 -0
- package/dist/terminal/shellper-main.d.ts.map +1 -0
- package/dist/terminal/shellper-main.js +153 -0
- package/dist/terminal/shellper-main.js.map +1 -0
- package/dist/terminal/shellper-process.d.ts +75 -0
- package/dist/terminal/shellper-process.d.ts.map +1 -0
- package/dist/terminal/shellper-process.js +279 -0
- package/dist/terminal/shellper-process.js.map +1 -0
- package/dist/terminal/shellper-protocol.d.ts +115 -0
- package/dist/terminal/shellper-protocol.d.ts.map +1 -0
- package/dist/terminal/shellper-protocol.js +214 -0
- package/dist/terminal/shellper-protocol.js.map +1 -0
- package/dist/terminal/shellper-replay-buffer.d.ts +38 -0
- package/dist/terminal/shellper-replay-buffer.d.ts.map +1 -0
- package/dist/terminal/shellper-replay-buffer.js +94 -0
- package/dist/terminal/shellper-replay-buffer.js.map +1 -0
- package/dist/terminal/ws-protocol.d.ts +27 -0
- package/dist/terminal/ws-protocol.d.ts.map +1 -0
- package/dist/terminal/ws-protocol.js +44 -0
- package/dist/terminal/ws-protocol.js.map +1 -0
- package/package.json +17 -5
- package/skeleton/.claude/skills/af/SKILL.md +89 -0
- package/skeleton/.claude/skills/codev/SKILL.md +41 -0
- package/skeleton/.claude/skills/consult/SKILL.md +81 -0
- package/skeleton/.claude/skills/generate-image/SKILL.md +56 -0
- package/skeleton/DEPENDENCIES.md +4 -62
- package/skeleton/builders.md +1 -1
- package/skeleton/consult-types/impl-review.md +18 -9
- package/skeleton/consult-types/integration-review.md +1 -1
- package/skeleton/consult-types/plan-review.md +1 -1
- package/skeleton/consult-types/pr-ready.md +1 -1
- package/skeleton/consult-types/spec-review.md +1 -1
- package/skeleton/porch/prompts/defend.md +1 -1
- package/skeleton/porch/prompts/evaluate.md +2 -2
- package/skeleton/porch/prompts/implement.md +1 -1
- package/skeleton/porch/prompts/plan.md +1 -1
- package/skeleton/porch/prompts/review.md +4 -4
- package/skeleton/porch/prompts/specify.md +1 -1
- package/skeleton/porch/prompts/understand.md +2 -2
- package/skeleton/protocol-schema.json +282 -0
- package/skeleton/protocols/bugfix/builder-prompt.md +60 -0
- package/skeleton/protocols/bugfix/prompts/fix.md +77 -0
- package/skeleton/protocols/bugfix/prompts/investigate.md +77 -0
- package/skeleton/protocols/bugfix/prompts/pr.md +84 -0
- package/skeleton/protocols/bugfix/protocol.json +20 -33
- package/skeleton/protocols/experiment/builder-prompt.md +52 -0
- package/skeleton/protocols/experiment/protocol.json +101 -0
- package/skeleton/protocols/experiment/protocol.md +3 -3
- package/skeleton/protocols/experiment/templates/notes.md +1 -1
- package/skeleton/protocols/maintain/builder-prompt.md +46 -0
- package/skeleton/protocols/maintain/prompts/audit.md +111 -0
- package/skeleton/protocols/maintain/prompts/clean.md +91 -0
- package/skeleton/protocols/maintain/prompts/sync.md +113 -0
- package/skeleton/protocols/maintain/prompts/verify.md +110 -0
- package/skeleton/protocols/maintain/protocol.json +141 -0
- package/skeleton/protocols/maintain/protocol.md +17 -11
- package/skeleton/protocols/protocol-schema.json +54 -1
- package/skeleton/protocols/spir/builder-prompt.md +66 -0
- package/skeleton/protocols/spir/prompts/implement.md +208 -0
- package/skeleton/protocols/{spider → spir}/prompts/plan.md +6 -70
- package/skeleton/protocols/{spider → spir}/prompts/review.md +20 -39
- package/skeleton/protocols/{spider → spir}/prompts/specify.md +24 -59
- package/skeleton/protocols/{spider → spir}/protocol.json +30 -10
- package/skeleton/protocols/{spider → spir}/protocol.md +35 -21
- package/skeleton/protocols/spir/templates/review.md +89 -0
- package/skeleton/protocols/tick/builder-prompt.md +56 -0
- package/skeleton/protocols/tick/protocol.json +7 -2
- package/skeleton/protocols/tick/protocol.md +18 -18
- package/skeleton/protocols/tick/templates/review.md +1 -1
- package/skeleton/resources/commands/agent-farm.md +63 -46
- package/skeleton/resources/commands/codev.md +0 -2
- package/skeleton/resources/commands/overview.md +7 -17
- package/skeleton/resources/workflow-reference.md +4 -4
- package/skeleton/roles/architect.md +151 -306
- package/skeleton/roles/builder.md +115 -332
- package/skeleton/roles/consultant.md +6 -6
- package/skeleton/templates/AGENTS.md +2 -2
- package/skeleton/templates/CLAUDE.md +2 -2
- package/skeleton/templates/cheatsheet.md +7 -5
- package/skeleton/templates/projectlist.md +1 -1
- package/templates/dashboard/index.html +17 -16
- package/templates/dashboard/js/dialogs.js +7 -7
- package/templates/dashboard/js/files.js +2 -2
- package/templates/dashboard/js/main.js +4 -4
- package/templates/dashboard/js/projects.js +3 -3
- package/templates/dashboard/js/tabs.js +1 -1
- package/templates/dashboard/js/utils.js +22 -1
- package/templates/open.html +26 -0
- package/templates/tower.html +731 -91
- package/dist/agent-farm/commands/kickoff.d.ts +0 -20
- package/dist/agent-farm/commands/kickoff.d.ts.map +0 -1
- package/dist/agent-farm/commands/kickoff.js +0 -273
- package/dist/agent-farm/commands/kickoff.js.map +0 -1
- package/dist/agent-farm/commands/rename.d.ts +0 -13
- package/dist/agent-farm/commands/rename.d.ts.map +0 -1
- package/dist/agent-farm/commands/rename.js +0 -33
- package/dist/agent-farm/commands/rename.js.map +0 -1
- package/dist/agent-farm/commands/tutorial.d.ts +0 -10
- package/dist/agent-farm/commands/tutorial.d.ts.map +0 -1
- package/dist/agent-farm/commands/tutorial.js +0 -49
- package/dist/agent-farm/commands/tutorial.js.map +0 -1
- package/dist/agent-farm/commands/util.d.ts +0 -15
- package/dist/agent-farm/commands/util.d.ts.map +0 -1
- package/dist/agent-farm/commands/util.js +0 -108
- package/dist/agent-farm/commands/util.js.map +0 -1
- package/dist/agent-farm/servers/dashboard-server.d.ts +0 -7
- package/dist/agent-farm/servers/dashboard-server.d.ts.map +0 -1
- package/dist/agent-farm/servers/dashboard-server.js +0 -1858
- package/dist/agent-farm/servers/dashboard-server.js.map +0 -1
- package/dist/agent-farm/servers/open-server.d.ts +0 -7
- package/dist/agent-farm/servers/open-server.d.ts.map +0 -1
- package/dist/agent-farm/servers/open-server.js +0 -315
- package/dist/agent-farm/servers/open-server.js.map +0 -1
- package/dist/agent-farm/tutorial/index.d.ts +0 -8
- package/dist/agent-farm/tutorial/index.d.ts.map +0 -1
- package/dist/agent-farm/tutorial/index.js +0 -8
- package/dist/agent-farm/tutorial/index.js.map +0 -1
- package/dist/agent-farm/tutorial/prompts.d.ts +0 -57
- package/dist/agent-farm/tutorial/prompts.d.ts.map +0 -1
- package/dist/agent-farm/tutorial/prompts.js +0 -147
- package/dist/agent-farm/tutorial/prompts.js.map +0 -1
- package/dist/agent-farm/tutorial/runner.d.ts +0 -52
- package/dist/agent-farm/tutorial/runner.d.ts.map +0 -1
- package/dist/agent-farm/tutorial/runner.js +0 -204
- package/dist/agent-farm/tutorial/runner.js.map +0 -1
- package/dist/agent-farm/tutorial/state.d.ts +0 -26
- package/dist/agent-farm/tutorial/state.d.ts.map +0 -1
- package/dist/agent-farm/tutorial/state.js +0 -89
- package/dist/agent-farm/tutorial/state.js.map +0 -1
- package/dist/agent-farm/tutorial/steps/first-spec.d.ts +0 -7
- package/dist/agent-farm/tutorial/steps/first-spec.d.ts.map +0 -1
- package/dist/agent-farm/tutorial/steps/first-spec.js +0 -136
- package/dist/agent-farm/tutorial/steps/first-spec.js.map +0 -1
- package/dist/agent-farm/tutorial/steps/implementation.d.ts +0 -7
- package/dist/agent-farm/tutorial/steps/implementation.d.ts.map +0 -1
- package/dist/agent-farm/tutorial/steps/implementation.js +0 -76
- package/dist/agent-farm/tutorial/steps/implementation.js.map +0 -1
- package/dist/agent-farm/tutorial/steps/index.d.ts +0 -10
- package/dist/agent-farm/tutorial/steps/index.d.ts.map +0 -1
- package/dist/agent-farm/tutorial/steps/index.js +0 -10
- package/dist/agent-farm/tutorial/steps/index.js.map +0 -1
- package/dist/agent-farm/tutorial/steps/planning.d.ts +0 -7
- package/dist/agent-farm/tutorial/steps/planning.d.ts.map +0 -1
- package/dist/agent-farm/tutorial/steps/planning.js +0 -143
- package/dist/agent-farm/tutorial/steps/planning.js.map +0 -1
- package/dist/agent-farm/tutorial/steps/review.d.ts +0 -7
- package/dist/agent-farm/tutorial/steps/review.d.ts.map +0 -1
- package/dist/agent-farm/tutorial/steps/review.js +0 -78
- package/dist/agent-farm/tutorial/steps/review.js.map +0 -1
- package/dist/agent-farm/tutorial/steps/setup.d.ts +0 -7
- package/dist/agent-farm/tutorial/steps/setup.d.ts.map +0 -1
- package/dist/agent-farm/tutorial/steps/setup.js +0 -126
- package/dist/agent-farm/tutorial/steps/setup.js.map +0 -1
- package/dist/agent-farm/tutorial/steps/welcome.d.ts +0 -7
- package/dist/agent-farm/tutorial/steps/welcome.d.ts.map +0 -1
- package/dist/agent-farm/tutorial/steps/welcome.js +0 -50
- package/dist/agent-farm/tutorial/steps/welcome.js.map +0 -1
- package/dist/agent-farm/utils/orphan-handler.d.ts +0 -27
- package/dist/agent-farm/utils/orphan-handler.d.ts.map +0 -1
- package/dist/agent-farm/utils/orphan-handler.js +0 -149
- package/dist/agent-farm/utils/orphan-handler.js.map +0 -1
- package/dist/agent-farm/utils/port-registry.d.ts +0 -58
- package/dist/agent-farm/utils/port-registry.d.ts.map +0 -1
- package/dist/agent-farm/utils/port-registry.js +0 -166
- package/dist/agent-farm/utils/port-registry.js.map +0 -1
- package/dist/agent-farm/utils/terminal-ports.d.ts +0 -18
- package/dist/agent-farm/utils/terminal-ports.d.ts.map +0 -1
- package/dist/agent-farm/utils/terminal-ports.js +0 -35
- package/dist/agent-farm/utils/terminal-ports.js.map +0 -1
- package/dist/commands/pcheck/cache.d.ts +0 -48
- package/dist/commands/pcheck/cache.d.ts.map +0 -1
- package/dist/commands/pcheck/cache.js +0 -170
- package/dist/commands/pcheck/cache.js.map +0 -1
- package/dist/commands/pcheck/evaluator.d.ts +0 -15
- package/dist/commands/pcheck/evaluator.d.ts.map +0 -1
- package/dist/commands/pcheck/evaluator.js +0 -246
- package/dist/commands/pcheck/evaluator.js.map +0 -1
- package/dist/commands/pcheck/index.d.ts +0 -12
- package/dist/commands/pcheck/index.d.ts.map +0 -1
- package/dist/commands/pcheck/index.js +0 -249
- package/dist/commands/pcheck/index.js.map +0 -1
- package/dist/commands/pcheck/parser.d.ts +0 -39
- package/dist/commands/pcheck/parser.d.ts.map +0 -1
- package/dist/commands/pcheck/parser.js +0 -155
- package/dist/commands/pcheck/parser.js.map +0 -1
- package/dist/commands/pcheck/types.d.ts +0 -82
- package/dist/commands/pcheck/types.d.ts.map +0 -1
- package/dist/commands/pcheck/types.js +0 -5
- package/dist/commands/pcheck/types.js.map +0 -1
- package/dist/commands/porch/claude.d.ts +0 -29
- package/dist/commands/porch/claude.d.ts.map +0 -1
- package/dist/commands/porch/claude.js +0 -79
- package/dist/commands/porch/claude.js.map +0 -1
- package/dist/commands/porch/consultation.d.ts +0 -56
- package/dist/commands/porch/consultation.d.ts.map +0 -1
- package/dist/commands/porch/consultation.js +0 -330
- package/dist/commands/porch/consultation.js.map +0 -1
- package/dist/commands/porch/notifications.d.ts +0 -99
- package/dist/commands/porch/notifications.d.ts.map +0 -1
- package/dist/commands/porch/notifications.js +0 -223
- package/dist/commands/porch/notifications.js.map +0 -1
- package/dist/commands/porch/plan-parser.d.ts +0 -38
- package/dist/commands/porch/plan-parser.d.ts.map +0 -1
- package/dist/commands/porch/plan-parser.js +0 -166
- package/dist/commands/porch/plan-parser.js.map +0 -1
- package/dist/commands/porch/protocol-loader.d.ts +0 -46
- package/dist/commands/porch/protocol-loader.d.ts.map +0 -1
- package/dist/commands/porch/protocol-loader.js +0 -262
- package/dist/commands/porch/protocol-loader.js.map +0 -1
- package/dist/commands/porch/repl.d.ts +0 -33
- package/dist/commands/porch/repl.d.ts.map +0 -1
- package/dist/commands/porch/repl.js +0 -206
- package/dist/commands/porch/repl.js.map +0 -1
- package/dist/commands/porch/run.d.ts +0 -15
- package/dist/commands/porch/run.d.ts.map +0 -1
- package/dist/commands/porch/run.js +0 -551
- package/dist/commands/porch/run.js.map +0 -1
- package/dist/commands/porch/signal-parser.d.ts +0 -102
- package/dist/commands/porch/signal-parser.d.ts.map +0 -1
- package/dist/commands/porch/signal-parser.js +0 -199
- package/dist/commands/porch/signal-parser.js.map +0 -1
- package/dist/commands/porch/signals.d.ts +0 -35
- package/dist/commands/porch/signals.d.ts.map +0 -1
- package/dist/commands/porch/signals.js +0 -76
- package/dist/commands/porch/signals.js.map +0 -1
- package/dist/commands/porch2/checks.d.ts +0 -29
- package/dist/commands/porch2/checks.d.ts.map +0 -1
- package/dist/commands/porch2/checks.js +0 -141
- package/dist/commands/porch2/checks.js.map +0 -1
- package/dist/commands/porch2/index.d.ts +0 -38
- package/dist/commands/porch2/index.d.ts.map +0 -1
- package/dist/commands/porch2/index.js +0 -483
- package/dist/commands/porch2/index.js.map +0 -1
- package/dist/commands/porch2/plan.d.ts +0 -70
- package/dist/commands/porch2/plan.d.ts.map +0 -1
- package/dist/commands/porch2/plan.js +0 -227
- package/dist/commands/porch2/plan.js.map +0 -1
- package/dist/commands/porch2/protocol.d.ts +0 -37
- package/dist/commands/porch2/protocol.d.ts.map +0 -1
- package/dist/commands/porch2/protocol.js +0 -183
- package/dist/commands/porch2/protocol.js.map +0 -1
- package/dist/commands/porch2/state.d.ts +0 -35
- package/dist/commands/porch2/state.d.ts.map +0 -1
- package/dist/commands/porch2/state.js +0 -124
- package/dist/commands/porch2/state.js.map +0 -1
- package/dist/commands/porch2/types.d.ts +0 -79
- package/dist/commands/porch2/types.d.ts.map +0 -1
- package/dist/commands/porch2/types.js +0 -8
- package/dist/commands/porch2/types.js.map +0 -1
- package/dist/commands/tower.d.ts +0 -16
- package/dist/commands/tower.d.ts.map +0 -1
- package/dist/commands/tower.js +0 -21
- package/dist/commands/tower.js.map +0 -1
- package/skeleton/config.json +0 -7
- package/skeleton/protocols/spider/prompts/defend.md +0 -215
- package/skeleton/protocols/spider/prompts/evaluate.md +0 -241
- package/skeleton/protocols/spider/prompts/implement.md +0 -149
- package/skeleton/protocols/spider/templates/review.md +0 -207
- /package/skeleton/protocols/{spider → spir}/templates/plan.md +0 -0
- /package/skeleton/protocols/{spider → spir}/templates/spec.md +0 -0
package/templates/tower.html
CHANGED
|
@@ -2,8 +2,12 @@
|
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
|
6
|
+
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
7
|
+
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
8
|
+
<meta name="theme-color" content="#252525">
|
|
9
|
+
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🗼</text></svg>">
|
|
10
|
+
<title>Agent Farm Tower</title>
|
|
7
11
|
<style>
|
|
8
12
|
* {
|
|
9
13
|
box-sizing: border-box;
|
|
@@ -199,6 +203,36 @@
|
|
|
199
203
|
overflow: hidden;
|
|
200
204
|
}
|
|
201
205
|
|
|
206
|
+
.instance.gate-pending {
|
|
207
|
+
border-color: #f59e0b;
|
|
208
|
+
animation: gate-pulse 2s infinite;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
@keyframes gate-pulse {
|
|
212
|
+
0%, 100% { border-color: #f59e0b; }
|
|
213
|
+
50% { border-color: #d97706; }
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.gate-indicator {
|
|
217
|
+
padding: 8px 20px;
|
|
218
|
+
background: rgba(245, 158, 11, 0.15);
|
|
219
|
+
border-bottom: 1px solid var(--border);
|
|
220
|
+
font-size: 14px;
|
|
221
|
+
color: #f59e0b;
|
|
222
|
+
display: flex;
|
|
223
|
+
align-items: center;
|
|
224
|
+
gap: 8px;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.gate-indicator .gate-name {
|
|
228
|
+
font-weight: 600;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.gate-indicator .gate-builder {
|
|
232
|
+
color: var(--text-muted);
|
|
233
|
+
font-size: 12px;
|
|
234
|
+
}
|
|
235
|
+
|
|
202
236
|
.instance-header {
|
|
203
237
|
display: flex;
|
|
204
238
|
justify-content: space-between;
|
|
@@ -388,6 +422,11 @@
|
|
|
388
422
|
gap: 24px;
|
|
389
423
|
}
|
|
390
424
|
|
|
425
|
+
.new-shell-row {
|
|
426
|
+
margin-top: 8px;
|
|
427
|
+
padding-top: 8px;
|
|
428
|
+
}
|
|
429
|
+
|
|
391
430
|
/* Recents section */
|
|
392
431
|
.recents-section {
|
|
393
432
|
margin-top: 32px;
|
|
@@ -603,6 +642,68 @@
|
|
|
603
642
|
}
|
|
604
643
|
}
|
|
605
644
|
|
|
645
|
+
/* Cloud status */
|
|
646
|
+
.cloud-status {
|
|
647
|
+
display: inline-flex;
|
|
648
|
+
align-items: center;
|
|
649
|
+
gap: 8px;
|
|
650
|
+
font-size: 14px;
|
|
651
|
+
color: var(--text-secondary);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
.cloud-dot {
|
|
655
|
+
width: 8px;
|
|
656
|
+
height: 8px;
|
|
657
|
+
border-radius: 50%;
|
|
658
|
+
display: inline-block;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
.cloud-dot--green { background: var(--status-running); }
|
|
662
|
+
.cloud-dot--yellow { background: #eab308; }
|
|
663
|
+
.cloud-dot--red { background: #ef4444; }
|
|
664
|
+
.cloud-dot--gray { background: var(--text-muted); }
|
|
665
|
+
|
|
666
|
+
.cloud-link {
|
|
667
|
+
color: var(--accent);
|
|
668
|
+
text-decoration: none;
|
|
669
|
+
font-size: 13px;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
.cloud-link:hover {
|
|
673
|
+
text-decoration: underline;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
.cloud-uptime {
|
|
677
|
+
color: var(--text-muted);
|
|
678
|
+
font-size: 12px;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
.cloud-btn {
|
|
682
|
+
padding: 4px 10px;
|
|
683
|
+
border-radius: 4px;
|
|
684
|
+
border: 1px solid var(--border);
|
|
685
|
+
background: var(--bg-tertiary);
|
|
686
|
+
color: var(--text-secondary);
|
|
687
|
+
cursor: pointer;
|
|
688
|
+
font-size: 12px;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
.cloud-btn:hover {
|
|
692
|
+
background: #333;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
.cloud-btn:disabled {
|
|
696
|
+
opacity: 0.5;
|
|
697
|
+
cursor: default;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
.connect-dialog-error {
|
|
701
|
+
color: #ef4444;
|
|
702
|
+
font-size: 13px;
|
|
703
|
+
margin-top: 8px;
|
|
704
|
+
display: none;
|
|
705
|
+
}
|
|
706
|
+
|
|
606
707
|
/* Reduced motion */
|
|
607
708
|
@media (prefers-reduced-motion: reduce) {
|
|
608
709
|
.status-dot.running,
|
|
@@ -611,6 +712,183 @@
|
|
|
611
712
|
}
|
|
612
713
|
}
|
|
613
714
|
|
|
715
|
+
/* Mobile optimizations */
|
|
716
|
+
@media (max-width: 600px) {
|
|
717
|
+
body {
|
|
718
|
+
padding-bottom: env(safe-area-inset-bottom, 0);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
.header {
|
|
722
|
+
padding: 16px;
|
|
723
|
+
padding-top: calc(16px + env(safe-area-inset-top, 0));
|
|
724
|
+
flex-wrap: wrap;
|
|
725
|
+
gap: 12px;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
.header h1 {
|
|
729
|
+
font-size: 18px;
|
|
730
|
+
width: 100%;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
.header h1 .emoji {
|
|
734
|
+
font-size: 22px;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
.header-actions {
|
|
738
|
+
width: 100%;
|
|
739
|
+
justify-content: flex-end;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
/* 1. Hide Share button on mobile (tunnel is for reaching phone, not FROM phone) */
|
|
743
|
+
#share-btn {
|
|
744
|
+
display: none !important;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
/* 7. Reduce section spacing */
|
|
748
|
+
.main {
|
|
749
|
+
padding: 12px;
|
|
750
|
+
padding-left: calc(12px + env(safe-area-inset-left, 0));
|
|
751
|
+
padding-right: calc(12px + env(safe-area-inset-right, 0));
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
.section-header {
|
|
755
|
+
margin-bottom: 8px;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
.instances {
|
|
759
|
+
gap: 10px;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
.btn {
|
|
763
|
+
min-height: 44px;
|
|
764
|
+
min-width: 44px;
|
|
765
|
+
padding: 12px 16px;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
.btn-small {
|
|
769
|
+
min-height: 44px;
|
|
770
|
+
padding: 10px 14px;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/* 2. Keep project name + status badge + Restart/Stop on one line */
|
|
774
|
+
.instance-header {
|
|
775
|
+
flex-wrap: wrap;
|
|
776
|
+
gap: 8px;
|
|
777
|
+
padding: 12px 16px;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
.instance-actions {
|
|
781
|
+
margin-left: auto;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
/* 3. Hide project path row on mobile */
|
|
785
|
+
.instance-path-row {
|
|
786
|
+
display: none;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
/* 4. Compact terminal list (Overview, Architect, shells) */
|
|
790
|
+
.port-item {
|
|
791
|
+
flex-direction: row;
|
|
792
|
+
align-items: center;
|
|
793
|
+
padding: 8px 12px;
|
|
794
|
+
gap: 8px;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
.port-actions {
|
|
798
|
+
width: auto;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
.port-actions a {
|
|
802
|
+
display: flex;
|
|
803
|
+
align-items: center;
|
|
804
|
+
justify-content: center;
|
|
805
|
+
padding: 6px 12px;
|
|
806
|
+
flex: 0;
|
|
807
|
+
/* min-height handled by @media (pointer: coarse) at 44px */
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
.instance-body {
|
|
811
|
+
padding: 12px;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
/* 5. Compact New Shell row */
|
|
815
|
+
.new-shell-row {
|
|
816
|
+
margin-top: 4px;
|
|
817
|
+
padding-top: 4px;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
/* 6. Compact Recent Projects */
|
|
821
|
+
.recent-item {
|
|
822
|
+
flex-direction: row;
|
|
823
|
+
align-items: center;
|
|
824
|
+
padding: 12px 16px;
|
|
825
|
+
gap: 8px;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
.recent-path {
|
|
829
|
+
display: none;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
.recent-time {
|
|
833
|
+
font-size: 12px;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/* 7. Reduce section spacing (continued) */
|
|
837
|
+
.recents-section {
|
|
838
|
+
margin-top: 16px;
|
|
839
|
+
padding-top: 16px;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
.instance-meta {
|
|
843
|
+
margin-top: 8px;
|
|
844
|
+
padding-top: 8px;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
.launch-section {
|
|
848
|
+
padding: 16px;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
.launch-form {
|
|
852
|
+
flex-direction: column;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
.launch-form input[type="text"] {
|
|
856
|
+
min-height: 44px;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
.btn-launch {
|
|
860
|
+
width: 100%;
|
|
861
|
+
min-height: 44px;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
.dialog-box {
|
|
865
|
+
min-width: auto;
|
|
866
|
+
width: calc(100% - 32px);
|
|
867
|
+
margin: 16px;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
.toast-container {
|
|
871
|
+
left: 16px;
|
|
872
|
+
right: 16px;
|
|
873
|
+
bottom: calc(16px + env(safe-area-inset-bottom, 0));
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
/* Touch-friendly tap targets */
|
|
878
|
+
@media (pointer: coarse) {
|
|
879
|
+
.btn, .port-actions a, .autocomplete-item, .recent-item {
|
|
880
|
+
min-height: 44px;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
.copy-btn {
|
|
884
|
+
min-width: 44px;
|
|
885
|
+
min-height: 44px;
|
|
886
|
+
display: flex;
|
|
887
|
+
align-items: center;
|
|
888
|
+
justify-content: center;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
614
892
|
/* Dialog styles */
|
|
615
893
|
.dialog-overlay {
|
|
616
894
|
position: fixed;
|
|
@@ -680,7 +958,7 @@
|
|
|
680
958
|
Agent Farm Control Tower
|
|
681
959
|
</h1>
|
|
682
960
|
<div class="header-actions">
|
|
683
|
-
<
|
|
961
|
+
<span id="cloud-status"></span>
|
|
684
962
|
</div>
|
|
685
963
|
</header>
|
|
686
964
|
|
|
@@ -710,29 +988,28 @@
|
|
|
710
988
|
Type a path to see suggestions. Directories with <code>codev/</code> are highlighted as valid projects.
|
|
711
989
|
Non-codev directories will be initialized with <code>codev adopt</code> on first launch.
|
|
712
990
|
</p>
|
|
713
|
-
<div style="margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--border);">
|
|
714
|
-
<button class="btn" onclick="showCreateProjectDialog()">Create New Project...</button>
|
|
715
|
-
</div>
|
|
716
991
|
</div>
|
|
717
992
|
|
|
718
|
-
<!--
|
|
719
|
-
<div class="dialog-overlay" id="
|
|
993
|
+
<!-- Cloud Connect Dialog -->
|
|
994
|
+
<div class="dialog-overlay" id="connect-dialog" style="display: none;">
|
|
720
995
|
<div class="dialog-box">
|
|
721
|
-
<h3>
|
|
996
|
+
<h3>Connect to Codev Cloud</h3>
|
|
722
997
|
<div class="dialog-field">
|
|
723
|
-
<label for="
|
|
724
|
-
<input type="text" id="
|
|
998
|
+
<label for="connect-device-name">Device Name</label>
|
|
999
|
+
<input type="text" id="connect-device-name" placeholder="my-tower" />
|
|
725
1000
|
</div>
|
|
726
1001
|
<div class="dialog-field">
|
|
727
|
-
<label for="
|
|
728
|
-
<input type="text" id="
|
|
1002
|
+
<label for="connect-server-url">Service URL</label>
|
|
1003
|
+
<input type="text" id="connect-server-url" value="https://cloud.codevos.ai" />
|
|
729
1004
|
</div>
|
|
1005
|
+
<div class="connect-dialog-error" id="connect-error"></div>
|
|
730
1006
|
<div class="dialog-actions">
|
|
731
|
-
<button class="btn" onclick="
|
|
732
|
-
<button class="btn btn-primary" onclick="
|
|
1007
|
+
<button class="btn" onclick="hideConnectDialog()">Cancel</button>
|
|
1008
|
+
<button class="btn btn-primary" id="connect-submit-btn" onclick="submitConnect()">Connect</button>
|
|
733
1009
|
</div>
|
|
734
1010
|
</div>
|
|
735
1011
|
</div>
|
|
1012
|
+
|
|
736
1013
|
</main>
|
|
737
1014
|
|
|
738
1015
|
<div class="toast-container" id="toast-container"></div>
|
|
@@ -742,23 +1019,153 @@
|
|
|
742
1019
|
let runningInstances = [];
|
|
743
1020
|
let recentInstances = [];
|
|
744
1021
|
|
|
1022
|
+
// Auth helper: get headers with auth token if available
|
|
1023
|
+
function getAuthHeaders() {
|
|
1024
|
+
const key = localStorage.getItem('codev-web-key');
|
|
1025
|
+
return key ? { 'Authorization': `Bearer ${key}` } : {};
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
// Auth helper: make authenticated fetch request
|
|
1029
|
+
async function authFetch(url, options = {}) {
|
|
1030
|
+
const headers = { ...getAuthHeaders(), ...(options.headers || {}) };
|
|
1031
|
+
const response = await fetch(url, { ...options, headers });
|
|
1032
|
+
|
|
1033
|
+
// If 401, clear key and redirect to login
|
|
1034
|
+
if (response.status === 401) {
|
|
1035
|
+
localStorage.removeItem('codev-web-key');
|
|
1036
|
+
location.reload();
|
|
1037
|
+
throw new Error('Unauthorized');
|
|
1038
|
+
}
|
|
1039
|
+
return response;
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
// Logout function
|
|
1043
|
+
function logout() {
|
|
1044
|
+
localStorage.removeItem('codev-web-key');
|
|
1045
|
+
window.location.href = './';
|
|
1046
|
+
}
|
|
1047
|
+
|
|
745
1048
|
// Initialize
|
|
746
1049
|
async function init() {
|
|
1050
|
+
// Request notification permission
|
|
1051
|
+
if ('Notification' in window && Notification.permission === 'default') {
|
|
1052
|
+
try {
|
|
1053
|
+
await Notification.requestPermission();
|
|
1054
|
+
} catch (e) {
|
|
1055
|
+
// Ignore errors, notifications are optional
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
// Subscribe to SSE for push notifications
|
|
1060
|
+
subscribeToEvents();
|
|
1061
|
+
|
|
747
1062
|
await refresh();
|
|
748
1063
|
// Poll every 5 seconds
|
|
749
1064
|
setInterval(refresh, 5000);
|
|
750
1065
|
}
|
|
751
1066
|
|
|
1067
|
+
// SSE subscription for real-time notifications
|
|
1068
|
+
// Uses fetch + ReadableStream to support Authorization header
|
|
1069
|
+
let sseController = null;
|
|
1070
|
+
|
|
1071
|
+
async function subscribeToEvents() {
|
|
1072
|
+
// Abort any existing connection
|
|
1073
|
+
if (sseController) {
|
|
1074
|
+
sseController.abort();
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
sseController = new AbortController();
|
|
1078
|
+
|
|
1079
|
+
try {
|
|
1080
|
+
const response = await fetch('./api/events', {
|
|
1081
|
+
headers: getAuthHeaders(),
|
|
1082
|
+
signal: sseController.signal,
|
|
1083
|
+
});
|
|
1084
|
+
|
|
1085
|
+
if (!response.ok) {
|
|
1086
|
+
console.log('SSE connection failed:', response.status);
|
|
1087
|
+
setTimeout(subscribeToEvents, 5000);
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
const reader = response.body.getReader();
|
|
1092
|
+
const decoder = new TextDecoder();
|
|
1093
|
+
let buffer = '';
|
|
1094
|
+
|
|
1095
|
+
while (true) {
|
|
1096
|
+
const { done, value } = await reader.read();
|
|
1097
|
+
|
|
1098
|
+
if (done) {
|
|
1099
|
+
console.log('SSE stream ended, reconnecting...');
|
|
1100
|
+
setTimeout(subscribeToEvents, 5000);
|
|
1101
|
+
return;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1105
|
+
|
|
1106
|
+
// Parse SSE format: "data: {...}\n\n"
|
|
1107
|
+
const lines = buffer.split('\n\n');
|
|
1108
|
+
buffer = lines.pop() || ''; // Keep incomplete message in buffer
|
|
1109
|
+
|
|
1110
|
+
for (const chunk of lines) {
|
|
1111
|
+
const dataMatch = chunk.match(/^data:\s*(.+)$/m);
|
|
1112
|
+
if (!dataMatch) continue;
|
|
1113
|
+
|
|
1114
|
+
try {
|
|
1115
|
+
const data = JSON.parse(dataMatch[1]);
|
|
1116
|
+
|
|
1117
|
+
if (data.type === 'connected') {
|
|
1118
|
+
console.log('SSE connected:', data.id);
|
|
1119
|
+
continue;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
// Show browser notification
|
|
1123
|
+
if ('Notification' in window && Notification.permission === 'granted') {
|
|
1124
|
+
const notification = new Notification(data.title, {
|
|
1125
|
+
body: data.body,
|
|
1126
|
+
icon: '/favicon.ico',
|
|
1127
|
+
tag: 'tower-notification-' + data.id,
|
|
1128
|
+
});
|
|
1129
|
+
|
|
1130
|
+
// Auto-close after 5 seconds
|
|
1131
|
+
setTimeout(() => notification.close(), 5000);
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
// Also show in-app toast
|
|
1135
|
+
showToast(`${data.title}: ${data.body}`, data.type || 'info');
|
|
1136
|
+
|
|
1137
|
+
// Refresh to show updated status
|
|
1138
|
+
refresh();
|
|
1139
|
+
} catch (e) {
|
|
1140
|
+
console.error('SSE parse error:', e);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
} catch (e) {
|
|
1145
|
+
if (e.name === 'AbortError') {
|
|
1146
|
+
// Intentional abort, don't reconnect
|
|
1147
|
+
return;
|
|
1148
|
+
}
|
|
1149
|
+
console.error('SSE error:', e);
|
|
1150
|
+
setTimeout(subscribeToEvents, 5000);
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
|
|
752
1154
|
// Refresh data from API
|
|
753
1155
|
async function refresh() {
|
|
754
1156
|
try {
|
|
755
|
-
const
|
|
756
|
-
|
|
1157
|
+
const [statusResponse, cloudStatus] = await Promise.all([
|
|
1158
|
+
authFetch('./api/status'),
|
|
1159
|
+
fetchCloudStatus(),
|
|
1160
|
+
]);
|
|
757
1161
|
|
|
758
|
-
|
|
1162
|
+
if (!statusResponse.ok) throw new Error('Failed to fetch status');
|
|
1163
|
+
|
|
1164
|
+
const data = await statusResponse.json();
|
|
759
1165
|
runningInstances = (data.instances || []).filter(i => i.running);
|
|
760
1166
|
recentInstances = (data.instances || []).filter(i => !i.running);
|
|
761
1167
|
render();
|
|
1168
|
+
renderCloudStatus(cloudStatus);
|
|
762
1169
|
} catch (err) {
|
|
763
1170
|
console.error('Refresh error:', err);
|
|
764
1171
|
showToast('Failed to refresh: ' + err.message, 'error');
|
|
@@ -774,7 +1181,7 @@
|
|
|
774
1181
|
<div class="empty-state">
|
|
775
1182
|
<div class="icon">📭</div>
|
|
776
1183
|
<h2>No running instances</h2>
|
|
777
|
-
<p>Start a new instance below or run <code>af start</code> in a project directory.</p>
|
|
1184
|
+
<p>Start a new instance below or run <code>af dash start</code> in a project directory.</p>
|
|
778
1185
|
</div>
|
|
779
1186
|
`;
|
|
780
1187
|
} else {
|
|
@@ -810,28 +1217,69 @@
|
|
|
810
1217
|
function renderInstance(instance) {
|
|
811
1218
|
const statusClass = instance.running ? 'running' : 'stopped';
|
|
812
1219
|
const statusText = instance.running ? 'Running' : 'Stopped';
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
1220
|
+
const hasGate = instance.gateStatus?.hasGate;
|
|
1221
|
+
|
|
1222
|
+
// Render terminals list - always show Overview first, then individual terminals
|
|
1223
|
+
const terminals = instance.terminals || [];
|
|
1224
|
+
let terminalsHtml = '';
|
|
1225
|
+
|
|
1226
|
+
if (instance.running) {
|
|
1227
|
+
// Always show Overview link first (opens split view with Architect + Status panel)
|
|
1228
|
+
terminalsHtml = `
|
|
1229
|
+
<div class="port-item">
|
|
1230
|
+
<div class="port-info">
|
|
1231
|
+
<span class="port-status active"></span>
|
|
1232
|
+
<span class="port-type">Overview</span>
|
|
1233
|
+
</div>
|
|
1234
|
+
<div class="port-actions">
|
|
1235
|
+
<a href="${escapeHtml(relUrl(instance.proxyUrl))}" target="_blank">Open</a>
|
|
1236
|
+
</div>
|
|
820
1237
|
</div>
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
1238
|
+
`;
|
|
1239
|
+
|
|
1240
|
+
// Then show individual terminals (open full-screen terminal view)
|
|
1241
|
+
if (terminals.length > 0) {
|
|
1242
|
+
terminalsHtml += terminals.map(terminal => `
|
|
1243
|
+
<div class="port-item">
|
|
1244
|
+
<div class="port-info">
|
|
1245
|
+
<span class="port-status ${terminal.active ? 'active' : 'inactive'}"></span>
|
|
1246
|
+
<span class="port-type">${escapeHtml(terminal.label)}</span>
|
|
1247
|
+
</div>
|
|
1248
|
+
<div class="port-actions">
|
|
1249
|
+
<a href="${escapeHtml(relUrl(terminal.url))}&fullscreen=1" target="_blank">Open</a>
|
|
1250
|
+
</div>
|
|
1251
|
+
</div>
|
|
1252
|
+
`).join('');
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
// Add "New Shell" button for this instance
|
|
1256
|
+
terminalsHtml += `
|
|
1257
|
+
<div class="port-item new-shell-row" style="border-top: 1px dashed var(--border);">
|
|
1258
|
+
<div class="port-info">
|
|
1259
|
+
<span class="port-status" style="background: var(--accent);"></span>
|
|
1260
|
+
<span class="port-type">New Shell</span>
|
|
1261
|
+
</div>
|
|
1262
|
+
<div class="port-actions">
|
|
1263
|
+
<button class="btn btn-small" onclick="createShell('${escapeHtml(instance.projectPath)}')">+ Create</button>
|
|
1264
|
+
</div>
|
|
825
1265
|
</div>
|
|
826
|
-
|
|
827
|
-
|
|
1266
|
+
`;
|
|
1267
|
+
}
|
|
828
1268
|
|
|
829
1269
|
const lastUsed = instance.lastUsed
|
|
830
1270
|
? formatDate(instance.lastUsed)
|
|
831
1271
|
: 'Never';
|
|
832
1272
|
|
|
1273
|
+
const gateIndicatorHtml = hasGate ? `
|
|
1274
|
+
<div class="gate-indicator">
|
|
1275
|
+
<span>⏳</span>
|
|
1276
|
+
<span class="gate-name">${escapeHtml(instance.gateStatus.gateName || 'approval')}</span>
|
|
1277
|
+
${instance.gateStatus.builderId ? `<span class="gate-builder">(${escapeHtml(instance.gateStatus.builderId)})</span>` : ''}
|
|
1278
|
+
</div>
|
|
1279
|
+
` : '';
|
|
1280
|
+
|
|
833
1281
|
return `
|
|
834
|
-
<div class="instance">
|
|
1282
|
+
<div class="instance ${hasGate ? 'gate-pending' : ''}" data-project="${escapeHtml(instance.projectPath)}">
|
|
835
1283
|
<div class="instance-header">
|
|
836
1284
|
<div class="instance-title">
|
|
837
1285
|
<span class="instance-name">${escapeHtml(instance.projectName)}</span>
|
|
@@ -842,13 +1290,14 @@
|
|
|
842
1290
|
</div>
|
|
843
1291
|
<div class="instance-actions">
|
|
844
1292
|
${instance.running ? `
|
|
845
|
-
<button class="btn btn-small" onclick="restartInstance(
|
|
846
|
-
<button class="btn btn-small btn-danger" onclick="stopInstance(${instance.
|
|
1293
|
+
<button class="btn btn-small" onclick="restartInstance('${escapeHtml(instance.projectPath)}')">Restart</button>
|
|
1294
|
+
<button class="btn btn-small btn-danger" onclick="stopInstance('${escapeHtml(instance.projectPath)}')">Stop</button>
|
|
847
1295
|
` : `
|
|
848
1296
|
<button class="btn btn-small btn-primary" onclick="launchPath('${escapeHtml(instance.projectPath)}')">Start</button>
|
|
849
1297
|
`}
|
|
850
1298
|
</div>
|
|
851
1299
|
</div>
|
|
1300
|
+
${gateIndicatorHtml}
|
|
852
1301
|
<div class="instance-path-row">
|
|
853
1302
|
<span class="instance-path" title="${escapeHtml(instance.projectPath)}">
|
|
854
1303
|
${escapeHtml(instance.projectPath)}
|
|
@@ -857,11 +1306,10 @@
|
|
|
857
1306
|
</div>
|
|
858
1307
|
<div class="instance-body">
|
|
859
1308
|
<div class="ports-list">
|
|
860
|
-
${
|
|
1309
|
+
${terminalsHtml}
|
|
861
1310
|
</div>
|
|
862
1311
|
<div class="instance-meta">
|
|
863
1312
|
<span>Last active: ${lastUsed}</span>
|
|
864
|
-
<span>Port block: ${instance.basePort}-${instance.basePort + 99}</span>
|
|
865
1313
|
</div>
|
|
866
1314
|
</div>
|
|
867
1315
|
</div>
|
|
@@ -952,7 +1400,7 @@
|
|
|
952
1400
|
}
|
|
953
1401
|
|
|
954
1402
|
try {
|
|
955
|
-
const response = await
|
|
1403
|
+
const response = await authFetch('./api/browse?path=' + encodeURIComponent(inputPath));
|
|
956
1404
|
const data = await response.json();
|
|
957
1405
|
suggestions = data.suggestions || [];
|
|
958
1406
|
selectedIndex = -1;
|
|
@@ -1007,7 +1455,7 @@
|
|
|
1007
1455
|
// Launch a specific path (from recents)
|
|
1008
1456
|
async function launchPath(projectPath) {
|
|
1009
1457
|
try {
|
|
1010
|
-
const response = await
|
|
1458
|
+
const response = await authFetch('./api/launch', {
|
|
1011
1459
|
method: 'POST',
|
|
1012
1460
|
headers: { 'Content-Type': 'application/json' },
|
|
1013
1461
|
body: JSON.stringify({ projectPath })
|
|
@@ -1030,13 +1478,13 @@
|
|
|
1030
1478
|
}
|
|
1031
1479
|
}
|
|
1032
1480
|
|
|
1033
|
-
// Stop an instance by
|
|
1034
|
-
async function stopInstance(
|
|
1481
|
+
// Stop an instance by project path
|
|
1482
|
+
async function stopInstance(projectPath) {
|
|
1035
1483
|
try {
|
|
1036
|
-
const response = await
|
|
1484
|
+
const response = await authFetch('./api/stop', {
|
|
1037
1485
|
method: 'POST',
|
|
1038
1486
|
headers: { 'Content-Type': 'application/json' },
|
|
1039
|
-
body: JSON.stringify({
|
|
1487
|
+
body: JSON.stringify({ projectPath })
|
|
1040
1488
|
});
|
|
1041
1489
|
|
|
1042
1490
|
const result = await response.json();
|
|
@@ -1053,20 +1501,20 @@
|
|
|
1053
1501
|
}
|
|
1054
1502
|
|
|
1055
1503
|
// Restart an instance
|
|
1056
|
-
async function restartInstance(
|
|
1504
|
+
async function restartInstance(projectPath) {
|
|
1057
1505
|
try {
|
|
1058
1506
|
// First stop
|
|
1059
|
-
await
|
|
1507
|
+
await authFetch('./api/stop', {
|
|
1060
1508
|
method: 'POST',
|
|
1061
1509
|
headers: { 'Content-Type': 'application/json' },
|
|
1062
|
-
body: JSON.stringify({
|
|
1510
|
+
body: JSON.stringify({ projectPath })
|
|
1063
1511
|
});
|
|
1064
1512
|
|
|
1065
1513
|
// Wait a moment for processes to die
|
|
1066
1514
|
await new Promise(r => setTimeout(r, 1000));
|
|
1067
1515
|
|
|
1068
1516
|
// Then start
|
|
1069
|
-
const response = await
|
|
1517
|
+
const response = await authFetch('./api/launch', {
|
|
1070
1518
|
method: 'POST',
|
|
1071
1519
|
headers: { 'Content-Type': 'application/json' },
|
|
1072
1520
|
body: JSON.stringify({ projectPath })
|
|
@@ -1096,7 +1544,7 @@
|
|
|
1096
1544
|
}
|
|
1097
1545
|
|
|
1098
1546
|
try {
|
|
1099
|
-
const response = await
|
|
1547
|
+
const response = await authFetch('./api/launch', {
|
|
1100
1548
|
method: 'POST',
|
|
1101
1549
|
headers: { 'Content-Type': 'application/json' },
|
|
1102
1550
|
body: JSON.stringify({ projectPath })
|
|
@@ -1122,6 +1570,34 @@
|
|
|
1122
1570
|
}
|
|
1123
1571
|
}
|
|
1124
1572
|
|
|
1573
|
+
// Create a new shell for a running instance (via tower proxy)
|
|
1574
|
+
async function createShell(projectPath) {
|
|
1575
|
+
try {
|
|
1576
|
+
// Use tower proxy to route to the project's dashboard API
|
|
1577
|
+
const encodedPath = toBase64URL(projectPath);
|
|
1578
|
+
const response = await authFetch(`./project/${encodedPath}/api/tabs/shell`, {
|
|
1579
|
+
method: 'POST',
|
|
1580
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1581
|
+
});
|
|
1582
|
+
|
|
1583
|
+
if (!response.ok) {
|
|
1584
|
+
const errorText = await response.text();
|
|
1585
|
+
throw new Error(errorText || 'Failed to create shell');
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
const result = await response.json();
|
|
1589
|
+
|
|
1590
|
+
if (result.success === false) {
|
|
1591
|
+
throw new Error(result.error || 'Failed to create shell');
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
showToast(`Shell created: ${result.name || 'shell'}`, 'success');
|
|
1595
|
+
setTimeout(refresh, 1000);
|
|
1596
|
+
} catch (err) {
|
|
1597
|
+
showToast('Failed to create shell: ' + err.message, 'error');
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1125
1601
|
// Format date for display
|
|
1126
1602
|
function formatDate(isoString) {
|
|
1127
1603
|
const date = new Date(isoString);
|
|
@@ -1159,6 +1635,13 @@
|
|
|
1159
1635
|
}
|
|
1160
1636
|
}
|
|
1161
1637
|
|
|
1638
|
+
// Convert absolute paths to relative so links work behind reverse proxies.
|
|
1639
|
+
// E.g. "/project/abc/" → "./project/abc/"
|
|
1640
|
+
function relUrl(path) {
|
|
1641
|
+
if (path && path.startsWith('/')) return '.' + path;
|
|
1642
|
+
return path || '';
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1162
1645
|
// HTML escape
|
|
1163
1646
|
function escapeHtml(str) {
|
|
1164
1647
|
if (!str) return '';
|
|
@@ -1170,6 +1653,23 @@
|
|
|
1170
1653
|
.replace(/'/g, ''');
|
|
1171
1654
|
}
|
|
1172
1655
|
|
|
1656
|
+
// Base64URL encoding (RFC 4648) for project paths
|
|
1657
|
+
// IMPORTANT: Use TextEncoder for Unicode support (btoa only handles Latin-1)
|
|
1658
|
+
function toBase64URL(str) {
|
|
1659
|
+
// Encode string to UTF-8 bytes, then to Base64, then to Base64URL
|
|
1660
|
+
const bytes = new TextEncoder().encode(str);
|
|
1661
|
+
const base64 = btoa(String.fromCharCode(...bytes));
|
|
1662
|
+
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
// Generate proxy URL for a project
|
|
1666
|
+
// All terminals are now multiplexed on a single port via WebSocket (Spec 0085)
|
|
1667
|
+
// The React dashboard handles tab selection internally
|
|
1668
|
+
function getProxyUrl(instance, portType) {
|
|
1669
|
+
const encodedPath = toBase64URL(instance.projectPath);
|
|
1670
|
+
return `./project/${encodedPath}/`;
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1173
1673
|
// Toast notifications
|
|
1174
1674
|
function showToast(message, type = 'info') {
|
|
1175
1675
|
const container = document.getElementById('toast-container');
|
|
@@ -1183,74 +1683,214 @@
|
|
|
1183
1683
|
}, 4000);
|
|
1184
1684
|
}
|
|
1185
1685
|
|
|
1186
|
-
//
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1686
|
+
// Close dialogs on escape
|
|
1687
|
+
document.addEventListener('keydown', (e) => {
|
|
1688
|
+
if (e.key === 'Escape') {
|
|
1689
|
+
hideConnectDialog();
|
|
1690
|
+
}
|
|
1691
|
+
});
|
|
1692
|
+
document.getElementById('connect-dialog').addEventListener('click', (e) => {
|
|
1693
|
+
if (e.target.id === 'connect-dialog') {
|
|
1694
|
+
hideConnectDialog();
|
|
1695
|
+
}
|
|
1696
|
+
});
|
|
1697
|
+
|
|
1698
|
+
// Cloud status
|
|
1699
|
+
let cloudLoading = false;
|
|
1700
|
+
|
|
1701
|
+
async function fetchCloudStatus() {
|
|
1702
|
+
try {
|
|
1703
|
+
const res = await authFetch('./api/tunnel/status');
|
|
1704
|
+
if (res.status === 404) return null;
|
|
1705
|
+
if (!res.ok) return { state: 'error' };
|
|
1706
|
+
return await res.json();
|
|
1707
|
+
} catch {
|
|
1708
|
+
return null;
|
|
1709
|
+
}
|
|
1194
1710
|
}
|
|
1195
1711
|
|
|
1196
|
-
function
|
|
1197
|
-
|
|
1712
|
+
function formatUptime(ms) {
|
|
1713
|
+
const s = Math.floor(ms / 1000);
|
|
1714
|
+
if (s < 60) return s + 's';
|
|
1715
|
+
if (s < 3600) return Math.floor(s / 60) + 'm ' + (s % 60) + 's';
|
|
1716
|
+
const h = Math.floor(s / 3600);
|
|
1717
|
+
const m = Math.floor((s % 3600) / 60);
|
|
1718
|
+
return h + 'h ' + m + 'm';
|
|
1198
1719
|
}
|
|
1199
1720
|
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1721
|
+
// Store the latest cloud status for use by cloudConnect
|
|
1722
|
+
let lastCloudStatus = null;
|
|
1723
|
+
|
|
1724
|
+
function renderCloudStatus(status) {
|
|
1725
|
+
const el = document.getElementById('cloud-status');
|
|
1726
|
+
lastCloudStatus = status;
|
|
1203
1727
|
|
|
1204
|
-
|
|
1205
|
-
|
|
1728
|
+
// When accessed through codevos.ai, cloud status is irrelevant
|
|
1729
|
+
if (window.location.hostname.endsWith('codevos.ai')) {
|
|
1730
|
+
el.innerHTML = '';
|
|
1206
1731
|
return;
|
|
1207
1732
|
}
|
|
1208
|
-
|
|
1209
|
-
|
|
1733
|
+
|
|
1734
|
+
if (!status || status.state === 'error') {
|
|
1735
|
+
el.innerHTML = '';
|
|
1210
1736
|
return;
|
|
1211
1737
|
}
|
|
1212
1738
|
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1739
|
+
if (!status.registered) {
|
|
1740
|
+
el.innerHTML = `
|
|
1741
|
+
<span class="cloud-status">
|
|
1742
|
+
Codev Cloud
|
|
1743
|
+
<button class="cloud-btn" onclick="cloudConnect()" ${cloudLoading ? 'disabled' : ''}>Connect</button>
|
|
1744
|
+
</span>`;
|
|
1216
1745
|
return;
|
|
1217
1746
|
}
|
|
1218
1747
|
|
|
1219
|
-
|
|
1748
|
+
if (status.state === 'auth_failed') {
|
|
1749
|
+
el.innerHTML = `
|
|
1750
|
+
<span class="cloud-status">
|
|
1751
|
+
<span class="cloud-dot cloud-dot--red"></span>
|
|
1752
|
+
auth failed
|
|
1753
|
+
</span>`;
|
|
1754
|
+
return;
|
|
1755
|
+
}
|
|
1220
1756
|
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1757
|
+
if (status.state === 'connecting') {
|
|
1758
|
+
el.innerHTML = `
|
|
1759
|
+
<span class="cloud-status">
|
|
1760
|
+
<span class="cloud-dot cloud-dot--yellow"></span>
|
|
1761
|
+
connecting...
|
|
1762
|
+
</span>`;
|
|
1763
|
+
return;
|
|
1764
|
+
}
|
|
1227
1765
|
|
|
1228
|
-
|
|
1766
|
+
if (status.state === 'connected') {
|
|
1767
|
+
const name = escapeHtml(status.towerName || 'connected');
|
|
1768
|
+
const uptimeStr = status.uptime != null ? ' <span class="cloud-uptime">' + formatUptime(status.uptime) + '</span>' : '';
|
|
1769
|
+
el.innerHTML = `
|
|
1770
|
+
<span class="cloud-status">
|
|
1771
|
+
<span class="cloud-dot cloud-dot--green"></span>
|
|
1772
|
+
${name}${uptimeStr}
|
|
1773
|
+
<button class="cloud-btn" onclick="cloudDisconnect()" ${cloudLoading ? 'disabled' : ''}>Disconnect</button>
|
|
1774
|
+
</span>`;
|
|
1775
|
+
return;
|
|
1776
|
+
}
|
|
1229
1777
|
|
|
1230
|
-
|
|
1231
|
-
|
|
1778
|
+
// Disconnected
|
|
1779
|
+
el.innerHTML = `
|
|
1780
|
+
<span class="cloud-status">
|
|
1781
|
+
<span class="cloud-dot cloud-dot--gray"></span>
|
|
1782
|
+
disconnected
|
|
1783
|
+
<button class="cloud-btn" onclick="cloudConnect()" ${cloudLoading ? 'disabled' : ''}>Connect</button>
|
|
1784
|
+
</span>`;
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
async function cloudConnect() {
|
|
1788
|
+
// Smart connect: if registered, reconnect without dialog
|
|
1789
|
+
if (lastCloudStatus && lastCloudStatus.registered) {
|
|
1790
|
+
cloudLoading = true;
|
|
1791
|
+
renderCloudStatus({ registered: true, state: 'connecting' });
|
|
1792
|
+
try {
|
|
1793
|
+
await authFetch('./api/tunnel/connect', { method: 'POST' });
|
|
1794
|
+
showToast('Connecting to cloud...', 'success');
|
|
1795
|
+
} catch (err) {
|
|
1796
|
+
showToast('Connect failed: ' + err.message, 'error');
|
|
1232
1797
|
}
|
|
1233
|
-
|
|
1234
|
-
showToast(`Project "${name}" created! Launching...`, 'success');
|
|
1798
|
+
cloudLoading = false;
|
|
1235
1799
|
setTimeout(refresh, 2000);
|
|
1800
|
+
return;
|
|
1801
|
+
}
|
|
1802
|
+
// Not registered: open the connect dialog
|
|
1803
|
+
showConnectDialog();
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
async function cloudDisconnect() {
|
|
1807
|
+
if (!confirm('This will disconnect from Codev Cloud. Continue?')) return;
|
|
1808
|
+
cloudLoading = true;
|
|
1809
|
+
try {
|
|
1810
|
+
const res = await authFetch('./api/tunnel/disconnect', { method: 'POST' });
|
|
1811
|
+
const data = await res.json();
|
|
1812
|
+
if (data.warning) {
|
|
1813
|
+
showToast(data.warning, 'error');
|
|
1814
|
+
} else {
|
|
1815
|
+
showToast('Disconnected from cloud', 'success');
|
|
1816
|
+
}
|
|
1236
1817
|
} catch (err) {
|
|
1237
|
-
showToast('
|
|
1818
|
+
showToast('Disconnect failed: ' + err.message, 'error');
|
|
1238
1819
|
}
|
|
1820
|
+
cloudLoading = false;
|
|
1821
|
+
setTimeout(refresh, 1000);
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
// Cloud connect dialog functions
|
|
1825
|
+
function showConnectDialog() {
|
|
1826
|
+
const dialog = document.getElementById('connect-dialog');
|
|
1827
|
+
const nameInput = document.getElementById('connect-device-name');
|
|
1828
|
+
const errorEl = document.getElementById('connect-error');
|
|
1829
|
+
errorEl.style.display = 'none';
|
|
1830
|
+
errorEl.textContent = '';
|
|
1831
|
+
// Default device name from hostname (fetched via /api/tunnel/status)
|
|
1832
|
+
nameInput.value = (lastCloudStatus && lastCloudStatus.hostname) || '';
|
|
1833
|
+
document.getElementById('connect-server-url').value = 'https://cloud.codevos.ai';
|
|
1834
|
+
dialog.style.display = 'flex';
|
|
1835
|
+
nameInput.focus();
|
|
1239
1836
|
}
|
|
1240
1837
|
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1838
|
+
function hideConnectDialog() {
|
|
1839
|
+
document.getElementById('connect-dialog').style.display = 'none';
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
function uiNormalizeDeviceName(raw) {
|
|
1843
|
+
return raw.trim().toLowerCase().replace(/[\s_]+/g, '-').replace(/[^a-z0-9-]/g, '');
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
function uiValidateDeviceName(name) {
|
|
1847
|
+
if (!name) return { valid: false, error: 'Device name is required.' };
|
|
1848
|
+
if (name.length > 63) return { valid: false, error: 'Device name must be 63 characters or fewer.' };
|
|
1849
|
+
if (!/^[a-z0-9]/.test(name) || !/[a-z0-9]$/.test(name)) return { valid: false, error: 'Device name must start and end with a letter or number.' };
|
|
1850
|
+
if (/^-+$/.test(name)) return { valid: false, error: 'Device name cannot be all hyphens.' };
|
|
1851
|
+
return { valid: true };
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
async function submitConnect() {
|
|
1855
|
+
const rawName = document.getElementById('connect-device-name').value;
|
|
1856
|
+
const serverUrl = document.getElementById('connect-server-url').value.trim();
|
|
1857
|
+
const errorEl = document.getElementById('connect-error');
|
|
1858
|
+
const submitBtn = document.getElementById('connect-submit-btn');
|
|
1859
|
+
|
|
1860
|
+
const name = uiNormalizeDeviceName(rawName);
|
|
1861
|
+
const result = uiValidateDeviceName(name);
|
|
1862
|
+
if (!result.valid) {
|
|
1863
|
+
errorEl.textContent = result.error;
|
|
1864
|
+
errorEl.style.display = 'block';
|
|
1865
|
+
return;
|
|
1245
1866
|
}
|
|
1246
|
-
});
|
|
1247
1867
|
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1868
|
+
errorEl.style.display = 'none';
|
|
1869
|
+
submitBtn.disabled = true;
|
|
1870
|
+
submitBtn.textContent = 'Connecting...';
|
|
1871
|
+
|
|
1872
|
+
try {
|
|
1873
|
+
const res = await authFetch('./api/tunnel/connect', {
|
|
1874
|
+
method: 'POST',
|
|
1875
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1876
|
+
body: JSON.stringify({ name, serverUrl, origin: window.location.origin }),
|
|
1877
|
+
});
|
|
1878
|
+
const data = await res.json();
|
|
1879
|
+
if (data.authUrl) {
|
|
1880
|
+
window.location.href = data.authUrl;
|
|
1881
|
+
} else {
|
|
1882
|
+
errorEl.textContent = data.error || 'Unexpected response from server';
|
|
1883
|
+
errorEl.style.display = 'block';
|
|
1884
|
+
submitBtn.disabled = false;
|
|
1885
|
+
submitBtn.textContent = 'Connect';
|
|
1886
|
+
}
|
|
1887
|
+
} catch (err) {
|
|
1888
|
+
errorEl.textContent = 'Connection failed: ' + err.message;
|
|
1889
|
+
errorEl.style.display = 'block';
|
|
1890
|
+
submitBtn.disabled = false;
|
|
1891
|
+
submitBtn.textContent = 'Connect';
|
|
1252
1892
|
}
|
|
1253
|
-
}
|
|
1893
|
+
}
|
|
1254
1894
|
|
|
1255
1895
|
// Initialize
|
|
1256
1896
|
init();
|