@cluesmith/codev 2.0.0-rc.6 → 2.0.0-rc.60
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/bin/porch.js +6 -35
- package/dashboard/dist/assets/index-CXloFYpB.css +32 -0
- package/dashboard/dist/assets/index-Ca2fjOJf.js +131 -0
- package/dashboard/dist/assets/index-Ca2fjOJf.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 +94 -65
- package/dist/agent-farm/cli.js.map +1 -1
- package/dist/agent-farm/commands/architect.d.ts.map +1 -1
- package/dist/agent-farm/commands/architect.js +13 -6
- 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 +202 -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 +30 -3
- package/dist/agent-farm/commands/cleanup.js.map +1 -1
- package/dist/agent-farm/commands/consult.js +1 -1
- 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 +37 -70
- package/dist/agent-farm/commands/open.js.map +1 -1
- package/dist/agent-farm/commands/send.d.ts.map +1 -1
- package/dist/agent-farm/commands/send.js +55 -17
- package/dist/agent-farm/commands/send.js.map +1 -1
- package/dist/agent-farm/commands/{util.d.ts → shell.d.ts} +5 -5
- package/dist/agent-farm/commands/shell.d.ts.map +1 -0
- package/dist/agent-farm/commands/{util.js → shell.js} +23 -36
- package/dist/agent-farm/commands/shell.js.map +1 -0
- package/dist/agent-farm/commands/spawn.d.ts.map +1 -1
- package/dist/agent-farm/commands/spawn.js +503 -226
- package/dist/agent-farm/commands/spawn.js.map +1 -1
- package/dist/agent-farm/commands/start.d.ts +3 -0
- package/dist/agent-farm/commands/start.d.ts.map +1 -1
- package/dist/agent-farm/commands/start.js +55 -265
- 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 +61 -3
- 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 +116 -12
- package/dist/agent-farm/commands/stop.js.map +1 -1
- 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.map +1 -1
- package/dist/agent-farm/db/index.js +124 -0
- package/dist/agent-farm/db/index.js.map +1 -1
- package/dist/agent-farm/db/schema.d.ts +2 -2
- package/dist/agent-farm/db/schema.d.ts.map +1 -1
- package/dist/agent-farm/db/schema.js +26 -5
- package/dist/agent-farm/db/schema.js.map +1 -1
- package/dist/agent-farm/db/types.d.ts +3 -0
- package/dist/agent-farm/db/types.d.ts.map +1 -1
- package/dist/agent-farm/db/types.js +3 -0
- 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/tower-client.d.ts +157 -0
- package/dist/agent-farm/lib/tower-client.d.ts.map +1 -0
- package/dist/agent-farm/lib/tower-client.js +223 -0
- package/dist/agent-farm/lib/tower-client.js.map +1 -0
- package/dist/agent-farm/servers/tower-server.js +2137 -112
- package/dist/agent-farm/servers/tower-server.js.map +1 -1
- package/dist/agent-farm/state.d.ts +4 -10
- package/dist/agent-farm/state.d.ts.map +1 -1
- package/dist/agent-farm/state.js +30 -31
- package/dist/agent-farm/state.js.map +1 -1
- package/dist/agent-farm/types.d.ts +48 -1
- package/dist/agent-farm/types.d.ts.map +1 -1
- package/dist/agent-farm/utils/config.d.ts.map +1 -1
- package/dist/agent-farm/utils/config.js +13 -14
- 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 -16
- package/dist/agent-farm/utils/deps.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/port-registry.d.ts +0 -1
- package/dist/agent-farm/utils/port-registry.d.ts.map +1 -1
- package/dist/agent-farm/utils/port-registry.js +1 -1
- package/dist/agent-farm/utils/port-registry.js.map +1 -1
- package/dist/agent-farm/utils/server-utils.d.ts +4 -4
- package/dist/agent-farm/utils/server-utils.d.ts.map +1 -1
- package/dist/agent-farm/utils/server-utils.js +4 -15
- package/dist/agent-farm/utils/server-utils.js.map +1 -1
- 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/agent-farm/utils/terminal-ports.d.ts +1 -1
- package/dist/agent-farm/utils/terminal-ports.js +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +7 -54
- package/dist/cli.js.map +1 -1
- package/dist/commands/adopt.d.ts.map +1 -1
- package/dist/commands/adopt.js +49 -4
- package/dist/commands/adopt.js.map +1 -1
- package/dist/commands/consult/index.d.ts +1 -0
- package/dist/commands/consult/index.d.ts.map +1 -1
- package/dist/commands/consult/index.js +85 -6
- package/dist/commands/consult/index.js.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +0 -15
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +41 -2
- 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 +17 -29
- package/dist/commands/porch/checks.d.ts.map +1 -1
- package/dist/commands/porch/checks.js +96 -144
- package/dist/commands/porch/checks.js.map +1 -1
- package/dist/commands/porch/index.d.ts +21 -43
- package/dist/commands/porch/index.d.ts.map +1 -1
- package/dist/commands/porch/index.js +418 -1123
- 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 +479 -0
- package/dist/commands/porch/next.js.map +1 -0
- package/dist/commands/porch/plan.d.ts +70 -0
- package/dist/commands/porch/plan.d.ts.map +1 -0
- package/dist/commands/porch/plan.js +190 -0
- package/dist/commands/porch/plan.js.map +1 -0
- package/dist/commands/porch/prompts.d.ts +19 -0
- package/dist/commands/porch/prompts.d.ts.map +1 -0
- package/dist/commands/porch/prompts.js +255 -0
- package/dist/commands/porch/prompts.js.map +1 -0
- package/dist/commands/porch/protocol.d.ts +59 -0
- package/dist/commands/porch/protocol.d.ts.map +1 -0
- package/dist/commands/porch/protocol.js +294 -0
- package/dist/commands/porch/protocol.js.map +1 -0
- package/dist/commands/porch/state.d.ts +23 -112
- package/dist/commands/porch/state.d.ts.map +1 -1
- package/dist/commands/porch/state.js +81 -699
- package/dist/commands/porch/state.js.map +1 -1
- package/dist/commands/porch/types.d.ts +99 -164
- package/dist/commands/porch/types.d.ts.map +1 -1
- package/dist/commands/porch/types.js +2 -1
- package/dist/commands/porch/types.js.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 +31 -0
- package/dist/commands/update.js.map +1 -1
- package/dist/lib/scaffold.d.ts +37 -0
- package/dist/lib/scaffold.d.ts.map +1 -1
- package/dist/lib/scaffold.js +114 -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 +60 -0
- package/dist/terminal/pty-manager.d.ts.map +1 -0
- package/dist/terminal/pty-manager.js +334 -0
- package/dist/terminal/pty-manager.js.map +1 -0
- package/dist/terminal/pty-session.d.ts +79 -0
- package/dist/terminal/pty-session.d.ts.map +1 -0
- package/dist/terminal/pty-session.js +215 -0
- package/dist/terminal/pty-session.js.map +1 -0
- package/dist/terminal/ring-buffer.d.ts +27 -0
- package/dist/terminal/ring-buffer.d.ts.map +1 -0
- package/dist/terminal/ring-buffer.js +74 -0
- package/dist/terminal/ring-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 +18 -5
- package/skeleton/.claude/skills/af/SKILL.md +74 -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 +3 -29
- package/skeleton/builders.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 +54 -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 +61 -0
- package/skeleton/protocols/bugfix/protocol.json +19 -2
- 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 +14 -8
- package/skeleton/protocols/protocol-schema.json +54 -1
- package/skeleton/protocols/spir/builder-prompt.md +59 -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 +7 -25
- package/skeleton/protocols/{spider → spir}/prompts/specify.md +33 -61
- package/skeleton/protocols/spir/protocol.json +152 -0
- package/skeleton/protocols/{spider → spir}/protocol.md +35 -21
- package/skeleton/protocols/{spider → spir}/templates/plan.md +14 -0
- package/skeleton/protocols/{spider → spir}/templates/review.md +1 -1
- 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 +25 -43
- package/skeleton/resources/commands/overview.md +7 -17
- package/skeleton/resources/workflow-reference.md +4 -4
- package/skeleton/roles/architect.md +152 -315
- package/skeleton/roles/builder.md +109 -218
- 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 -43
- 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 -87
- package/templates/open.html +26 -0
- package/templates/tower.html +542 -27
- package/dist/agent-farm/commands/kickoff.d.ts +0 -19
- package/dist/agent-farm/commands/kickoff.d.ts.map +0 -1
- package/dist/agent-farm/commands/kickoff.js +0 -331
- 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.map +0 -1
- 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 -1872
- 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/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/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 -253
- package/dist/commands/porch/protocol-loader.js.map +0 -1
- package/dist/commands/porch/signal-parser.d.ts +0 -88
- package/dist/commands/porch/signal-parser.d.ts.map +0 -1
- package/dist/commands/porch/signal-parser.js +0 -148
- package/dist/commands/porch/signal-parser.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/porch/protocols/bugfix.json +0 -85
- package/skeleton/porch/protocols/spider.json +0 -135
- package/skeleton/porch/protocols/tick.json +0 -76
- 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/protocol.json +0 -210
- package/templates/dashboard/css/activity.css +0 -151
- package/templates/dashboard/js/activity.js +0 -112
- /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;
|
|
@@ -611,6 +645,132 @@
|
|
|
611
645
|
}
|
|
612
646
|
}
|
|
613
647
|
|
|
648
|
+
/* Mobile optimizations */
|
|
649
|
+
@media (max-width: 600px) {
|
|
650
|
+
body {
|
|
651
|
+
padding-bottom: env(safe-area-inset-bottom, 0);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
.header {
|
|
655
|
+
padding: 16px;
|
|
656
|
+
padding-top: calc(16px + env(safe-area-inset-top, 0));
|
|
657
|
+
flex-wrap: wrap;
|
|
658
|
+
gap: 12px;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
.header h1 {
|
|
662
|
+
font-size: 18px;
|
|
663
|
+
width: 100%;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
.header h1 .emoji {
|
|
667
|
+
font-size: 22px;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
.header-actions {
|
|
671
|
+
width: 100%;
|
|
672
|
+
justify-content: flex-end;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
.main {
|
|
676
|
+
padding: 16px;
|
|
677
|
+
padding-left: calc(16px + env(safe-area-inset-left, 0));
|
|
678
|
+
padding-right: calc(16px + env(safe-area-inset-right, 0));
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
.btn {
|
|
682
|
+
min-height: 44px;
|
|
683
|
+
min-width: 44px;
|
|
684
|
+
padding: 12px 16px;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
.btn-small {
|
|
688
|
+
min-height: 44px;
|
|
689
|
+
padding: 10px 14px;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
.instance-header {
|
|
693
|
+
flex-direction: column;
|
|
694
|
+
align-items: flex-start;
|
|
695
|
+
gap: 12px;
|
|
696
|
+
padding: 16px;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
.instance-actions {
|
|
700
|
+
width: 100%;
|
|
701
|
+
justify-content: flex-end;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
.port-item {
|
|
705
|
+
flex-direction: column;
|
|
706
|
+
align-items: flex-start;
|
|
707
|
+
gap: 8px;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
.port-actions {
|
|
711
|
+
width: 100%;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
.port-actions a {
|
|
715
|
+
min-height: 44px;
|
|
716
|
+
display: flex;
|
|
717
|
+
align-items: center;
|
|
718
|
+
justify-content: center;
|
|
719
|
+
flex: 1;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
.launch-section {
|
|
723
|
+
padding: 16px;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
.launch-form {
|
|
727
|
+
flex-direction: column;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
.launch-form input[type="text"] {
|
|
731
|
+
min-height: 44px;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
.btn-launch {
|
|
735
|
+
width: 100%;
|
|
736
|
+
min-height: 44px;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
.recent-item {
|
|
740
|
+
flex-direction: column;
|
|
741
|
+
align-items: flex-start;
|
|
742
|
+
gap: 12px;
|
|
743
|
+
padding: 16px;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
.dialog-box {
|
|
747
|
+
min-width: auto;
|
|
748
|
+
width: calc(100% - 32px);
|
|
749
|
+
margin: 16px;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
.toast-container {
|
|
753
|
+
left: 16px;
|
|
754
|
+
right: 16px;
|
|
755
|
+
bottom: calc(16px + env(safe-area-inset-bottom, 0));
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
/* Touch-friendly tap targets */
|
|
760
|
+
@media (pointer: coarse) {
|
|
761
|
+
.btn, .port-actions a, .autocomplete-item, .recent-item {
|
|
762
|
+
min-height: 44px;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
.copy-btn {
|
|
766
|
+
min-width: 44px;
|
|
767
|
+
min-height: 44px;
|
|
768
|
+
display: flex;
|
|
769
|
+
align-items: center;
|
|
770
|
+
justify-content: center;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
|
|
614
774
|
/* Dialog styles */
|
|
615
775
|
.dialog-overlay {
|
|
616
776
|
position: fixed;
|
|
@@ -680,6 +840,7 @@
|
|
|
680
840
|
Agent Farm Control Tower
|
|
681
841
|
</h1>
|
|
682
842
|
<div class="header-actions">
|
|
843
|
+
<button class="btn" id="share-btn" onclick="showShareDialog()" style="display: none;" title="Share via QR code">📱 Share</button>
|
|
683
844
|
<button class="btn" onclick="refresh()">Refresh</button>
|
|
684
845
|
</div>
|
|
685
846
|
</header>
|
|
@@ -733,6 +894,23 @@
|
|
|
733
894
|
</div>
|
|
734
895
|
</div>
|
|
735
896
|
</div>
|
|
897
|
+
|
|
898
|
+
<!-- Share Dialog (QR Code) -->
|
|
899
|
+
<div class="dialog-overlay" id="share-dialog" style="display: none;">
|
|
900
|
+
<div class="dialog-box" style="text-align: center;">
|
|
901
|
+
<h3>📱 Share via QR Code</h3>
|
|
902
|
+
<div id="share-content">
|
|
903
|
+
<div class="loading">
|
|
904
|
+
<div class="spinner"></div>
|
|
905
|
+
<p>Starting tunnel...</p>
|
|
906
|
+
</div>
|
|
907
|
+
</div>
|
|
908
|
+
<div class="dialog-actions" style="margin-top: 16px;">
|
|
909
|
+
<button class="btn" onclick="hideShareDialog()">Close</button>
|
|
910
|
+
<button class="btn" id="stop-tunnel-btn" onclick="stopTunnelAndClose()" style="display: none;">Stop Tunnel</button>
|
|
911
|
+
</div>
|
|
912
|
+
</div>
|
|
913
|
+
</div>
|
|
736
914
|
</main>
|
|
737
915
|
|
|
738
916
|
<div class="toast-container" id="toast-container"></div>
|
|
@@ -742,17 +920,153 @@
|
|
|
742
920
|
let runningInstances = [];
|
|
743
921
|
let recentInstances = [];
|
|
744
922
|
|
|
923
|
+
// Auth helper: get headers with auth token if available
|
|
924
|
+
function getAuthHeaders() {
|
|
925
|
+
const key = localStorage.getItem('codev_web_key');
|
|
926
|
+
return key ? { 'Authorization': `Bearer ${key}` } : {};
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
// Auth helper: make authenticated fetch request
|
|
930
|
+
async function authFetch(url, options = {}) {
|
|
931
|
+
const headers = { ...getAuthHeaders(), ...(options.headers || {}) };
|
|
932
|
+
const response = await fetch(url, { ...options, headers });
|
|
933
|
+
|
|
934
|
+
// If 401, clear key and redirect to login
|
|
935
|
+
if (response.status === 401) {
|
|
936
|
+
localStorage.removeItem('codev_web_key');
|
|
937
|
+
location.reload();
|
|
938
|
+
throw new Error('Unauthorized');
|
|
939
|
+
}
|
|
940
|
+
return response;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
// Logout function
|
|
944
|
+
function logout() {
|
|
945
|
+
localStorage.removeItem('codev_web_key');
|
|
946
|
+
window.location.href = '/';
|
|
947
|
+
}
|
|
948
|
+
|
|
745
949
|
// Initialize
|
|
746
950
|
async function init() {
|
|
951
|
+
// Check if cloudflared is available and show share button
|
|
952
|
+
try {
|
|
953
|
+
const tunnelRes = await fetch('/api/tunnel/status');
|
|
954
|
+
const tunnelStatus = await tunnelRes.json();
|
|
955
|
+
if (tunnelStatus.available) {
|
|
956
|
+
document.getElementById('share-btn').style.display = 'inline-block';
|
|
957
|
+
}
|
|
958
|
+
} catch (e) {
|
|
959
|
+
// Tunnel not available, button stays hidden
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// Request notification permission
|
|
963
|
+
if ('Notification' in window && Notification.permission === 'default') {
|
|
964
|
+
try {
|
|
965
|
+
await Notification.requestPermission();
|
|
966
|
+
} catch (e) {
|
|
967
|
+
// Ignore errors, notifications are optional
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
// Subscribe to SSE for push notifications
|
|
972
|
+
subscribeToEvents();
|
|
973
|
+
|
|
747
974
|
await refresh();
|
|
748
975
|
// Poll every 5 seconds
|
|
749
976
|
setInterval(refresh, 5000);
|
|
750
977
|
}
|
|
751
978
|
|
|
979
|
+
// SSE subscription for real-time notifications
|
|
980
|
+
// Uses fetch + ReadableStream to support Authorization header
|
|
981
|
+
let sseController = null;
|
|
982
|
+
|
|
983
|
+
async function subscribeToEvents() {
|
|
984
|
+
// Abort any existing connection
|
|
985
|
+
if (sseController) {
|
|
986
|
+
sseController.abort();
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
sseController = new AbortController();
|
|
990
|
+
|
|
991
|
+
try {
|
|
992
|
+
const response = await fetch('/api/events', {
|
|
993
|
+
headers: getAuthHeaders(),
|
|
994
|
+
signal: sseController.signal,
|
|
995
|
+
});
|
|
996
|
+
|
|
997
|
+
if (!response.ok) {
|
|
998
|
+
console.log('SSE connection failed:', response.status);
|
|
999
|
+
setTimeout(subscribeToEvents, 5000);
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
const reader = response.body.getReader();
|
|
1004
|
+
const decoder = new TextDecoder();
|
|
1005
|
+
let buffer = '';
|
|
1006
|
+
|
|
1007
|
+
while (true) {
|
|
1008
|
+
const { done, value } = await reader.read();
|
|
1009
|
+
|
|
1010
|
+
if (done) {
|
|
1011
|
+
console.log('SSE stream ended, reconnecting...');
|
|
1012
|
+
setTimeout(subscribeToEvents, 5000);
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1017
|
+
|
|
1018
|
+
// Parse SSE format: "data: {...}\n\n"
|
|
1019
|
+
const lines = buffer.split('\n\n');
|
|
1020
|
+
buffer = lines.pop() || ''; // Keep incomplete message in buffer
|
|
1021
|
+
|
|
1022
|
+
for (const chunk of lines) {
|
|
1023
|
+
const dataMatch = chunk.match(/^data:\s*(.+)$/m);
|
|
1024
|
+
if (!dataMatch) continue;
|
|
1025
|
+
|
|
1026
|
+
try {
|
|
1027
|
+
const data = JSON.parse(dataMatch[1]);
|
|
1028
|
+
|
|
1029
|
+
if (data.type === 'connected') {
|
|
1030
|
+
console.log('SSE connected:', data.id);
|
|
1031
|
+
continue;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
// Show browser notification
|
|
1035
|
+
if ('Notification' in window && Notification.permission === 'granted') {
|
|
1036
|
+
const notification = new Notification(data.title, {
|
|
1037
|
+
body: data.body,
|
|
1038
|
+
icon: '/favicon.ico',
|
|
1039
|
+
tag: 'tower-notification-' + data.id,
|
|
1040
|
+
});
|
|
1041
|
+
|
|
1042
|
+
// Auto-close after 5 seconds
|
|
1043
|
+
setTimeout(() => notification.close(), 5000);
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
// Also show in-app toast
|
|
1047
|
+
showToast(`${data.title}: ${data.body}`, data.type || 'info');
|
|
1048
|
+
|
|
1049
|
+
// Refresh to show updated status
|
|
1050
|
+
refresh();
|
|
1051
|
+
} catch (e) {
|
|
1052
|
+
console.error('SSE parse error:', e);
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
} catch (e) {
|
|
1057
|
+
if (e.name === 'AbortError') {
|
|
1058
|
+
// Intentional abort, don't reconnect
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1061
|
+
console.error('SSE error:', e);
|
|
1062
|
+
setTimeout(subscribeToEvents, 5000);
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
|
|
752
1066
|
// Refresh data from API
|
|
753
1067
|
async function refresh() {
|
|
754
1068
|
try {
|
|
755
|
-
const response = await
|
|
1069
|
+
const response = await authFetch('/api/status');
|
|
756
1070
|
if (!response.ok) throw new Error('Failed to fetch status');
|
|
757
1071
|
|
|
758
1072
|
const data = await response.json();
|
|
@@ -774,7 +1088,7 @@
|
|
|
774
1088
|
<div class="empty-state">
|
|
775
1089
|
<div class="icon">📭</div>
|
|
776
1090
|
<h2>No running instances</h2>
|
|
777
|
-
<p>Start a new instance below or run <code>af start</code> in a project directory.</p>
|
|
1091
|
+
<p>Start a new instance below or run <code>af dash start</code> in a project directory.</p>
|
|
778
1092
|
</div>
|
|
779
1093
|
`;
|
|
780
1094
|
} else {
|
|
@@ -810,28 +1124,69 @@
|
|
|
810
1124
|
function renderInstance(instance) {
|
|
811
1125
|
const statusClass = instance.running ? 'running' : 'stopped';
|
|
812
1126
|
const statusText = instance.running ? 'Running' : 'Stopped';
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
1127
|
+
const hasGate = instance.gateStatus?.hasGate;
|
|
1128
|
+
|
|
1129
|
+
// Render terminals list - always show Overview first, then individual terminals
|
|
1130
|
+
const terminals = instance.terminals || [];
|
|
1131
|
+
let terminalsHtml = '';
|
|
1132
|
+
|
|
1133
|
+
if (instance.running) {
|
|
1134
|
+
// Always show Overview link first (opens split view with Architect + Status panel)
|
|
1135
|
+
terminalsHtml = `
|
|
1136
|
+
<div class="port-item">
|
|
1137
|
+
<div class="port-info">
|
|
1138
|
+
<span class="port-status active"></span>
|
|
1139
|
+
<span class="port-type">Overview</span>
|
|
1140
|
+
</div>
|
|
1141
|
+
<div class="port-actions">
|
|
1142
|
+
<a href="${escapeHtml(instance.proxyUrl)}" target="_blank">Open</a>
|
|
1143
|
+
</div>
|
|
820
1144
|
</div>
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
1145
|
+
`;
|
|
1146
|
+
|
|
1147
|
+
// Then show individual terminals (open full-screen terminal view)
|
|
1148
|
+
if (terminals.length > 0) {
|
|
1149
|
+
terminalsHtml += terminals.map(terminal => `
|
|
1150
|
+
<div class="port-item">
|
|
1151
|
+
<div class="port-info">
|
|
1152
|
+
<span class="port-status ${terminal.active ? 'active' : 'inactive'}"></span>
|
|
1153
|
+
<span class="port-type">${escapeHtml(terminal.label)}</span>
|
|
1154
|
+
</div>
|
|
1155
|
+
<div class="port-actions">
|
|
1156
|
+
<a href="${escapeHtml(terminal.url)}&fullscreen=1" target="_blank">Open</a>
|
|
1157
|
+
</div>
|
|
1158
|
+
</div>
|
|
1159
|
+
`).join('');
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
// Add "New Shell" button for this instance
|
|
1163
|
+
terminalsHtml += `
|
|
1164
|
+
<div class="port-item" style="border-top: 1px dashed var(--border); margin-top: 8px; padding-top: 8px;">
|
|
1165
|
+
<div class="port-info">
|
|
1166
|
+
<span class="port-status" style="background: var(--accent);"></span>
|
|
1167
|
+
<span class="port-type">New Shell</span>
|
|
1168
|
+
</div>
|
|
1169
|
+
<div class="port-actions">
|
|
1170
|
+
<button class="btn btn-small" onclick="createShell('${escapeHtml(instance.projectPath)}')">+ Create</button>
|
|
1171
|
+
</div>
|
|
825
1172
|
</div>
|
|
826
|
-
|
|
827
|
-
|
|
1173
|
+
`;
|
|
1174
|
+
}
|
|
828
1175
|
|
|
829
1176
|
const lastUsed = instance.lastUsed
|
|
830
1177
|
? formatDate(instance.lastUsed)
|
|
831
1178
|
: 'Never';
|
|
832
1179
|
|
|
1180
|
+
const gateIndicatorHtml = hasGate ? `
|
|
1181
|
+
<div class="gate-indicator">
|
|
1182
|
+
<span>⏳</span>
|
|
1183
|
+
<span class="gate-name">${escapeHtml(instance.gateStatus.gateName || 'approval')}</span>
|
|
1184
|
+
${instance.gateStatus.builderId ? `<span class="gate-builder">(${escapeHtml(instance.gateStatus.builderId)})</span>` : ''}
|
|
1185
|
+
</div>
|
|
1186
|
+
` : '';
|
|
1187
|
+
|
|
833
1188
|
return `
|
|
834
|
-
<div class="instance">
|
|
1189
|
+
<div class="instance ${hasGate ? 'gate-pending' : ''}" data-project="${escapeHtml(instance.projectPath)}">
|
|
835
1190
|
<div class="instance-header">
|
|
836
1191
|
<div class="instance-title">
|
|
837
1192
|
<span class="instance-name">${escapeHtml(instance.projectName)}</span>
|
|
@@ -849,6 +1204,7 @@
|
|
|
849
1204
|
`}
|
|
850
1205
|
</div>
|
|
851
1206
|
</div>
|
|
1207
|
+
${gateIndicatorHtml}
|
|
852
1208
|
<div class="instance-path-row">
|
|
853
1209
|
<span class="instance-path" title="${escapeHtml(instance.projectPath)}">
|
|
854
1210
|
${escapeHtml(instance.projectPath)}
|
|
@@ -857,11 +1213,10 @@
|
|
|
857
1213
|
</div>
|
|
858
1214
|
<div class="instance-body">
|
|
859
1215
|
<div class="ports-list">
|
|
860
|
-
${
|
|
1216
|
+
${terminalsHtml}
|
|
861
1217
|
</div>
|
|
862
1218
|
<div class="instance-meta">
|
|
863
1219
|
<span>Last active: ${lastUsed}</span>
|
|
864
|
-
<span>Port block: ${instance.basePort}-${instance.basePort + 99}</span>
|
|
865
1220
|
</div>
|
|
866
1221
|
</div>
|
|
867
1222
|
</div>
|
|
@@ -952,7 +1307,7 @@
|
|
|
952
1307
|
}
|
|
953
1308
|
|
|
954
1309
|
try {
|
|
955
|
-
const response = await
|
|
1310
|
+
const response = await authFetch('/api/browse?path=' + encodeURIComponent(inputPath));
|
|
956
1311
|
const data = await response.json();
|
|
957
1312
|
suggestions = data.suggestions || [];
|
|
958
1313
|
selectedIndex = -1;
|
|
@@ -1007,7 +1362,7 @@
|
|
|
1007
1362
|
// Launch a specific path (from recents)
|
|
1008
1363
|
async function launchPath(projectPath) {
|
|
1009
1364
|
try {
|
|
1010
|
-
const response = await
|
|
1365
|
+
const response = await authFetch('/api/launch', {
|
|
1011
1366
|
method: 'POST',
|
|
1012
1367
|
headers: { 'Content-Type': 'application/json' },
|
|
1013
1368
|
body: JSON.stringify({ projectPath })
|
|
@@ -1033,7 +1388,7 @@
|
|
|
1033
1388
|
// Stop an instance by port
|
|
1034
1389
|
async function stopInstance(basePort) {
|
|
1035
1390
|
try {
|
|
1036
|
-
const response = await
|
|
1391
|
+
const response = await authFetch('/api/stop', {
|
|
1037
1392
|
method: 'POST',
|
|
1038
1393
|
headers: { 'Content-Type': 'application/json' },
|
|
1039
1394
|
body: JSON.stringify({ basePort })
|
|
@@ -1056,7 +1411,7 @@
|
|
|
1056
1411
|
async function restartInstance(basePort, projectPath) {
|
|
1057
1412
|
try {
|
|
1058
1413
|
// First stop
|
|
1059
|
-
await
|
|
1414
|
+
await authFetch('/api/stop', {
|
|
1060
1415
|
method: 'POST',
|
|
1061
1416
|
headers: { 'Content-Type': 'application/json' },
|
|
1062
1417
|
body: JSON.stringify({ basePort })
|
|
@@ -1066,7 +1421,7 @@
|
|
|
1066
1421
|
await new Promise(r => setTimeout(r, 1000));
|
|
1067
1422
|
|
|
1068
1423
|
// Then start
|
|
1069
|
-
const response = await
|
|
1424
|
+
const response = await authFetch('/api/launch', {
|
|
1070
1425
|
method: 'POST',
|
|
1071
1426
|
headers: { 'Content-Type': 'application/json' },
|
|
1072
1427
|
body: JSON.stringify({ projectPath })
|
|
@@ -1096,7 +1451,7 @@
|
|
|
1096
1451
|
}
|
|
1097
1452
|
|
|
1098
1453
|
try {
|
|
1099
|
-
const response = await
|
|
1454
|
+
const response = await authFetch('/api/launch', {
|
|
1100
1455
|
method: 'POST',
|
|
1101
1456
|
headers: { 'Content-Type': 'application/json' },
|
|
1102
1457
|
body: JSON.stringify({ projectPath })
|
|
@@ -1122,6 +1477,34 @@
|
|
|
1122
1477
|
}
|
|
1123
1478
|
}
|
|
1124
1479
|
|
|
1480
|
+
// Create a new shell for a running instance (via tower proxy)
|
|
1481
|
+
async function createShell(projectPath) {
|
|
1482
|
+
try {
|
|
1483
|
+
// Use tower proxy to route to the project's dashboard API
|
|
1484
|
+
const encodedPath = toBase64URL(projectPath);
|
|
1485
|
+
const response = await authFetch(`/project/${encodedPath}/api/tabs/shell`, {
|
|
1486
|
+
method: 'POST',
|
|
1487
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1488
|
+
});
|
|
1489
|
+
|
|
1490
|
+
if (!response.ok) {
|
|
1491
|
+
const errorText = await response.text();
|
|
1492
|
+
throw new Error(errorText || 'Failed to create shell');
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
const result = await response.json();
|
|
1496
|
+
|
|
1497
|
+
if (result.success === false) {
|
|
1498
|
+
throw new Error(result.error || 'Failed to create shell');
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
showToast(`Shell created: ${result.name || 'shell'}`, 'success');
|
|
1502
|
+
setTimeout(refresh, 1000);
|
|
1503
|
+
} catch (err) {
|
|
1504
|
+
showToast('Failed to create shell: ' + err.message, 'error');
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1125
1508
|
// Format date for display
|
|
1126
1509
|
function formatDate(isoString) {
|
|
1127
1510
|
const date = new Date(isoString);
|
|
@@ -1170,6 +1553,23 @@
|
|
|
1170
1553
|
.replace(/'/g, ''');
|
|
1171
1554
|
}
|
|
1172
1555
|
|
|
1556
|
+
// Base64URL encoding (RFC 4648) for project paths
|
|
1557
|
+
// IMPORTANT: Use TextEncoder for Unicode support (btoa only handles Latin-1)
|
|
1558
|
+
function toBase64URL(str) {
|
|
1559
|
+
// Encode string to UTF-8 bytes, then to Base64, then to Base64URL
|
|
1560
|
+
const bytes = new TextEncoder().encode(str);
|
|
1561
|
+
const base64 = btoa(String.fromCharCode(...bytes));
|
|
1562
|
+
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
// Generate proxy URL for a project
|
|
1566
|
+
// All terminals are now multiplexed on a single port via WebSocket (Spec 0085)
|
|
1567
|
+
// The React dashboard handles tab selection internally
|
|
1568
|
+
function getProxyUrl(instance, portType) {
|
|
1569
|
+
const encodedPath = toBase64URL(instance.projectPath);
|
|
1570
|
+
return `/project/${encodedPath}/`;
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1173
1573
|
// Toast notifications
|
|
1174
1574
|
function showToast(message, type = 'info') {
|
|
1175
1575
|
const container = document.getElementById('toast-container');
|
|
@@ -1183,6 +1583,121 @@
|
|
|
1183
1583
|
}, 4000);
|
|
1184
1584
|
}
|
|
1185
1585
|
|
|
1586
|
+
// Share dialog functions (QR code for mobile access)
|
|
1587
|
+
let tunnelAbortController = null;
|
|
1588
|
+
|
|
1589
|
+
async function showShareDialog() {
|
|
1590
|
+
const dialog = document.getElementById('share-dialog');
|
|
1591
|
+
const content = document.getElementById('share-content');
|
|
1592
|
+
const stopBtn = document.getElementById('stop-tunnel-btn');
|
|
1593
|
+
|
|
1594
|
+
dialog.style.display = 'flex';
|
|
1595
|
+
stopBtn.style.display = 'none';
|
|
1596
|
+
|
|
1597
|
+
// Create abort controller for cancellation
|
|
1598
|
+
tunnelAbortController = new AbortController();
|
|
1599
|
+
|
|
1600
|
+
content.innerHTML = `
|
|
1601
|
+
<div class="loading">
|
|
1602
|
+
<div class="spinner"></div>
|
|
1603
|
+
<p>Starting tunnel...</p>
|
|
1604
|
+
<p style="font-size: 12px; color: var(--muted); margin-top: 8px;">This may take up to 30 seconds</p>
|
|
1605
|
+
<button class="btn" style="margin-top: 16px;" onclick="cancelTunnelStart()">Cancel</button>
|
|
1606
|
+
</div>
|
|
1607
|
+
`;
|
|
1608
|
+
|
|
1609
|
+
try {
|
|
1610
|
+
// Check if tunnel is already running
|
|
1611
|
+
const statusRes = await fetch('/api/tunnel/status', { signal: tunnelAbortController.signal });
|
|
1612
|
+
const status = await statusRes.json();
|
|
1613
|
+
|
|
1614
|
+
if (status.running && status.url) {
|
|
1615
|
+
showQRCode(status.url);
|
|
1616
|
+
return;
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
// Start tunnel
|
|
1620
|
+
const res = await fetch('/api/tunnel/start', { method: 'POST', signal: tunnelAbortController.signal });
|
|
1621
|
+
const result = await res.json();
|
|
1622
|
+
|
|
1623
|
+
if (result.success && result.url) {
|
|
1624
|
+
showQRCode(result.url);
|
|
1625
|
+
} else {
|
|
1626
|
+
content.innerHTML = `<p style="color: var(--danger);">Failed to start tunnel: ${result.error || 'Unknown error'}</p>`;
|
|
1627
|
+
}
|
|
1628
|
+
} catch (e) {
|
|
1629
|
+
if (e.name === 'AbortError') {
|
|
1630
|
+
content.innerHTML = '<p>Tunnel start cancelled.</p>';
|
|
1631
|
+
} else {
|
|
1632
|
+
content.innerHTML = `<p style="color: var(--danger);">Error: ${e.message}</p>`;
|
|
1633
|
+
}
|
|
1634
|
+
} finally {
|
|
1635
|
+
tunnelAbortController = null;
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
function cancelTunnelStart() {
|
|
1640
|
+
if (tunnelAbortController) {
|
|
1641
|
+
tunnelAbortController.abort();
|
|
1642
|
+
}
|
|
1643
|
+
// Also stop any tunnel that might have started
|
|
1644
|
+
fetch('/api/tunnel/stop', { method: 'POST' }).catch(() => {});
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
function showQRCode(tunnelUrl) {
|
|
1648
|
+
const content = document.getElementById('share-content');
|
|
1649
|
+
const stopBtn = document.getElementById('stop-tunnel-btn');
|
|
1650
|
+
|
|
1651
|
+
// Generate QR code using qrcode.js (loaded from CDN)
|
|
1652
|
+
content.innerHTML = `
|
|
1653
|
+
<p style="margin-bottom: 16px;">Scan to access from mobile:</p>
|
|
1654
|
+
<div id="qrcode" style="display: inline-block; padding: 16px; background: white; border-radius: 8px;"></div>
|
|
1655
|
+
<p style="margin-top: 16px; font-size: 12px; color: var(--muted);">
|
|
1656
|
+
<a href="${tunnelUrl}" target="_blank" style="color: var(--primary);">${tunnelUrl}</a>
|
|
1657
|
+
</p>
|
|
1658
|
+
`;
|
|
1659
|
+
|
|
1660
|
+
// Load QRCode library and generate
|
|
1661
|
+
if (!window.QRCode) {
|
|
1662
|
+
const script = document.createElement('script');
|
|
1663
|
+
script.src = 'https://cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js';
|
|
1664
|
+
script.onload = () => generateQR(tunnelUrl);
|
|
1665
|
+
document.head.appendChild(script);
|
|
1666
|
+
} else {
|
|
1667
|
+
generateQR(tunnelUrl);
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
stopBtn.style.display = 'inline-block';
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
function generateQR(url) {
|
|
1674
|
+
const container = document.getElementById('qrcode');
|
|
1675
|
+
if (container && window.QRCode) {
|
|
1676
|
+
container.innerHTML = '';
|
|
1677
|
+
new QRCode(container, {
|
|
1678
|
+
text: url,
|
|
1679
|
+
width: 200,
|
|
1680
|
+
height: 200,
|
|
1681
|
+
colorDark: '#000000',
|
|
1682
|
+
colorLight: '#ffffff',
|
|
1683
|
+
});
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
function hideShareDialog() {
|
|
1688
|
+
document.getElementById('share-dialog').style.display = 'none';
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
async function stopTunnelAndClose() {
|
|
1692
|
+
try {
|
|
1693
|
+
await fetch('/api/tunnel/stop', { method: 'POST' });
|
|
1694
|
+
} catch (e) {
|
|
1695
|
+
// Ignore
|
|
1696
|
+
}
|
|
1697
|
+
hideShareDialog();
|
|
1698
|
+
showToast('Tunnel stopped', 'success');
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1186
1701
|
// Create project dialog functions
|
|
1187
1702
|
function showCreateProjectDialog() {
|
|
1188
1703
|
const dialog = document.getElementById('create-dialog');
|
|
@@ -1219,7 +1734,7 @@
|
|
|
1219
1734
|
hideCreateProjectDialog();
|
|
1220
1735
|
|
|
1221
1736
|
try {
|
|
1222
|
-
const response = await
|
|
1737
|
+
const response = await authFetch('/api/create', {
|
|
1223
1738
|
method: 'POST',
|
|
1224
1739
|
headers: { 'Content-Type': 'application/json' },
|
|
1225
1740
|
body: JSON.stringify({ parent, name })
|