agents-city 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +16 -0
- package/LICENSE +202 -0
- package/NOTICE +5 -0
- package/README.es.md +2226 -0
- package/README.md +2196 -0
- package/benchmarks/committee/README.md +39 -0
- package/benchmarks/committee/metrics.py +51 -0
- package/benchmarks/committee/run.py +64 -0
- package/benchmarks/committee/traces/01-single.json +8 -0
- package/benchmarks/committee/traces/02-mesh.json +16 -0
- package/benchmarks/committee/traces/03-chair.json +15 -0
- package/benchmarks/latency/README.md +35 -0
- package/benchmarks/latency/fake-claude-cli.mjs +119 -0
- package/benchmarks/latency/fake-native-server.mjs +340 -0
- package/benchmarks/latency/fake-tui.py +91 -0
- package/benchmarks/latency/live.py +380 -0
- package/benchmarks/stress/README.md +20 -0
- package/benchmarks/stress/run.py +487 -0
- package/bin/agents +4 -0
- package/bin/agents-city.js +119 -0
- package/bin/benchmark +17 -0
- package/bin/bus +6 -0
- package/bin/cities +3 -0
- package/bin/city +86 -0
- package/bin/committee +6 -0
- package/bin/demo +200 -0
- package/bin/doctor +3 -0
- package/bin/exit +5 -0
- package/bin/hall +73 -0
- package/bin/hall.html +498 -0
- package/bin/logs +3 -0
- package/bin/navegador.mjs +243 -0
- package/bin/report.py +14 -0
- package/bin/reset +3 -0
- package/bin/road +3 -0
- package/bin/seat +10 -0
- package/bin/serve.py +1579 -0
- package/bin/setup.py +753 -0
- package/bin/shortcut +3 -0
- package/bin/skills +3 -0
- package/bin/test +133 -0
- package/bin/test-actualiza.py +130 -0
- package/bin/test-adapter.py +183 -0
- package/bin/test-admision.py +99 -0
- package/bin/test-atajos.py +301 -0
- package/bin/test-avatar.py +58 -0
- package/bin/test-benchmark.py +40 -0
- package/bin/test-broker.py +334 -0
- package/bin/test-cage.py +374 -0
- package/bin/test-card.py +402 -0
- package/bin/test-channel.py +379 -0
- package/bin/test-cities.py +453 -0
- package/bin/test-claude-runtime.py +252 -0
- package/bin/test-committee.py +340 -0
- package/bin/test-contracts.py +551 -0
- package/bin/test-crecimiento.py +164 -0
- package/bin/test-demo.py +308 -0
- package/bin/test-doctor.py +139 -0
- package/bin/test-domains.py +151 -0
- package/bin/test-evidencia.py +50 -0
- package/bin/test-exit.py +128 -0
- package/bin/test-hall-protocol.py +93 -0
- package/bin/test-launch.py +169 -0
- package/bin/test-live-feed.py +508 -0
- package/bin/test-navegador.py +140 -0
- package/bin/test-pairing.py +99 -0
- package/bin/test-parcels.py +283 -0
- package/bin/test-runtime-failures.py +252 -0
- package/bin/test-runtime-ui.py +488 -0
- package/bin/test-runtime.py +268 -0
- package/bin/test-rutas.py +80 -0
- package/bin/test-seat.py +1598 -0
- package/bin/test-security.py +150 -0
- package/bin/test-serve.py +1251 -0
- package/bin/test-stress.py +64 -0
- package/bin/test-widgets.py +143 -0
- package/bin/test-workspace.py +251 -0
- package/bin/testlib.py +118 -0
- package/bin/tokens.py +14 -0
- package/bin/update +3 -0
- package/bus/scripts/deploy.sh +60 -0
- package/bus/scripts/mint-token.sh +68 -0
- package/bus/scripts/setup-dev.sh +79 -0
- package/bus/scripts/test-channel.ts +8 -0
- package/bus/scripts/test-hub.ts +266 -0
- package/bus/scripts/test-local.sh +40 -0
- package/bus/scripts/test-queue.ts +132 -0
- package/bus/worker/package-lock.json +1971 -0
- package/bus/worker/package.json +17 -0
- package/bus/worker/src/index.ts +485 -0
- package/bus/worker/tsconfig.json +13 -0
- package/bus/worker/wrangler.toml +32 -0
- package/city/oven/README.md +59 -0
- package/city/oven/collect.py +43 -0
- package/city/oven/oven.html +4 -0
- package/city/scripts/history.py +120 -0
- package/city/scripts/seed.py +442 -0
- package/city/web/assets/modelos/License.txt +28 -0
- package/city/web/assets/modelos/PROCEDENCIA.txt +3 -0
- package/city/web/assets/modelos/building-a.glb +0 -0
- package/city/web/assets/modelos/building-b.glb +0 -0
- package/city/web/assets/modelos/building-c.glb +0 -0
- package/city/web/assets/modelos/building-d.glb +0 -0
- package/city/web/assets/modelos/building-e.glb +0 -0
- package/city/web/assets/modelos/building-f.glb +0 -0
- package/city/web/assets/modelos/building-g.glb +0 -0
- package/city/web/assets/modelos/building-h.glb +0 -0
- package/city/web/assets/sprites/building-a.png +0 -0
- package/city/web/assets/sprites/building-b.png +0 -0
- package/city/web/assets/sprites/building-c.png +0 -0
- package/city/web/assets/sprites/building-d.png +0 -0
- package/city/web/assets/sprites/building-e.png +0 -0
- package/city/web/assets/sprites/building-f.png +0 -0
- package/city/web/assets/sprites/building-g.png +0 -0
- package/city/web/assets/sprites/building-h.png +0 -0
- package/city/web/assets/sprites/building-i.png +0 -0
- package/city/web/assets/sprites/building-j.png +0 -0
- package/city/web/assets/sprites/building-k.png +0 -0
- package/city/web/assets/sprites/building-l.png +0 -0
- package/city/web/assets/sprites/building-m.png +0 -0
- package/city/web/assets/sprites/building-n.png +0 -0
- package/city/web/assets/sprites/building-skyscraper-a.png +0 -0
- package/city/web/assets/sprites/building-skyscraper-b.png +0 -0
- package/city/web/assets/sprites/building-skyscraper-c.png +0 -0
- package/city/web/assets/sprites/building-skyscraper-d.png +0 -0
- package/city/web/assets/sprites/building-skyscraper-e.png +0 -0
- package/city/web/assets/sprites/catalogo.json +47 -0
- package/city/web/assets/sprites/detail-awning-wide.png +0 -0
- package/city/web/assets/sprites/detail-awning.png +0 -0
- package/city/web/assets/sprites/detail-overhang-wide.png +0 -0
- package/city/web/assets/sprites/detail-overhang.png +0 -0
- package/city/web/assets/sprites/detail-parasol-a.png +0 -0
- package/city/web/assets/sprites/detail-parasol-b.png +0 -0
- package/city/web/assets/sprites/low-detail-building-a.png +0 -0
- package/city/web/assets/sprites/low-detail-building-b.png +0 -0
- package/city/web/assets/sprites/low-detail-building-c.png +0 -0
- package/city/web/assets/sprites/low-detail-building-d.png +0 -0
- package/city/web/assets/sprites/low-detail-building-e.png +0 -0
- package/city/web/assets/sprites/low-detail-building-f.png +0 -0
- package/city/web/assets/sprites/low-detail-building-g.png +0 -0
- package/city/web/assets/sprites/low-detail-building-h.png +0 -0
- package/city/web/assets/sprites/low-detail-building-i.png +0 -0
- package/city/web/assets/sprites/low-detail-building-j.png +0 -0
- package/city/web/assets/sprites/low-detail-building-k.png +0 -0
- package/city/web/assets/sprites/low-detail-building-l.png +0 -0
- package/city/web/assets/sprites/low-detail-building-m.png +0 -0
- package/city/web/assets/sprites/low-detail-building-n.png +0 -0
- package/city/web/assets/sprites/low-detail-building-wide-a.png +0 -0
- package/city/web/assets/sprites/low-detail-building-wide-b.png +0 -0
- package/city/web/assets/sprites/medidas.json +248 -0
- package/city/web/dist/city.js +939 -0
- package/city/web/dist/index.html +294 -0
- package/city/web/dist/modelos/License.txt +28 -0
- package/city/web/dist/modelos/PROCEDENCIA.txt +3 -0
- package/city/web/dist/modelos/building-a.glb +0 -0
- package/city/web/dist/modelos/building-b.glb +0 -0
- package/city/web/dist/modelos/building-c.glb +0 -0
- package/city/web/dist/modelos/building-d.glb +0 -0
- package/city/web/dist/modelos/building-e.glb +0 -0
- package/city/web/dist/modelos/building-f.glb +0 -0
- package/city/web/dist/modelos/building-g.glb +0 -0
- package/city/web/dist/modelos/building-h.glb +0 -0
- package/city/web/dist/sprites/building-a.png +0 -0
- package/city/web/dist/sprites/building-b.png +0 -0
- package/city/web/dist/sprites/building-c.png +0 -0
- package/city/web/dist/sprites/building-d.png +0 -0
- package/city/web/dist/sprites/building-e.png +0 -0
- package/city/web/dist/sprites/building-f.png +0 -0
- package/city/web/dist/sprites/building-g.png +0 -0
- package/city/web/dist/sprites/building-h.png +0 -0
- package/city/web/dist/sprites/building-i.png +0 -0
- package/city/web/dist/sprites/building-j.png +0 -0
- package/city/web/dist/sprites/building-k.png +0 -0
- package/city/web/dist/sprites/building-l.png +0 -0
- package/city/web/dist/sprites/building-m.png +0 -0
- package/city/web/dist/sprites/building-n.png +0 -0
- package/city/web/dist/sprites/building-skyscraper-a.png +0 -0
- package/city/web/dist/sprites/building-skyscraper-b.png +0 -0
- package/city/web/dist/sprites/building-skyscraper-c.png +0 -0
- package/city/web/dist/sprites/building-skyscraper-d.png +0 -0
- package/city/web/dist/sprites/building-skyscraper-e.png +0 -0
- package/city/web/dist/sprites/catalogo.json +47 -0
- package/city/web/dist/sprites/detail-awning-wide.png +0 -0
- package/city/web/dist/sprites/detail-awning.png +0 -0
- package/city/web/dist/sprites/detail-overhang-wide.png +0 -0
- package/city/web/dist/sprites/detail-overhang.png +0 -0
- package/city/web/dist/sprites/detail-parasol-a.png +0 -0
- package/city/web/dist/sprites/detail-parasol-b.png +0 -0
- package/city/web/dist/sprites/low-detail-building-a.png +0 -0
- package/city/web/dist/sprites/low-detail-building-b.png +0 -0
- package/city/web/dist/sprites/low-detail-building-c.png +0 -0
- package/city/web/dist/sprites/low-detail-building-d.png +0 -0
- package/city/web/dist/sprites/low-detail-building-e.png +0 -0
- package/city/web/dist/sprites/low-detail-building-f.png +0 -0
- package/city/web/dist/sprites/low-detail-building-g.png +0 -0
- package/city/web/dist/sprites/low-detail-building-h.png +0 -0
- package/city/web/dist/sprites/low-detail-building-i.png +0 -0
- package/city/web/dist/sprites/low-detail-building-j.png +0 -0
- package/city/web/dist/sprites/low-detail-building-k.png +0 -0
- package/city/web/dist/sprites/low-detail-building-l.png +0 -0
- package/city/web/dist/sprites/low-detail-building-m.png +0 -0
- package/city/web/dist/sprites/low-detail-building-n.png +0 -0
- package/city/web/dist/sprites/low-detail-building-wide-a.png +0 -0
- package/city/web/dist/sprites/low-detail-building-wide-b.png +0 -0
- package/city/web/dist/sprites/medidas.json +248 -0
- package/city/web/dist-hall/hall.js +1363 -0
- package/city/web/index.html +294 -0
- package/city/web/package.json +18 -0
- package/city/web/sella.py +21 -0
- package/city/web/src/activity-actors.ts +47 -0
- package/city/web/src/activity.ts +172 -0
- package/city/web/src/ayuntamiento.ts +580 -0
- package/city/web/src/draw.ts +640 -0
- package/city/web/src/game-speech.ts +79 -0
- package/city/web/src/hall.ts +1959 -0
- package/city/web/src/main.ts +2252 -0
- package/city/web/src/oven.ts +124 -0
- package/city/web/src/people.ts +244 -0
- package/city/web/src/presencia.ts +193 -0
- package/city/web/src/puertas.ts +180 -0
- package/city/web/src/repo-roles.ts +59 -0
- package/city/worker/package.json +16 -0
- package/city/worker/schema.sql +117 -0
- package/city/worker/src/index.ts +702 -0
- package/city/worker/src/square.ts +218 -0
- package/city/worker/wrangler.toml +58 -0
- package/demo/ada.md +50 -0
- package/demo/bruno.md +62 -0
- package/demo/camila.md +56 -0
- package/demo/city/ada.md +40 -0
- package/demo/city/city.yml +8 -0
- package/demo/city/parcels.yml +15 -0
- package/demo/city/roads.json +4 -0
- package/demo/city/units.yml +10 -0
- package/demo/clinica/city.yml +8 -0
- package/demo/clinica/parcels.yml +15 -0
- package/demo/clinica/roads.json +4 -0
- package/demo/clinica/units.yml +9 -0
- package/demo/clinica/vera.md +46 -0
- package/demo/dante.md +54 -0
- package/demo/despacho/city.yml +8 -0
- package/demo/despacho/marta.md +46 -0
- package/demo/despacho/parcels.yml +15 -0
- package/demo/despacho/roads.json +4 -0
- package/demo/despacho/units.yml +9 -0
- package/demo/elsa.md +50 -0
- package/demo/farid.md +52 -0
- package/demo/greta.md +50 -0
- package/demo/hugo.md +49 -0
- package/demo/iris.md +49 -0
- package/demo/jonas.md +51 -0
- package/demo/kira.md +51 -0
- package/demo/luca.md +49 -0
- package/demo/parcels.yml +92 -0
- package/demo/seed.py +20 -0
- package/demo/show.py +161 -0
- package/demo/stories.py +824 -0
- package/demo/units.yml +33 -0
- package/docs/agents-first.md +106 -0
- package/docs/glossary.md +55 -0
- package/docs/map-live-layers.md +73 -0
- package/docs/security.md +265 -0
- package/docs/self-host.md +151 -0
- package/docs/testing.md +205 -0
- package/package.json +73 -0
- package/plugin/.claude-plugin/plugin.json +62 -0
- package/plugin/.mcp.json +15 -0
- package/plugin/channel/activity-cli.ts +16 -0
- package/plugin/channel/adapter-prompts.ts +113 -0
- package/plugin/channel/adapter.js +4292 -0
- package/plugin/channel/adapter.ts +133 -0
- package/plugin/channel/bus.js +20188 -0
- package/plugin/channel/bus.ts +131 -0
- package/plugin/channel/city-config.ts +125 -0
- package/plugin/channel/claude-channel.ts +58 -0
- package/plugin/channel/cli-args.ts +75 -0
- package/plugin/channel/client.js +4200 -0
- package/plugin/channel/client.ts +38 -0
- package/plugin/channel/committee/activity.ts +256 -0
- package/plugin/channel/committee/collection.ts +154 -0
- package/plugin/channel/committee/decision.ts +175 -0
- package/plugin/channel/committee/floor.ts +135 -0
- package/plugin/channel/committee/guards.ts +30 -0
- package/plugin/channel/committee/history.ts +38 -0
- package/plugin/channel/committee/render.ts +102 -0
- package/plugin/channel/committee/service.ts +72 -0
- package/plugin/channel/committee/storage.ts +92 -0
- package/plugin/channel/committee/types.ts +163 -0
- package/plugin/channel/committee/view.ts +63 -0
- package/plugin/channel/committee-cli.ts +109 -0
- package/plugin/channel/delivery-metrics.ts +61 -0
- package/plugin/channel/delivery-queue.ts +118 -0
- package/plugin/channel/hub/activity-controller.ts +72 -0
- package/plugin/channel/hub/activity-feed.ts +169 -0
- package/plugin/channel/hub/committee-controller.ts +60 -0
- package/plugin/channel/hub/connections.ts +48 -0
- package/plugin/channel/hub/diagnostics.ts +67 -0
- package/plugin/channel/hub/envelope-validity.ts +109 -0
- package/plugin/channel/hub/envelopes.ts +66 -0
- package/plugin/channel/hub/lifecycle.ts +91 -0
- package/plugin/channel/hub/local-roads.ts +89 -0
- package/plugin/channel/hub/remote-roads.ts +165 -0
- package/plugin/channel/hub/road-controller.ts +99 -0
- package/plugin/channel/hub-client.ts +149 -0
- package/plugin/channel/local-hub.js +5995 -0
- package/plugin/channel/local-hub.ts +349 -0
- package/plugin/channel/map-reporter.ts +48 -0
- package/plugin/channel/package-lock.json +1731 -0
- package/plugin/channel/package.json +19 -0
- package/plugin/channel/protocol.ts +98 -0
- package/plugin/channel/road-cli.ts +18 -0
- package/plugin/channel/run.sh +15 -0
- package/plugin/channel/runtime/claude.ts +458 -0
- package/plugin/channel/runtime/codex-config.ts +77 -0
- package/plugin/channel/runtime/codex.ts +669 -0
- package/plugin/channel/runtime/command.ts +62 -0
- package/plugin/channel/runtime/factory.ts +15 -0
- package/plugin/channel/runtime/json-rpc.ts +154 -0
- package/plugin/channel/runtime/kimi.ts +277 -0
- package/plugin/channel/runtime/opencode.ts +248 -0
- package/plugin/channel/runtime/process.ts +84 -0
- package/plugin/channel/runtime/types.ts +45 -0
- package/plugin/channel/runtime-files.ts +73 -0
- package/plugin/channel/runtime-gateway.js +6396 -0
- package/plugin/channel/runtime-gateway.ts +358 -0
- package/plugin/channel/runtime-metrics.ts +52 -0
- package/plugin/channel/runtime-subscription.ts +180 -0
- package/plugin/channel/terminal-delivery.ts +131 -0
- package/plugin/channel/untrusted.ts +60 -0
- package/plugin/commands/committee.md +31 -0
- package/plugin/commands/exit.md +32 -0
- package/plugin/commands/goals.md +20 -0
- package/plugin/commands/join.md +18 -0
- package/plugin/commands/notice.md +20 -0
- package/plugin/commands/propose.md +19 -0
- package/plugin/commands/round.md +17 -0
- package/plugin/commands/session.md +22 -0
- package/plugin/commands/settings.md +25 -0
- package/plugin/commands/setup.md +21 -0
- package/plugin/commands/team.md +23 -0
- package/plugin/domains/custom.md +27 -0
- package/plugin/domains/finance.md +31 -0
- package/plugin/domains/healthcare.md +34 -0
- package/plugin/domains/legal.md +31 -0
- package/plugin/domains/marketing.md +33 -0
- package/plugin/domains/operations.md +31 -0
- package/plugin/domains/research.md +32 -0
- package/plugin/domains/sales.md +30 -0
- package/plugin/domains/software.md +37 -0
- package/plugin/hooks/activity.sh +6 -0
- package/plugin/hooks/digging.sh +68 -0
- package/plugin/hooks/growth.sh +42 -0
- package/plugin/hooks/hooks.json +106 -0
- package/plugin/hooks/notice-on-pr.sh +35 -0
- package/plugin/hooks/notice-on-stop.sh +80 -0
- package/plugin/hooks/notice-pending.sh +58 -0
- package/plugin/hooks/solo-en-ciudad.sh +23 -0
- package/plugin/hooks/tokens.sh +47 -0
- package/plugin/roles/examples/account-executive.md +25 -0
- package/plugin/roles/examples/ai-manager.md +34 -0
- package/plugin/roles/examples/associate.md +24 -0
- package/plugin/roles/examples/brand-lead.md +20 -0
- package/plugin/roles/examples/cfo.md +20 -0
- package/plugin/roles/examples/city-lead.md +25 -0
- package/plugin/roles/examples/clinical-director.md +26 -0
- package/plugin/roles/examples/clinical-ops.md +25 -0
- package/plugin/roles/examples/clinician.md +25 -0
- package/plugin/roles/examples/compliance.md +27 -0
- package/plugin/roles/examples/content.md +24 -0
- package/plugin/roles/examples/controller.md +27 -0
- package/plugin/roles/examples/cpto.md +41 -0
- package/plugin/roles/examples/customer-success.md +25 -0
- package/plugin/roles/examples/data-engineer.md +37 -0
- package/plugin/roles/examples/data.md +60 -0
- package/plugin/roles/examples/dev.md +37 -0
- package/plugin/roles/examples/devops.md +39 -0
- package/plugin/roles/examples/enablement.md +24 -0
- package/plugin/roles/examples/ethics.md +24 -0
- package/plugin/roles/examples/fin-analytics.md +27 -0
- package/plugin/roles/examples/health-compliance.md +25 -0
- package/plugin/roles/examples/health-data.md +26 -0
- package/plugin/roles/examples/knowledge.md +25 -0
- package/plugin/roles/examples/lifecycle.md +26 -0
- package/plugin/roles/examples/llm-engineer.md +34 -0
- package/plugin/roles/examples/managing-partner.md +20 -0
- package/plugin/roles/examples/methods.md +23 -0
- package/plugin/roles/examples/operations-lead.md +23 -0
- package/plugin/roles/examples/ops.md +24 -0
- package/plugin/roles/examples/patient-safety.md +26 -0
- package/plugin/roles/examples/performance.md +27 -0
- package/plugin/roles/examples/po.md +36 -0
- package/plugin/roles/examples/process-owner.md +23 -0
- package/plugin/roles/examples/product-design.md +39 -0
- package/plugin/roles/examples/program-manager.md +24 -0
- package/plugin/roles/examples/quality.md +25 -0
- package/plugin/roles/examples/research-director.md +24 -0
- package/plugin/roles/examples/research-ops.md +25 -0
- package/plugin/roles/examples/researcher.md +25 -0
- package/plugin/roles/examples/revenue-lead.md +25 -0
- package/plugin/roles/examples/revops.md +26 -0
- package/plugin/roles/examples/seo.md +27 -0
- package/plugin/roles/examples/specialist.md +24 -0
- package/plugin/scripts/actualiza.py +198 -0
- package/plugin/scripts/admision.py +165 -0
- package/plugin/scripts/apaga.py +232 -0
- package/plugin/scripts/atajos.py +506 -0
- package/plugin/scripts/avatar.py +211 -0
- package/plugin/scripts/broker.py +531 -0
- package/plugin/scripts/cage.py +492 -0
- package/plugin/scripts/capabilities.py +210 -0
- package/plugin/scripts/card.py +322 -0
- package/plugin/scripts/cities.py +520 -0
- package/plugin/scripts/city-env.sh +90 -0
- package/plugin/scripts/city-runtime.sh +87 -0
- package/plugin/scripts/city-session.sh +552 -0
- package/plugin/scripts/city_env.py +67 -0
- package/plugin/scripts/crecimiento.py +140 -0
- package/plugin/scripts/deliberations.py +48 -0
- package/plugin/scripts/doctor.py +294 -0
- package/plugin/scripts/domains.py +201 -0
- package/plugin/scripts/evidencia.py +64 -0
- package/plugin/scripts/find-repos.sh +111 -0
- package/plugin/scripts/gh.py +159 -0
- package/plugin/scripts/hall_protocol.py +147 -0
- package/plugin/scripts/hook_activity.py +139 -0
- package/plugin/scripts/launch.py +94 -0
- package/plugin/scripts/logs.py +98 -0
- package/plugin/scripts/pairing.py +185 -0
- package/plugin/scripts/parcels.py +132 -0
- package/plugin/scripts/read-card.py +55 -0
- package/plugin/scripts/report.py +201 -0
- package/plugin/scripts/reset.py +180 -0
- package/plugin/scripts/roads.py +230 -0
- package/plugin/scripts/roles.py +274 -0
- package/plugin/scripts/runtime_log.py +69 -0
- package/plugin/scripts/runtime_processes.py +132 -0
- package/plugin/scripts/rutas.py +83 -0
- package/plugin/scripts/seat.py +1696 -0
- package/plugin/scripts/tokens.py +194 -0
- package/plugin/scripts/trust-repos.py +59 -0
- package/plugin/scripts/ui.py +280 -0
- package/plugin/scripts/units.py +80 -0
- package/plugin/scripts/workspace.py +561 -0
- package/plugin/skills/city/SKILL.md +191 -0
- package/templates/blank.md +39 -0
- package/templates/finance.md +44 -0
- package/templates/legal.md +45 -0
- package/templates/marketing.md +48 -0
- package/templates/product.md +45 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/* Drive a real Chrome against a running Hall, over the DevTools protocol.
|
|
3
|
+
*
|
|
4
|
+
* Why this exists: every other suite in this repo can pass while the page is
|
|
5
|
+
* dead. Instruction editors and a zip upload once shipped rendered, typechecked
|
|
6
|
+
* and fully tested on the server side — and wired to nothing. `tsc` cannot see
|
|
7
|
+
* an event handler that was never attached, and neither can a DOM-free test.
|
|
8
|
+
* Only a browser that clicks can.
|
|
9
|
+
*
|
|
10
|
+
* No dependency: Node 22 has a global WebSocket, and CDP is JSON over one. A
|
|
11
|
+
* headless browser is heavy enough without a driver library on top.
|
|
12
|
+
*
|
|
13
|
+
* node bin/navegador.mjs <url>
|
|
14
|
+
*
|
|
15
|
+
* Prints one line per check and exits non-zero if any failed.
|
|
16
|
+
*/
|
|
17
|
+
import { spawn } from 'node:child_process';
|
|
18
|
+
import { existsSync } from 'node:fs';
|
|
19
|
+
import { mkdtemp } from 'node:fs/promises';
|
|
20
|
+
import { tmpdir } from 'node:os';
|
|
21
|
+
import { join } from 'node:path';
|
|
22
|
+
|
|
23
|
+
const CANDIDATOS = [
|
|
24
|
+
process.env.CHROME_PATH,
|
|
25
|
+
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
|
26
|
+
'/Applications/Chromium.app/Contents/MacOS/Chromium',
|
|
27
|
+
'/usr/bin/google-chrome',
|
|
28
|
+
'/usr/bin/google-chrome-stable',
|
|
29
|
+
'/usr/bin/chromium',
|
|
30
|
+
'/usr/bin/chromium-browser',
|
|
31
|
+
'/snap/bin/chromium',
|
|
32
|
+
].filter(Boolean);
|
|
33
|
+
|
|
34
|
+
function dondeEstaChrome() {
|
|
35
|
+
return CANDIDATOS.find((p) => existsSync(p)) || '';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Chrome prints its DevTools endpoint on stderr; that is the handshake. */
|
|
39
|
+
function esperaEndpoint(proceso, limite = 20000) {
|
|
40
|
+
return new Promise((listo, falla) => {
|
|
41
|
+
let visto = '';
|
|
42
|
+
const reloj = setTimeout(() => falla(new Error('chrome never announced its port')), limite);
|
|
43
|
+
proceso.stderr.on('data', (trozo) => {
|
|
44
|
+
visto += trozo.toString();
|
|
45
|
+
const m = visto.match(/ws:\/\/[^\s]+/);
|
|
46
|
+
if (m) {
|
|
47
|
+
clearTimeout(reloj);
|
|
48
|
+
listo(m[0]);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
proceso.on('exit', (codigo) => {
|
|
52
|
+
clearTimeout(reloj);
|
|
53
|
+
falla(new Error(`chrome exited with ${codigo}: ${visto.slice(-400)}`));
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
class Cdp {
|
|
59
|
+
constructor(socket) {
|
|
60
|
+
this.socket = socket;
|
|
61
|
+
this.n = 0;
|
|
62
|
+
this.pendientes = new Map();
|
|
63
|
+
socket.addEventListener('message', (evento) => {
|
|
64
|
+
const mensaje = JSON.parse(evento.data);
|
|
65
|
+
const espera = this.pendientes.get(mensaje.id);
|
|
66
|
+
if (!espera) return;
|
|
67
|
+
this.pendientes.delete(mensaje.id);
|
|
68
|
+
mensaje.error ? espera.falla(new Error(JSON.stringify(mensaje.error))) : espera.listo(mensaje.result);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
static async abre(url) {
|
|
73
|
+
const socket = new WebSocket(url);
|
|
74
|
+
await new Promise((listo, falla) => {
|
|
75
|
+
socket.addEventListener('open', listo, { once: true });
|
|
76
|
+
socket.addEventListener('error', () => falla(new Error('cannot reach chrome')), { once: true });
|
|
77
|
+
});
|
|
78
|
+
return new Cdp(socket);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
manda(metodo, params = {}) {
|
|
82
|
+
const id = ++this.n;
|
|
83
|
+
this.socket.send(JSON.stringify({ id, method: metodo, params }));
|
|
84
|
+
return new Promise((listo, falla) => this.pendientes.set(id, { listo, falla }));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Evaluate in the page and return the value, awaiting promises. */
|
|
88
|
+
async evalua(expresion) {
|
|
89
|
+
const r = await this.manda('Runtime.evaluate', {
|
|
90
|
+
expression: expresion,
|
|
91
|
+
awaitPromise: true,
|
|
92
|
+
returnByValue: true,
|
|
93
|
+
});
|
|
94
|
+
if (r.exceptionDetails) {
|
|
95
|
+
throw new Error(r.exceptionDetails.exception?.description ?? 'page threw');
|
|
96
|
+
}
|
|
97
|
+
return r.result.value;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const dormir = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
102
|
+
|
|
103
|
+
let fallos = 0;
|
|
104
|
+
function comprueba(texto, bien, detalle = '') {
|
|
105
|
+
console.log(`${bien ? ' ok ·' : ' FAIL·'} ${texto}${bien || !detalle ? '' : `\n ${detalle}`}`);
|
|
106
|
+
if (!bien) fallos += 1;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function main() {
|
|
110
|
+
const url = process.argv[2];
|
|
111
|
+
if (!url) {
|
|
112
|
+
console.error('usage: navegador.mjs <hall url>');
|
|
113
|
+
process.exit(2);
|
|
114
|
+
}
|
|
115
|
+
const chrome = dondeEstaChrome();
|
|
116
|
+
if (!chrome) {
|
|
117
|
+
if (process.env.CITY_BROWSER_REQUIRED === '1') {
|
|
118
|
+
console.error(' no Chrome on this machine, and CITY_BROWSER_REQUIRED=1');
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
console.log(' no Chrome here — browser checks skipped');
|
|
122
|
+
process.exit(0);
|
|
123
|
+
}
|
|
124
|
+
const perfil = await mkdtemp(join(tmpdir(), 'agents-city-chrome-'));
|
|
125
|
+
const proceso = spawn(chrome, [
|
|
126
|
+
'--headless=new',
|
|
127
|
+
'--remote-debugging-port=0',
|
|
128
|
+
'--no-first-run',
|
|
129
|
+
'--no-default-browser-check',
|
|
130
|
+
'--disable-gpu',
|
|
131
|
+
'--disable-dev-shm-usage',
|
|
132
|
+
`--user-data-dir=${perfil}`,
|
|
133
|
+
'about:blank',
|
|
134
|
+
]);
|
|
135
|
+
try {
|
|
136
|
+
// Talk to the PAGE's own socket rather than the browser's: no sessions to
|
|
137
|
+
// thread through every message, which is where a hand-rolled driver goes
|
|
138
|
+
// wrong first.
|
|
139
|
+
const endpoint = await esperaEndpoint(proceso);
|
|
140
|
+
const base = new URL(endpoint.replace(/^ws/, 'http'));
|
|
141
|
+
const pestanas = await (await fetch(`http://${base.host}/json/list`)).json();
|
|
142
|
+
const pagina = pestanas.find((t) => t.type === 'page' && t.webSocketDebuggerUrl);
|
|
143
|
+
if (!pagina) throw new Error('chrome opened no page to drive');
|
|
144
|
+
const cdp = await Cdp.abre(pagina.webSocketDebuggerUrl);
|
|
145
|
+
|
|
146
|
+
await cdp.manda('Page.enable');
|
|
147
|
+
await cdp.manda('Runtime.enable');
|
|
148
|
+
await cdp.manda('Page.navigate', { url });
|
|
149
|
+
for (let i = 0; i < 60; i += 1) {
|
|
150
|
+
const listo = await cdp.evalua('!!document.getElementById("rail") && !!window.PASE');
|
|
151
|
+
if (listo) break;
|
|
152
|
+
await dormir(250);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
comprueba(
|
|
156
|
+
'the hall loads and reads its city',
|
|
157
|
+
await cdp.evalua(
|
|
158
|
+
'(async () => { for (let i = 0; i < 40; i++) { if (document.querySelectorAll("nav li").length) return true; await new Promise(r => setTimeout(r, 250)); } return false; })()',
|
|
159
|
+
),
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
// The skin, both ways, and remembered.
|
|
163
|
+
const tema = await cdp.evalua(`(() => {
|
|
164
|
+
const b = document.getElementById('temaBoton');
|
|
165
|
+
if (!b) return { falta: true };
|
|
166
|
+
const antes = document.documentElement.getAttribute('data-tema');
|
|
167
|
+
b.click();
|
|
168
|
+
const despues = document.documentElement.getAttribute('data-tema');
|
|
169
|
+
return { antes, despues, guardado: localStorage.getItem('hall-tema'), etiqueta: b.textContent.trim() };
|
|
170
|
+
})()`);
|
|
171
|
+
comprueba(
|
|
172
|
+
'the day/night switch flips the page and remembers the choice',
|
|
173
|
+
!!tema && !tema.falta && tema.despues && tema.guardado === tema.despues,
|
|
174
|
+
JSON.stringify(tema),
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
// The agents view: this is where dead controls shipped once.
|
|
178
|
+
const abierto = await cdp.evalua(`(async () => {
|
|
179
|
+
const ir = [...document.querySelectorAll('nav li')].find(l => /agents/i.test(l.textContent));
|
|
180
|
+
if (!ir) return { falta: 'no agents entry' };
|
|
181
|
+
ir.click();
|
|
182
|
+
for (let i = 0; i < 40; i++) {
|
|
183
|
+
if (document.querySelector('.fichaRPG')) break;
|
|
184
|
+
await new Promise(r => setTimeout(r, 250));
|
|
185
|
+
}
|
|
186
|
+
const boton = (sel) => { const e = document.querySelector(sel); return e ? !!(e.onclick || e.onchange) : null; };
|
|
187
|
+
return {
|
|
188
|
+
fichas: document.querySelectorAll('.fichaRPG').length,
|
|
189
|
+
alta: boton('#altaAgente'),
|
|
190
|
+
instrucciones: boton('.rpgIns'),
|
|
191
|
+
subir: boton('.rpgSubir input'),
|
|
192
|
+
monta: boton('.rpgMonta'),
|
|
193
|
+
dado: boton('.rpgDado'),
|
|
194
|
+
test: boton('.rpgTest'),
|
|
195
|
+
};
|
|
196
|
+
})()`);
|
|
197
|
+
comprueba('the agents view renders its sheets', (abierto?.fichas ?? 0) > 0, JSON.stringify(abierto));
|
|
198
|
+
for (const [control, etiqueta] of [
|
|
199
|
+
['alta', '+ Add an agent'],
|
|
200
|
+
['instrucciones', 'the CLAUDE.md / AGENTS.md editors'],
|
|
201
|
+
['subir', 'the skill zip upload'],
|
|
202
|
+
['monta', 'the + folder mount button'],
|
|
203
|
+
['dado', 'the avatar reroll'],
|
|
204
|
+
['test', 'the engine test button'],
|
|
205
|
+
]) {
|
|
206
|
+
comprueba(
|
|
207
|
+
`${etiqueta} is wired to a handler, not just drawn`,
|
|
208
|
+
abierto?.[control] === true,
|
|
209
|
+
`${control}=${JSON.stringify(abierto?.[control])}`,
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// And one of them actually does something when clicked.
|
|
214
|
+
const modal = await cdp.evalua(`(async () => {
|
|
215
|
+
const b = document.querySelector('.rpgIns');
|
|
216
|
+
if (!b) return { falta: true };
|
|
217
|
+
b.click();
|
|
218
|
+
for (let i = 0; i < 40; i++) {
|
|
219
|
+
const m = document.getElementById('editorIns');
|
|
220
|
+
if (m && !m.hidden) return { abierto: true, titulo: document.getElementById('edTitulo')?.textContent ?? '' };
|
|
221
|
+
await new Promise(r => setTimeout(r, 250));
|
|
222
|
+
}
|
|
223
|
+
return { abierto: false };
|
|
224
|
+
})()`);
|
|
225
|
+
comprueba(
|
|
226
|
+
'clicking CLAUDE.md opens the editor on that agent',
|
|
227
|
+
modal?.abierto === true && /CLAUDE\.md/.test(modal.titulo ?? ''),
|
|
228
|
+
JSON.stringify(modal),
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
const errores = await cdp.evalua('window.__erroresDePagina ? window.__erroresDePagina.length : 0');
|
|
232
|
+
comprueba('the page raised no uncaught errors while we drove it', errores === 0, String(errores));
|
|
233
|
+
} finally {
|
|
234
|
+
proceso.kill('SIGKILL');
|
|
235
|
+
}
|
|
236
|
+
console.log(`\n ${fallos ? `${fallos} failed` : 'browser ok'} — 11 checks\n`);
|
|
237
|
+
process.exit(fallos ? 1 : 0);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
main().catch((e) => {
|
|
241
|
+
console.error(` browser checks could not run: ${e.message}`);
|
|
242
|
+
process.exit(1);
|
|
243
|
+
});
|
package/bin/report.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Report growth from where the work actually happens.
|
|
3
|
+
|
|
4
|
+
A one-line wrapper. The reporter itself ships inside the plugin, because that is
|
|
5
|
+
what gets installed on everybody's machine — and growth for a marketing, legal or
|
|
6
|
+
finance city has to be counted where the folders are.
|
|
7
|
+
|
|
8
|
+
./bin/report.py --push
|
|
9
|
+
"""
|
|
10
|
+
import os
|
|
11
|
+
import sys
|
|
12
|
+
os.execv(sys.executable, [sys.executable,
|
|
13
|
+
os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
|
14
|
+
'..', 'plugin', 'scripts', 'report.py'), *sys.argv[1:]])
|
package/bin/reset
ADDED
package/bin/road
ADDED
package/bin/seat
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Sit down to work: one command, no account, no setup.
|
|
3
|
+
#
|
|
4
|
+
# ./bin/seat --help what it asks and what it writes
|
|
5
|
+
#
|
|
6
|
+
# The logic is in plugin/scripts/seat.py and not here, because that folder is what
|
|
7
|
+
# gets installed on everybody's machine — so `/city:join`, which runs inside Claude
|
|
8
|
+
# from an installed plugin with no clone around it, reaches exactly the same code.
|
|
9
|
+
# One job, one implementation, two doors.
|
|
10
|
+
exec python3 "$(cd "$(dirname "$0")/../plugin/scripts" && pwd)/seat.py" "$@"
|